customize Spring MVC requestBody mapping based on HTTP method? - java

My Java 8, Spring boot 1.4 application has a controller method consuming application/json (jackson 2.6.5) as
public MyModel updateModel(#Valid #RequestBody( required = true) MyModel myModel) {
....
}
And within MyModel, I have a field that I want to given auto-generated value when HTTP method = POST, directly use request value when HTTP method = PUT. IS it doable?
Hope I explain it well. Any helps are deeply appreciated

Add an HttpServletRequest parameter and call getMethod().
See Spring documentation for supported method argument types. Or read the javadoc of #RequestMapping.
You should also specify the HTTP Methods you want your controller method to handle, e.g. #RequestMapping(method={RequestMethod.POST, RequestMethod.PUT})

Related

Distinguish Spring Boot PostMapping based on key in RequestBody

I have a REST endpoint with a PostMapping that should be able to accept different objects in the body and map them based on existence of keys.
When I use the same PostMapping for both functions, it gives me an "Ambiguous mapping" error.
When I use params similar to https://www.baeldung.com/spring-requestmapping, the default mapping is called even if the specialKey exists in the request body.
Is there any workaround to achieve this?
#PostMapping(value = "/classes", params = {"specialKey"})
public ResponseEntity<Class> createClass(#Valid #RequestBody SpecialClass class) throws URISyntaxException {
// do something special
}
#PostMapping("/classes")
public ResponseEntity<Class> createClass(#Valid #RequestBody Class class) throws URISyntaxException {
// do something
}
Based on Mapping the same url to different methods based on request body in spring it's not possible (or at least wasn't at the time). The params needs a separate request parameter, it can't be used to look for things inside the request body like that.
You could include the parameter in the URI, the special endpoint would be /classes?specialKey, and the normal endpoint just /classes. But I would just use different paths.

How can I specify method with an parameterized annotation and its value with #Pointcut

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.

How does Spring call these #RequestMappings

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 =)

Adding parameters to #ExceptionHandler for MethodArgumentNotValidException in Spring

I have a Spring controller that validates incoming requests with hibernate validator.
When the request is invalid, MethodArgumentNotValidException is thrown by the validator. Would it be possible to add additional class as an argument to handler method for the exception?
This is what i have:
#RequestMapping(value = "/...", method = RequestMethod.POST)
#ResponseBody
public Response handleCustomObject(#Valid #RequestBody CustomObject obj) {
//..
}
#ExceptionHandler(MethodArgumentNotValidException.class)
#ResponseBody
public Response handleInvalidRequest(MethodArgumentNotValidException e) {
return getMissingMandatoryParametersResponse(e);
}
}
And i would need something like example bellow, however this doesn't work:
#ExceptionHandler(MethodArgumentNotValidException.class)
#ResponseBody
public Response handleInvalidRequest(MethodArgumentNotValidException e, CustomObject obj) {
// do something with CustomObject
}
If you want to do something with the object which failed the validation in the exception handler, you can retrieve it from BindingResult like so:
#ExceptionHandler(MethodArgumentNotValidException.class)
#ResponseBody
public Response handleInvalidRequest(MethodArgumentNotValidException e) {
CustomObject ce = (CustomObject) e.getBindingResult().getTarget();
// do something with CustomObject
}
You can also take a look at Spring JavaDoc for #ExceptionHandler annotation to see the list of supported exception handler method argument types:
Handler methods which are annotated with this annotation are allowed
to have very flexible signatures. They may have arguments of the
following types, in arbitrary order:
An exception argument: declared as a general Exception or as a more specific exception. This also serves as a mapping hint if the
annotation itself does not narrow the exception types through its
value().
Request and/or response objects (Servlet API or Portlet API). You may choose any specific request/response type, e.g. ServletRequest /
HttpServletRequest or PortletRequest / ActionRequest / RenderRequest.
Note that in the Portlet case, an explicitly declared action/render
argument is also used for mapping specific request types onto a
handler method (in case of no other information given that
differentiates between action and render requests).
Session object (Servlet API or Portlet API): either HttpSession or PortletSession. An argument of this type will enforce the presence of
a corresponding session. As a consequence, such an argument will never
be null. Note that session access may not be thread-safe, in
particular in a Servlet environment: Consider switching the
"synchronizeOnSession" flag to "true" if multiple requests are allowed
to access a session concurrently.
WebRequest or NativeWebRequest. Allows for generic request parameter access as well as request/session attribute access, without
ties to the native Servlet/Portlet API.
Locale for the current request locale (determined by the most specific locale resolver available, i.e. the configured LocaleResolver
in a Servlet environment and the portal locale in a Portlet
environment).
InputStream / Reader for access to the request's content. This will be the raw InputStream/Reader as exposed by the Servlet/Portlet
API.
OutputStream / Writer for generating the response's content. This will be the raw OutputStream/Writer as exposed by the Servlet/Portlet
API.

Is there any way to configure spring mvc to assume a content-type?

I have a method to which I want to post some json data, that looks like this
#RequestMapping(value = "/m1", method = RequestMethod.POST)
public Object m1(#RequestBody Map<String, ?> body) {
// do something
}
This works great when I set the content-type header to application/json when I post, but fails with an error if I don't (it cannot deserialize the post body into the map because it doesn't know how)
What would I have to configure in spring to make it use application/json as a default when no header is specified?
The class that converts the JSON to your object is called an HttpMessageConverter. I assume you are using the default Jackson one that comes with Spring. You can write a custom MessageConverter, that will always return true in it's supports method with your response object type and then just call the Jackson httpconverter in your readInternal and writeInternal methods. If you do this however, be careful, as once it's registered in your requesthandler, it will be asked on all #ResponseBody and #RequestBody requests.

Categories

Resources