What's the difference between #GetMapping and #RequestMapping(method = RequestMethod.GET)?
I've seen in some Spring Reactive examples, that
#GetMapping was used instead of #RequestMapping
#GetMapping is a composed annotation that acts as a shortcut for #RequestMapping(method = RequestMethod.GET).
#GetMapping is the newer annotaion.
It supports consumes
Consume options are :
consumes = "text/plain"
consumes = {"text/plain", "application/*"}
For Further details see:
GetMapping Annotation
or read:
request mapping variants
RequestMapping supports consumes as well
GetMapping we can apply only on method level and RequestMapping annotation we can apply on class level and as well as on method level
As you can see here:
Specifically, #GetMapping is a composed annotation that acts as a
shortcut for #RequestMapping(method = RequestMethod.GET).
Difference between #GetMapping & #RequestMapping
#GetMapping supports the consumes attribute like
#RequestMapping.
#RequestMapping is a class level
#GetMapping is a method-level
With sprint Spring 4.3. and up things have changed. Now you can use #GetMapping on the method that will handle the http request. The class-level #RequestMapping specification is refined with the (method-level)#GetMapping annotation
Here is an example:
#Slf4j
#Controller
#RequestMapping("/orders")/* The #Request-Mapping annotation, when applied
at the class level, specifies the kind of requests
that this controller handles*/
public class OrderController {
#GetMapping("/current")/*#GetMapping paired with the classlevel
#RequestMapping, specifies that when an
HTTP GET request is received for /order,
orderForm() will be called to handle the request..*/
public String orderForm(Model model) {
model.addAttribute("order", new Order());
return "orderForm";
}
}
Prior to Spring 4.3, it was #RequestMapping(method=RequestMethod.GET)
Extra reading from a book authored by Craig Walls
Short answer:
There is no difference in semantic.
Specifically, #GetMapping is a composed annotation that acts as a
shortcut for #RequestMapping(method = RequestMethod.GET).
Further reading:
RequestMapping can be used at class level:
This annotation can be used both at the class and at the method level.
In most cases, at the method level applications will prefer to use one
of the HTTP method specific variants #GetMapping, #PostMapping,
#PutMapping, #DeleteMapping, or #PatchMapping.
while GetMapping only applies to method:
Annotation for mapping HTTP GET requests onto specific handler
methods.
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/GetMapping.html
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestMapping.html
`#RequestMapping` since 2.5
=> Can handle all HTTP methods
=> Applicable to class and method
=> Can be used as a substitute of #Controller and #RestController, If we use it
along with #Component.
`#GetMapping` since 4.3
=> Can handle only GET method of HTTP
=> Applicable to method only
#GetMapping is a specific type of #RequestMapping(method = RequestMethod.GET). Both supports consumes
#RequestMapping supports consumes even with method=GET, while #GetMapping doesn't supports consumes.
#RequestMapping is Method & Type level annotation, while #GetMapping is a Method level annotation
Other than that #GetMapping is same as #RequestMapping(method=RequestMethod.GET)
#GetMapping is the shortcut for #RequestMapping(method = RequestMethod.GET)
#RequestMapping is a class level
#GetMapping is a method-level
4)The #RequestMapping annotation is used to map web requests to specific handler classes and functions. This annotations key advantage is that it may be used on both the controller class and methods.
5)It is always advised to be specific while declaring #RequestMapping on the controller methods as in most mapping handler classes, #Getmapping is not used.
Related
Given the #Controller below, even if i send a Get Request to the webApp, the controller run the homePage method.
#RestController
#RequestMapping(method = RequestMethod.POST)
public class MyController {
#GetMapping("/hello")
public String homePage() {
return "Hello, It is my first application";
}
}
How could that happen? Normally, i restrict that from the class level.
Your method with #GetMapping("/hello") picked up as most specific and enables GET requests with /hello path
This annotation can be used both at the class and at the method level. In most cases, at the method level applications will prefer to use one of the HTTP method specific variants #GetMapping
Background:
I am developing an web application with Spring MVC.
I want to make an aspect that is executed on POST requests and not executed on GET requests, because I want to inject the logic that prevent POST requests which are sent before completion of HTML rendering.
#RequestMapping(value = "/aaa", method = RequestMethod.POST)
public String methodForPost(AnDto dto, Model model) {
// the aspect should be executed on this method
}
#RequestMapping(value = "/bbb", method = RequestMethod.GET)
public String methodForGET(AnDto dto, Model model) {
// the aspect shouldn't be executed on this method
}
Question:
How can I specify method with an parameterized annotation and its value with #Pointcut ?
How can I specify method with an parameterized annotation and its value in <aop:pointcut> in Spring applicationContext.xml?
#Around(value="#annotation(RequestMapping)")
public Object display(ProceedingJoinPoint joinPoint, RequestMapping requestMapping ) throws Throwable {
// You have access to requestMapping. From which you can get whether its get or post and decide to proceed or not.
}
More info http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html#aop-ataspectj-advice-params-passing
You might have to extend this to intercept only for Requestmapping's in your package. Because this intercepts every RequestMappig you might have in your project including one used by the libraries which you might be using, which is a burden.
The source code for Spring OAuth2's AuthorizationEndpoint contains two redundant #RequestMapping annotations for the same /oauth/authorize endpoint. One of them specifies the POST method, while the other does not specify a method.
How are the two #RequestMapping annotations interpreted? Does the one that specifies POST exclusively handle all POST /oauth/authorize requests, and does the one that does not specify a method exclusively handle any non-POST requests to /oauth/authorize? Or do both methods overlap, with both methods being called for certain requests?
This is probably a Spring MVC question, though the source code on GitHub uses Spring MVC to define what is Spring OAuth2.
Though the complete source code is available on GitHub at the link that the top of this OP, the headers for the two relevant methods are summarized here as follows:
#RequestMapping(value = "/oauth/authorize")
public ModelAndView authorize(Map<String, Object> model, #RequestParam Map<String, String> parameters,
SessionStatus sessionStatus, Principal principal) {
//other stuff
}
#RequestMapping(value = "/oauth/authorize", method = RequestMethod.POST, params = OAuth2Utils.USER_OAUTH_APPROVAL)
public View approveOrDeny(#RequestParam Map<String, String> approvalParameters, Map<String, ?> model,
SessionStatus sessionStatus, Principal principal) {
//other stuff
}
This is already explained in the official documentation: if you provide the values for the method field, they'll be used to narrow down the mapping. In other words: Spring MVC will use these hints to find the most precise match for each request.
It's also easy to build a simple proof-of-concept application that demonstrates it in practice:
#RequestMapping("/foo")
#ResponseBody
public String hello() {
return "hello, default";
}
#RequestMapping(value="/foo", method = RequestMethod.GET)
#ResponseBody
public String helloGet() {
return "hello, GET";
}
Hitting /foo with a GET request, for instance using Postman, will return "hello, GET". All other supported HTTP methods (POST, PUT, DELETE, etc.) will result in "hello, default".
The default method used by Spring request mapping is GET, so if you specify a request mapping with only #RequestMapping annotation, Spring will route all GET requests for the value of the annotation to this method.
To use any other method you basically need to say the method in the annotation. like #RequestMapping(method = RequestMethod.POST)
So for your example the first method will only handle the GET requests, while the other will handle the POST requests exclusively.
Usually GET in OAuth is used for normal interpretations, while the POST is used to authenticate un-authenticated users using the param passed to the method, which in this case is OAuth2Utils.USER_OAUTH_APPROVAL.
How are the two #RequestMapping annotations interpreted?
First of, from http://javatechig.com/java/spring/how-spring-controller-request-mapping-works-in-spring-mvc the default is interpreted as a GET. This is the first distinction. Second the paramaters of both methods are slightly different where method 1 requests a Map<String, String> and the other method Map<String, ?>. So even if both methods were GET, it would still make the distinction on parameter level.
Does the one that specifies POST exclusively handle all POST
/oauth/authorize requests, and does the one that does not specify a
method exclusively handle any non-POST requests to /oauth/authorize?
Or do both methods overlap, with both methods being called for certain
requests?
The POST exclusively handles post and nothing else. The other method only handles GET requests. They never overlap. As is java's law and Spring is still bound by the rules of the java overlords =)
Iv'e seen this nice mechanism:
http://www.mkyong.com/spring-mvc/spring-3-mvc-and-jsr303-valid-example/
Is it possible to make the #Valid annotation avaialble for all the Controllers with validation? It seems very redundant to do the following:
#RequestMapping(value = "/getPlayerAccounts", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
#ResponseStatus(value = HttpStatus.OK)
#ResponseBody
public QueryResultsDTO<PlayerAccountResultDTO> getPlayerAccounts(#RequestBody **#Valid** FilteredQueryRequestDTO filteredQueryRequestDTO,
**BindingResult result**) {
**this.validateDTO(result);**
return this.playerService.getPlayerAccounts(filteredQueryRequestDTO);
}
Reduandant code:
#Valid
BindingResult result
this.validateDTO(result);
These seems like a recurring pattern, probably someone already solved it? maybe with aspects?
I dont care that all my methods and controllers will have the #Valid login, most of the DTOs they recieve will be valid anyway (since no validation annotations are applied to them)
Thanks
you cannot omit #Valid annotation, since this is the way to tell spring which dto to validate, that is just the way the spring validation works. But having a BindingResult result to each of your methods is not necessary. You may omit it completely. If you want to do something when validation fails, you can catch the MethodArgumentNotValidException that is thrown in that case from an exception handling method (e.g you can use a class with #ControllerAdvice annotations that will contain #ExceptionHandler methods applied to all controllers - exception handling is a whole different topic, you can read more details on related spring mvc exception handling documentation)
Let say we have an API endpoint configured using Spring MVC and Spring Security. We would like to be able to handle pairs of #RequestMapping and #Secured annotations where the only #Secured annotation values differ from pair to pair. This way, we would be able to return a different response body depending on security rules for the same request.
This may allow our code to be more maintainable by avoiding to check for security rules directly into the method body.
With a not working example, here is what we would like to do :
#Controller
#RequestMapping("/api")
public class Controller {
#Secured ({"ROLE_A"})
#RequestMapping(value="{uid}", method=RequestMethod.GET)
#ResponseBody
public Response getSomething(#PathVariable("uid") String uid) {
// Returns something for users having ROLE_A
}
#Secured ({"ROLE_B"})
#RequestMapping(value="{uid}", method=RequestMethod.GET)
#ResponseBody
public Response getSomethingDifferent(#PathVariable("uid") String uid) {
// Returns something different for users having ROLE_B
}
}
How can we achieve this ?
And if this can be done: How the priority should be managed for a user who has both ROLE_A and ROLE_B ?
Assuming you are using Spring 3.1 (or up) together with the RequestMappingHandlerMapping (and RequestMappingHandlerAdapter) you can extend the request mapping mechanism. You can do this by creating your own implementation of the RequestCondition interface and extend the RequestMappingHandlerMapping to construct this based on the #Secured annotation on your method.
You would need to override the 'getCustomMethodCondition' method on the RequestMappingHandlerMapping and based on the Method and the existence of the #Secured annotation construct your custom implementation of the RequestCondition. All that information is then taken into account when matching incoming requests to methods.
Related answers (although not specific for #Secured annotations but the mechanism is the same) is also to be found here or here
I don't think you can do this in spring-mvc, since both routes have exactly the same #RequestMapping (#Secured) is not taken into account by the route engine of spring-mvc. The easiest solution would be to do this:
#Secured ({"ROLE_A", "ROLE_B"})
#RequestMapping(value="{uid}", method=RequestMethod.GET)
#ResponseBody
public Response getSomething(#PathVariable("uid") String uid, Principal p) {
// Principal p gets injected by spring
// and you need to cast it to check access roles.
if (/* p.hasRole("ROLE_A") */) {
return "responseForA";
} else if (/* p.hasRole("ROLE_B") */) {
return "responseForB";
} else {
// This is not really needed since #Secured guarantees that you don't get other role.
return 403;
}
}
However, I would change your design, since the response is different per role, why not have 2 separate request mappings with slightly different URLs? If at some point you have users with role A and B at the same time, you can't let the user choose what response to get (think, for example, of the public and private profiles of LinkedIn)