Accessing multiple controllers with same request mapping - spring

Please find my HomeController and DemoController
class HomeController{
#RequestMapping(value="index")
public void home(){
}
}
class DemoController{
#RequestMapping(value="index")
public void demo(){
}
}
when I try to send a request to index, which one will get executed?
I wanted to know how can we have same request mapping value for multiple controllers

https://stackoverflow.com/a/34590355/2682499 is only partially correct at this point.
You can have multiple controller methods use the same URI so long as you provide Spring enough additional information on which one it should use. Whether or not you should do this is a different question. I would certainly not recommend using the same URI in two separate controller classes to avoid confusion, though.
You can do something like this:
class HomeController{
#RequestMapping(value="/index", params = {"!name", "!foo"})
public List<Something> listItems(){
// retrieve Something list
}
#RequestMapping(value="/index", params = "name")
public List<Something> listItems(String name) {
// retrieve Something list WHERE name LIKE %name%
}
#RequestMapping(value="/index", params = {"!name", "foo"})
public List<Something> listItems(String foo) {
// Do something completely different
}
}
For the full documentation on what is possible when overloading URIs you should reference the #ReqeustMapping documentation: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestMapping.html. And, specifically https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestMapping.html#params-- for the section request parameters.

In Spring Web MVC this is not possible. Each mapping must be unique in your context. If not, you will receive a RuntimeException during context initialization.
You cannot even use parameters to differentiate your endpoints because they are not evaluated while searching for a suitable handler (applicable for Servlet environments). From #RequestMapping javadoc:
In a Servlet environment, parameter mappings are considered as restrictions that are enforced at the type level. The primary path mapping (i.e. the specified URI value) still has to uniquely identify the target handler, with parameter mappings simply expressing preconditions for invoking the handler.
Note that you can do the opposite, so multiple URLs can point to the same handler. Have a look at Spring MVC: Mapping Multiple URLs to Same Controller

Unfortunately, this is not possible. The request mapping has to be unique otherwise the application can't determine which method the incoming request should be mapped to.
What you can do instead is to extend the request mapping:
class HomeController{
#RequestMapping(value="home/index")
public void home(){
}
}
class DemoController{
#RequestMapping(value="demo/index")
public void demo(){
}
}

Related

Spring Boot - Is it possible to disable an end-point

Assuming I have a controller like:
public class MyController {
public String endpoint1() {...}
public String endpoint2() {...}
}
I want to disable endpoint1 for whatever reason in Spring. Simply, just disable it so that it cannot be accessed. So, I am not looking for how and what response to return in that case or how to secure that endpoint. Just looking to simply disable the endpoint, something like #Disabled annotation on it or so.
SOLUTION UPDATE:
Thanks all who contributed. I decided to go with #AdolinK suggestion . However, that solution will only disable access to the controller resulting into 404 Not Found. However, if you use OpenApi, your controller and all of its models such as request/response body will still show in swagger.
So, in addition to Adolin's suggestion and also added #Hidden OpenApi annotation to my controllers like:
In application.properties, set:
cars.controller.enabled=false
Then in your controller, use it. To hide controller from the OpenApi/Swagger as well, you can use #Hiden tag:
#Hidden
#ConditionalOnExpression("${cars.controller.enabled}")
#RestController
#RequestMapping("/cars")
public class Carontroller {
...
}
After this, every end point handled by this controller will return 404 Not Found and OpenApi/Swagger will not show the controllers nor any of its related schema objects such as CarRequestModel, CarResponseModel etc.
You can use #ConditionalOnExpression annotation.
public class MyController {
#ConditionalOnExpression("${my.controller.enabled:false}")
public String endpoint1() {...}
public String endpoint2() {...}
}
In application.properties, you indicates that controller is enabled by default
my.controller.enabled=true
ConditionalOnExpression sets false your property, and doesn't allow access to end-point
Why not remove the mapping annotation over that method?
Try this simple approach: You can define a property is.enable.enpoint1 to turn on/off your endpoint in a flexible way.
If you turn off the endpoint, then return a 404 or error page, which depends on your situation.
#Value("${is.enable.enpoint1}")
private String isEnableEnpoint1;
public String endpoint1() {
if (!"true".equals(isEnableEnpoint1)) {
return "404";
}
// code
}

Obtaining handle to Collection/Array object in #PreFilter and #PostFilter in Spring Security

In Spring Security, #PreFilter and #PostFilter can be used to trim/prune the argument/return object and filterObject references each element in the object and is used to loop through the argument/return Collection/Array.
However, I need to get a handle to the actual Collection/Array as a whole and not specific elements in the context. Is there any way to do this?
The reason is that I am creating an externalized authorization service that is used by Spring Security to query and prune the collection/array and this service supports querying for multiple answers in a single question. Once I get a reference to the object as a whole, I can iterate though the elements myself to create this request to the externalized service.
Can this be done in Spring Security? I am implementing this as an custom express handler.
Assuming the return value is modifiable, you can use #PostAuthorize. For example:
#PostAuthorize("#mySecurityFilter.filter(authentication, returnObject)")
List<String> findAllMessages();
This assumes you created a Bean by the name of "mySecurityFilter" that looks something like this:
#Component
public class MySecurityFilter {
public boolean filter(Authentication authentication, List<String> domain) {
// submit to service and get back all allowed values
List<String> allowed = Arrays.asList("Hello");
Iterator<String> iValues = domain.iterator();
while(iValues.hasNext()) {
String value = iValues.next();
if(!allowed.contains(value)) {
iValues.remove();
}
}
return true;
}
}

read Asp.Net Web api GET values from url

I am trying to map /{Controller}/{Variable1}/{Variable2}/{Variable3} to a GET method in controller
public TestController{
public ActionResult Get([FromUrl] Entity instance){}
}
So I need to map variables to the entity.
To put it into an example
/Product/{category}/{filter1}/{filter2}/
Entity
public class ProductSearchRequest
{
public string Category{get;set;}
public string filter1 {get;set;}
public string filter2 {get;set;}
}
Controller
public ProductController: Controller {
public ActionResult GET([FromUri] ProductSearchRequest productSearchRequest){
}
}
[EDITED]
Had to do following changes to get this working
Instead of RouteCollection.MapHttpRoute use HttpConfiguration.Routes.MapHttpRoute as this is API routing not MVC routing.
Inherit controller from ApiController rather than Controller which I was before.
Basically you are not going to be able to do that. Complex types are not compatible with the routing mechanism.
Take a read of this article. But this paragraph explains why the routing mechanism cannot do what you are asking.
A complex type can only bind to the URI through a custom binding. But
in that case, the framework cannot know in advance whether the
parameter would bind to a particular URI. To find out, it would need
to invoke the binding. The goal of the selection algorithm is to
select an action from the static description, before invoking any
bindings. Therefore, complex types are excluded from the matching
algorithm.
Therefore the basic rule is:
For every parameter of the action, if the parameter is taken from the
URI, then the parameter name must be found either in the route
dictionary or in the URI query string. (Optional parameters and
parameters with complex types are excluded.)
Which means you need to define your action like so:
public ActionResult GET(string Category, string filter1, string filter2){
}
And your route template:
/{controller}/{category}/{filter1}/{filter2}/

Duplicate RequestMapping

I'm working on an open source named OpenMRS supporting Spring MVC. I cannot modify core source for update purpose later. So I must write a module, something like plugin to add functions to the system. The problem is that I want to alter the original screen to mine by using portlet to redirect to my jsp. The controller of the core code is something like this:
#RequestMapping("/patientDashboard.form")
protected String renderDashboard(#RequestParam(required = true, value = "patientId") Integer patientId, ModelMap map){
....
return "patientDashboardForm";
}
I'm not familiar with Spring but as I know that when the url ends with /patientDashboard.form?patientId=xxx the function will call patientDashboardForm.jsp. Now I want to return to my jsp so I must define a new class with same code but return to my jsp (to do this because cannot modify the core code). But by defining same mapping /patientDashboard.form causes error "Cannot map handler XXX to URL path /patientDashboard.form: There is already handler YYY mapped".
So is there anyway to overcome this situation ?
There is no way to overrule an existing #RequestMapping. Each mapping must be unique. What you could do is the following. Instead of adding a request parameter, add a path parameter like this
#RequestMapping("/patientDashboard.form/{patientId}", method = RequestMethod.GET)
public String renderDashboard(#PathVariable("patientId") final long id, Model model) {
/* your code here */
}
This will create a new #RequestMapping that will differ from the existing one.
You have to create another #Controller extending the existing one. Then, you can define your custom mappings (you can't reuse the existing one) and reimplement the superclass methods at your convenience, redirecting to your view and defining custom logic there.
Example:
#Controller
#RequestMapping("/your_new_mapping")
public class YourController extends BaseController {
#Override
#RequestMapping("/patientDashboard.form")
public void renderDashboard(#RequestParam(required = true, value = "patientId") Integer patientId, ModelMap map){
// Call to default functionallity
super.renderDashboard(patientId, map);
...
// your custom code here
return "yourCustomJSPHere";
}
}

URI-specific ExceptionMapper in JAX-RS

I'm using Jersey, and Guice as my IOC-container. I'd like to know if it is possible to associate an ExceptionMapper with a specific URI. The reason for this is that I want to map the same exception differently based on what URI was visited. For example, suppose I've got the following two exception mappers for my custom exception:
public class MyExceptionMapperForFirstURI implements
ExceptionMapper<MyException> {..return response based on first URI..}
public class MyExceptionMapperForSecondURI implements
ExceptionMapper<MyException> {..return response based on second URI..}
As far as I understand you bind an ExceptionMapper in your ServletModule as follows:
public class MyModule extends ServletModule {
#Override
public void configureServlets() {
super.configureServlets();
bind(MyCustomExceptionMapper.class);
}
}
How would I go about binding MyExceptionMapperForFirstURI and MyExceptionMapperForSecondURI so that they get associated with the correct URIs. Is this possible, and if possible: is this the correct way to do this?
This is quite late answer ;-) but you can always inject the UriInfo and branch on that. So,
#Context
UriInfo uriInfo;
.....
if (matchesA(uriInfo.getAbsolutePath())) {
// do something
}
Not sure how the URI's of your app look like, but if it is possible to split your app into two servlets or filters, then you can do it like that - i.e. have one servlet/filter serve one set of resources and include the first mapper and have the other servlet/filter serve the other set of resources and include the other mapper.
If these are custom exceptions, you can also pass Request as an argument to the exception and have just a single mapper - decide on the response based in the request uri in the mapper.

Resources