I am willing to incorporate JSR303-like validation, which would be aware of related HttpServletRequest.
For example, Re-Captcha requires to send IP of user who is solving the CAPTCHa alongside with challenge and response and for that i need something like HttpServltRequest.getRemoteAddr(), so for validating form, containing ReCAPTCHa with annotations/corresponding validators transparently for the controller (like, annotating POJO with something like #ReCaptcha) i need such instance in validation layer.
Ideally, i want to inject my validation logic between
[Http Servlet Request] -> [Jackson or similar data bind of request parts to POJO]
and
#RequestMapping("somewhere")
#ResponseBody
public Object login(#Valid #RequestBody LoginData loginData) {
...
}
call.
Which Spring facilities exists for implementing such validation?
I've found HandlerInterceptor concept which acts a bit before i wanted it to, right after forming HttpServletRequest, before binding of request data to objects seems to be happening.
Looks like i've found something usable:
In controller' class, add
#InitBinder
public void initBinder(WebDataBinder binder, HttpServletRequest request) {
binder.addValidators(new ReCaptchaValidator(request));
}
This #InitBinder-annotated method would be called before each controller #RequestMapping-methods. You could refine this selection by providing as annotation value a list of model attributes name to trigger on.
And ReCaptchaValidator is a mere implementation of org.springframework.validation.Validator interface.
Arguments, which could be used in this #InitBinder-method are, as documentation says, similar to #RequestMapping controller methods:
* <p>Such init-binder methods support all arguments that {#link RequestMapping}
* supports, except for command/form objects and corresponding validation result
* objects. Init-binder methods must not have a return value; they are usually
* declared as {#code void}.
*
* <p>Typical arguments are {#link org.springframework.web.bind.WebDataBinder}
* in combination with {#link org.springframework.web.context.request.WebRequest}
* or {#link java.util.Locale}, allowing to register context-specific editors.
Related
How does the DispatcherServlet (or any other bean, supporting Spring MVC infrastructure), dynamically, resolve the signature of the handler method of the #Component instance, and how does it know, what parameters/types are expected, in which order, in that method?
If I have a "#RequestMapping-ed" handler method, in my #Controller instance, defining any version of its signature:
public String f() {...}
public String f(Model model) {...}
public String f(HttpServletRequest, Model model) {...}
public String f(Model model, HttpServletRequest) {...}
public String f(SomeEntity se, Model model, HttpServletRequest, AnotherModel am) {...}
//so on..
would work fine, disregarding of parameter number, types, and order, which, eventually, are supplied with corresponding arguments, again - disregarding of their number, order and types.
There should be quite a work going underneath, to correctly instantiate expected arguments, pass them in a proper order, maybe even do some type-casting, if needed.. an so on, but I don't grasp the fundamentals of this.
I looked up the corresponding sources from spring-webmvc module, and sources of the DispatcherServlet; however, haven't got a firm grasp of the underlying mechanism.
I can guess, that some BeanPostProcessors could be involved.. doing reflective accesses, and so on.. but even in this case, as I'm not certain on what's happening, I would very much appreciate any valuable input.
In Spring MVC there are three implementations of HandlerMapping: BeanNameUrlHandlerMapping, SimpleUrlHandlerMapping and ControllerClassNameHandlerMapping.
You can read more about this subject here:
https://www.baeldung.com/spring-handler-mappings
I've not used Spring in years, but this post gives a pretty good step-by-step outline of the spring mvc request lifecycle
When the DispatcherServlet receives a request, it iterates over this list until it finds a matching handler object for the request in question. For simplicity, let's consider only RequestMappingHandlerMapping.
A bean of this type stores a mapping of #RequestMapping annotated methods (the actual Method object retrieved with reflection) stored as a HandlerMethod instances and wrapped in RequestMappingInfo objects that hold mapping data for matching the request, ie. URL, headers, and request parameters.
The DispatcherServlet retrieves the best matching HandlerMethod from these and any corresponding HandlerInterceptor instances which you may have registered. It retrieves these as a HandlerExecutionChain object. It will first apply any pre-handling by HandlerInterceptors. It will then try to invoke your HandlerMethod. This will typically (but not always) be a #RequestMapping annotated method inside a #Controller annotated class. This produces what Spring calls a dispatch result. The DispatcherServlet then applies post-handling by the HandlerInterceptors.
In a SpringBoot REST application I have a TableRequest type that contains column sorting, filtering, and paging details for GET requests for tabular data. It's generic in that it doesn't care what the specific data being requested is, it only specifies generic table parameters. As such it's applicable across many different controller methods. Also, because it applies to GET requests the fields are passed as request parameters (no #RequestBody json parameter). I've got a #ModelAttribute method inside the controller class that parses the request parameters into a TableRequest object, then the actual #RequestMapping method receives that object as a #ModelAttribute parameter.
Because the TableRequest class is generic, I'd like to be able to use it across multiple controllers without having to copy the parsing logic into every one. I'm wondering if there's a Spring-y annotation-based way of reusing the same #ModelAttribute method any time a controller has a TableRequest input parameter.
Thanks in advance :)
My Solution (based on selected answer below)
I created a #TableRequestController annotation and a corresponding #ControllerAdvice class that applies only to controller classes that have that annotation. That ControllerAdvice class includes the #ModelAttribute method tht parses the GET request parameters into a TableRequest object.
The one important caveat here is that the new #TableRequestController may only be applied to Controller class as a whole, not to individual controller methods. As such, I created a separate inner controller class, tagged with that annotation, whose #RequestMapping methods all accept a TableRequest object.
#TableRequestController:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface TableRequestController {}
ControllerAdvice class:
#ControllerAdvice(annotations = TableRequestController.class)
public class TableRequestControllerAdvice {
#ModelAttribute
public TableRequest tableRequest(
#RequestParam Map<String, String> params,
#RequestParam int pageStart,
#RequestParam int pageSize) {
return new TableRequest(params, pageStart, pageSize);
}
}
TableRequest REST controller class:
#RestController
#TableRequestController
public static class MyTableRequestController {
#RequestMapping("/the/table/request/url")
public MyResponse makeTableRequest(
TableRequest tableRequest) {
return new MyResponse(tableRequest);
}
}
You can use #ControllerAdvice. Everything defined here applies to all controllers, or a defined subset, if you prefer that.
Documentation
Another alternative (better imho) is to write a message converter. It only handles one specific type. You no longer need #ModelAttribute but simply have a TableRequest parameter in you controller method.
In a SpringBoot REST application I have a TableRequest type that
contains column sorting, filtering, and paging details for GET
requests for tabular data. It's generic in that it doesn't care what the specific data being requested is, it only specifies generic table parameters
This means what you have is a utility service.
So define the class as service and access it inside the views.
Access any beans in your application context using SpringEL’s syntax: ${#myBean.doSomething()}
I am new to Spring MVC and went thru some tutorials on net.
Came across below code in one of handler class under my project.
When form is submitted for action userHistory , call goes to my below handler method
#RequestMapping(value="/userHistory", method=RequestMethod.GET)
public #ResponseBody UserDetails getUserHistory(Model model, HttpServletRequest request, HttpServletResponse response) {
model.addAttribute("userDetail", new userDetail());
}
Does DispatcherServlet construct empty Model object and pass to handler method getUserHistory?
Similarly when i submit for action "/userDetail" from my jsp, i get method parameter "userDetail" object filled with required data.
#RequestMapping(value="/userDetail", method=RequestMethod.POST)
public String userDetail(UserDetail userDetail, HttpServletRequest request, HttpServletResponse response, Locale locale)
{}
Is DispatcherServlet doing this or some other interceptor?
UPDATE:- JSP code snippet is
<form:form id="userForm" action="path/userDetail" method="post" commandName="userDetail">
Does DispatcherServlet construct empty Model object and pass to handler method getUserHistory?
Partially yes, an empty Model gets constructed, and its passed to the method getUserHistory. But its not really done by the DispatcherServlet, rather an implementation of HandlerMethodArgumentResolver (in the particular case a ModelMethodProcessor class). When the matching of the method is done, before the method is actually called another process takes place, that is argument resolving. The signature of the matched method is inspected, and objects of certain types known to spring get automatically resolved and injected by Spring. The types are defined in the docs, http://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-ann-methods check the list under subtitle Supported method argument types. All of the listed types, have a registered HandlerMethodArgumentResolver implementation that guides the creation of these objects
Is DispatcherServlet doing this or some other interceptor?
Picking up with the first answer, you can register your custom argument resolver. This great blog article tells you all you need to know to implement one. Note that UserDetail by what you have described could also be a form-backing bean, whose values are bound to the values of the input fields of the submitted form, here's an example http://www.codejava.net/frameworks/spring/spring-mvc-form-handling-tutorial-and-example
I want to register a custom HandlerMethodArgumentResolver that could handle the following #Controller handler method definition
#RequestMapping(method = RequestMethod.POST)
public String createDomain(#Valid Domain domain, BindingResult errors, #RequestParam("countryId") Long countryId) {
I can register my resolver, which just creates a Domain object through request parameters, by overriding addArgumentResolver() from WebMvcConfigurerAdapter. When Spring tries to resolve the Domain parameter, it goes through its list of HandlerMethodArgumentResolver (there are a lot) and picks the first one that supports() it.
In the above example, although my resolver will get called and my Domain argument will get initialized, the #Valid annotation won't have been processed and the resolver for BindingResult, an ErrorsMethodArgumentResolver will fail because it requires a #ModelAttribute, #RequestBody or the #RequestPart argument in the handler method, which I don't have.
If I try to fix it by adding #ModelAttribute
#RequestMapping(method = RequestMethod.POST)
public String createDomain(#Valid #ModelAttribute Domain domain, BindingResult errors, #RequestParam("countryId") Long countryId) {
a HandlerMethodArgumentResolver implementation, ModelAttributeMethodProcessor, will get checked first with supports() and resolve the argument (with #ModelAttribute and #Valid) before my custom resolver. The BindingResult won't fail, but I won't have my custom creation behavior on the Domain instance.
I could just copy-paste the code for validation and adding to model that's in ModelAttributeMethodProcessor, but I was hoping there was an easier way to resolve my parameters and perform validation without adding an object to the model. Is there such a way?
Nice description of the issue that you are facing.
I checked out the code that you have outlined and have come to the same conclusion that you have - there is no built-in way to have both a custom HandlerMethodArgumentResolver as well as #Valid related validation applied at the same time, the only choice is to do what the ModelAttributeMethodProcessor does which is to check if the parameter has a #Valid annotation and call the validation logic related code.
You can probably derive your HandlerMethodResolverArgumentResolver from ModelAttributeMethodProcessor and call super.validateIfApplicable(..) atleast this way the existing code is leveraged.
It's may be too late, but your HandlerMethodArgumentResolver gets WebDataBinderFactory object as last argument, then, to hook up the validation, simply add this to your resolver implementation:
Object resolvedObject = // your logic
if(parameter.hasParameterAnnotation(Valid.class){
binderFactory.createBinder(webRequest,resolvedObject,"resolvedObjectLogicalName").validate ();
}
I am working on spring REST APIs. In requirements, there are 2 POST requests with same URL but different request body. Since Spring MVC must have unique mappings across controller, I have to pre-process the request body to map to a specific POJO.
On the basis of session_type in request body, I have to map the request to specific POJO (JSON -> JAVA POJO).
For example, if 'session_type' in request body is 'typeX' then the request should map to ClassX POJO. If 'session_type' in request body is 'typeY' then the request should map to ClassY POJO.
If there a way to do it using some kind of requestbody annotation?
If you want to bind typeX and typeY, then you definitely need 2 handlers. But, why wouldn't we use param option of #RequestMapping:
#RequestMapping(method = RequestMethod.POST,
value = "/url", params = "session_type=typeX")
public String handleTypeX(#RequestBody #ModelAttribute TypeX typeX){
//TODO implement
}
#RequestMapping(method = RequestMethod.POST,
value = "/url", params = "session_type=typeY")
public String handleTypeY(#RequestBody #ModelAttribute TypeY typeY){
//TODO implement
}
If you need some preparations (f.e. normalize params or perform model binding manually), then the approach above you may combine along with #InitBinder, but please note, that #InitBinder needs exact ULR's rules along with #ModelAttribute parameters in handlers.
EDIT: In Spring MVC there is no possibility to use 2 handlers for exact URL, i.e. when method/URL/params/consumes type are the same.
Thus I suggest use unified handler, where you would check necessary parameter and then manually convert into corresponding class. For finding necessary class I suppose it would be better to use Strategy pattern:
//class resolver according "session_type" parameter
//note, that you can use Spring autowiring capabilities
private final Map<String, Class> TYPES_CONTEXT = new HashMap<String, Class>(){
{
this.put("x", TypeX.class);
this.put("y", TypeY.class);
//TODO probably other classes
}
}
#RequestMapping(method = RequestMethod.POST,
value = "/url")
public #ResponseBody String handleAnyType(#RequestBody Map<String, String> body){
String sessionType = body.get("session_type");
//TODO handle case if sessionType is NULL
Class convertedClass = TYPES_CONTEXT.get(sessionType);
//TODO handle case if class is not found
Object actualObject = objectMapper.convertValue(body, convertedClass);
//now we use reflection for actual handlers, but you may refactor this in the way you want, f.e. again with Strategy pattern
//note that current approach there should be contract for methods names
Method actualHandler = this.getClass().getMethod("handle" + actualObject.getClass().getSimpleName());
return (String)actualHandler.invoke(this, actualObject);
}
public String handleTypeX(TypeX typeX){
//TODO implement
}
public String handleTypeY(TypeY typeY){
//TODO implement
}
//TODO probably other methods
This approach doesn't handle validation and some things were omitted, but I believe this might be helpful.
I think you should created controller with one method for both types, and call required component\method in it depending on typeX or typeY.
GETs shouldn't have request bodies, or at least if they do, the server side isn't required to do anything with them. As you describe it, this API isn't RESTful.
Assuming you don't care about that, try creating a controller method that takes a parent class of TypeX and TypeY, or interface that both TypeX and TypeY implement, annotate it with #SomethingMeaningfulToYou, then use a web argument method resolver to instantiate the child class you want.
It's a hack around a broken API though.
there are 2 POST requests with same URL but different request body
For a RESTful interface, the same URL should always indicate the same resource. The body of a request may contain different representations of that resource. You could create different HttpMessageContverter classes for the two different kinds of representation.