Validating #RequestParam in Spring 3 MVC - java

How do I validate Spring #RequestParam so that they are in BindingResult with out having to use some sort of POJO transfer object (#ModelAttribute)?
I could use MapBindingResult and put the request parameters in that but then I have to get that binding result into the model.
Which I can do with org.springframework.validation.BindingResult.MODEL_KEY_PREFIX + name.
Is there a better way to bind and validate request parameters (instead of making another POJO)?

If you are using Spring MVC 3.0 or later, you can use the declarative validation support Spring provides. You would then declare the validation restrictions on the model bean and add the #Valid annotation to your form backing bean as described in the chapter "Spring Validation" in the reference docs.
If you add a BindingResult parameter directly after the bean being validated, you can then check for validation errors in your controller:
// .. in the bean class
public class MyBean {
#NotNull
private String name;
// ..
}
// .. in the #Controller
public ModelAndView doSomething(#Valid MyBean data, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
// back to form
}
// do something with the bean
}

Related

Getting ConstraintViolationException when trying to use BindingResult in Spring Controller

I have and endpoint, where I validate received json document which contains collections of objects. I would like to only log these objects which don't pass a validation, when others i would like to store in db. Controller should return 200 OK in that situation. I was trying to use BindingResult object for this purpose. Unfortunately i always get a ConstraintViolationException. It seems that it validates it before it enter the method and throw exception. How can I force it to use BindingResult object ?
#RestController
#Validated
#RequestMapping(path = "/test")
class TestController {
#PostMapping(consumes = APPLICATION_JSON_VALUE)
public ResponseEntity<Void> addObjects(#RequestBody #Valid List<Document> objects, BindingResult bindingResult) {
if(bindingResult.hasErrors()){
}
return new ResponseEntity<Void>(HttpStatus.OK);
}
}
I'm using Spring Boot 1.5.9.RELEASE with Java 8
I've managed to solve it finally. Problem is with #Validated annotation on controller class. With this annotation spring do a validation on request and throw ConstraintViolationException. Without that, validation is triggered later and it results are stored in BindingResult object as expected
Could You please add the model classes with its annotations?
Remember that if You have any fields in Document class which are Your custom defined classes and You want it to be validated also then You have to decorate these fields with #Valid annotation too.

Ambiguous mapping when using RequestBody annotation

I am trying to create an endpoint in Spring Boot that accepts both single object or an array of these objects. I know that mappings need to have unique signiture so I am wondering what is the correct way to make it work using POJOs?
#RequestMapping(method = { RequestMethod.POST })
public ResponseEntity<String> postSingleFoo(HttpServletRequest request,
#RequestBody(required = true) Foo foo) {
// process
}
#RequestMapping(method = { RequestMethod.POST })
public ResponseEntity<String> postMultiFoo(HttpServletRequest request,
#RequestBody(required = true) Foo[] foo) {
// process
}
Obviously I am getting an exception for ambiguous mapping. But I would still like to use POJOs in my #RequestBody annotation since i am performing couple of conversions in them.
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'requestMappingHandlerMapping' defined in class path resource [org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]: Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'fooController' method
public void com.usquared.icecream.lrs.controller.FooController.postSingleFoo(javax.servlet.http.HttpServletRequest,java.lang.String)
to {[/foo],methods=[POST]}: There is already 'fooController' bean method
What is the recommended approach to implement such feature correctly?
This is not a problem that can be fixed through Spring MVC. Spring MVC creates mappings from the #RequestMapping annotating your handler methods. These help distinguish how Spring MVC delegates HTTP requests to be handled by your methods. Your current configuration attempts to map two handler methods to the same request details. That can never work.
One solution, assuming you're expecting JSON and working with Jackson, is to configure your ObjectMapper to accept single values as arrays and define a single handler method with an array parameter. For example, you'd keep only this handler method
#RequestMapping(method = { RequestMethod.POST })
public ResponseEntity<String> postMultiFoo(HttpServletRequest request,
#RequestBody(required = true) Foo[] foo) {
// process
}
but configure your ObjectMapper as such
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
The configuration depends on how your application is configured. With Spring Boot, it should be as simple as declaring a #Bean method for ObjectMapper. With your typical Spring MVC application, you'll need to register a MappingJackson2HttpMessageConverter with a custom ObjectMapper.
If your JSON, the request body, contained
{
"someProperty":"whatever"
}
Jackson would be able to wrap the single value into a Foo[] and Spring MVC would pass that as an argument to your handler method. You can then check the length of the array and act accordingly.

Spring BindingResult in MVC Controller method

Take this part of code for example
#RequestMapping(method=RequestMethod.POST)
public String addUserFromForm(#Valid User user, BindingResult bindingResult){
if(bindingResult.hasErrors()){
return "users/edit";
}
userService.saveUser(user);
return "redirect:/users/"+user.getName();
}
How does the controller's method knows about BindingResult object? Where's that object initialized and passed to the method?
All the parameters passed to controller methods are initialized and filled by the spring-mvc dispatcher servlet.
The User object is instantiated and mapped to request parameters by a binder (DataBinder implementation). If there are problems, the BindingResult is filled with information about these problems and passed to your method.

#RequestMapping does not work on type and method in Spring 2.5

I have been reading on Spring 3.2 lately and I am now trying the following code using Spring 2.5. From what I have read this should mean that it should map profile/tags/me. However it doesn't. It just throws a No mapping found for HTTP request with URI .... What is wrong with the code, or didn't Spring 2.5 work like it does in Spring 3?
Problem when using Spring 2.5
#Controller
#RequestMapping("/profile/tags")
public class ProfileController { ... }
And this is the method inside ProfileController class:
#RequestMapping(value = "/me", method = RequestMethod.GET)
public String show(#RequestParam final long id, final ModelMap model) { ... }
According to Spring documentation, I imagine you're missing the required configuration to receive the request parameter, if you mean to receive this request parameter:
#RequestMapping(value = "/me/{id}", method = RequestMethod.GET)
public String show(#RequestParam("id") final long id, final ModelMap model) { ... }
Or you should remove RequestParam.
Update for Spring 2.5
Additionally, since you're using Spring 2.5, make sure that you've configured your DispatcherServlet in the expected way; Sections 13.11, subsections 1, 2, and 3. In summary:
DispatcherServlet should be told to load annotated RequestMappings.
DispatcherServlet should be told to load Controller annotations.
Not sure but maybe you need to refine the paths you use for the request mappings.
Hope this helps.

What does the #Valid annotation indicate in Spring?

In the following example, the ScriptFile parameter is marked with an #Valid annotation.
What does #Valid annotation do?
#RequestMapping(value = "/scriptfile", method = RequestMethod.POST)
public String create(#Valid ScriptFile scriptFile, BindingResult result, ModelMap modelMap) {
if (scriptFile == null) throw new IllegalArgumentException("A scriptFile is required");
if (result.hasErrors()) {
modelMap.addAttribute("scriptFile", scriptFile);
modelMap.addAttribute("showcases", ShowCase.findAllShowCases());
return "scriptfile/create";
}
scriptFile.persist();
return "redirect:/scriptfile/" + scriptFile.getId();
}
It's for validation purposes.
Validation It is common to validate a
model after binding user input to it.
Spring 3 provides support for
declarative validation with JSR-303.
This support is enabled automatically
if a JSR-303 provider, such as
Hibernate Validator, is present on
your classpath. When enabled, you can
trigger validation simply by
annotating a Controller method
parameter with the #Valid annotation:
After binding incoming POST
parameters, the AppointmentForm will
be validated; in this case, to verify
the date field value is not null and
occurs in the future.
Look here for more info:
http://blog.springsource.com/2009/11/17/spring-3-type-conversion-and-validation/
Adding to above answers, take a look at following. AppointmentForm's date column is annotated with couple of annotations. By having #Valid annotation that triggers validations on the AppointmentForm (in this case #NotNull and #Future). These annotations could come from different JSR-303 providers (e.g, Hibernate, Spring..etc).
#RequestMapping(value = "/appointments", method = RequestMethod.POST)
public String add(#Valid AppointmentForm form, BindingResult result) {
....
}
static class AppointmentForm {
#NotNull #Future
private Date date;
}
#Valid in itself has nothing to do with Spring. It's part of Bean Validation specification(there are several of them, the latest one being JSR 380 as of second half of 2017), but #Valid is very old and derives all the way from JSR 303.
As we all know, Spring is very good at providing integration with all different JSRs and java libraries in general(think of JPA, JTA, Caching, etc.) and of course those guys took care of validation as well. One of the key components that facilitates this is MethodValidationPostProcessor.
Trying to answer your question - #Valid is very handy for so called validation cascading when you want to validate a complex graph and not just a top-level elements of an object. Every time you want to go deeper, you have to use #Valid. That's what JSR dictates. Spring will comply with that with some minor deviations(for example I tried putting #Validated instead of #Valid on RestController method and validation works, but the same will not apply for a regular "service" beans).
I wanted to add more details about how the #Valid works, especially in spring.
Everything you'd want to know about validation in spring is explained clearly and in detail in https://reflectoring.io/bean-validation-with-spring-boot/, but I'll copy the answer to how #Valid works incase the link goes down.
The #Valid annotation can be added to variables in a rest controller method to validate them. There are 3 types of variables that can be validated:
the request body,
variables within the path (e.g. id in /foos/{id}) and,
query parameters.
So now... how does spring "validate"? You can define constraints to the fields of a class by annotating them with certain annotations. Then, you pass an object of that class into a Validator which checks if the constraints are satisfied.
For example, suppose I had controller method like this:
#RestController
class ValidateRequestBodyController {
#PostMapping("/validateBody")
ResponseEntity<String> validateBody(#Valid #RequestBody Input input) {
return ResponseEntity.ok("valid");
}
}
So this is a POST request which takes in a request body, and we're mapping that request body to a class Input.
Here's the class Input:
class Input {
#Min(1)
#Max(10)
private int numberBetweenOneAndTen;
#Pattern(regexp = "^[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}$")
private String ipAddress;
// ...
}
The #Valid annotation will tell spring to go and validate the data passed into the controller by checking to see that the integer numberBetweenOneAndTen is between 1 and 10 inclusive because of those min and max annotations. It'll also check to make sure the ip address passed in matches the regular expression in the annotation.
side note: the regular expression isn't perfect.. you could pass in 3 digit numbers that are greater than 255 and it would still match the regular expression.
Here's an example of validating a query variable and path variable:
#RestController
#Validated
class ValidateParametersController {
#GetMapping("/validatePathVariable/{id}")
ResponseEntity<String> validatePathVariable(
#PathVariable("id") #Min(5) int id) {
return ResponseEntity.ok("valid");
}
#GetMapping("/validateRequestParameter")
ResponseEntity<String> validateRequestParameter(
#RequestParam("param") #Min(5) int param) {
return ResponseEntity.ok("valid");
}
}
In this case, since the query variable and path variable are just integers instead of just complex classes, we put the constraint annotation #Min(5) right on the parameter instead of using #Valid.
IIRC #Valid isn't a Spring annotation but a JSR-303 annotation (which is the Bean Validation standard). What it does is it basically checks if the data that you send to the method is valid or not (it will validate the scriptFile for you).
public String create(#Valid #NotNull ScriptFile scriptFile, BindingResult result, ModelMap modelMap) {
if (scriptFile == null) throw new IllegalArgumentException("A scriptFile is required");
I guess this #NotNull annotation is valid therefore if condition is not needed.
I think I know where your question is headed. And since this question is the one that pop ups in google's search main results, I can give a plain answer on what the #Valid annotation does.
I'll present 3 scenarios on how I've used #Valid
Model:
public class Employee{
private String name;
#NotNull(message="cannot be null")
#Size(min=1, message="cannot be blank")
private String lastName;
//Getters and Setters for both fields.
//...
}
JSP:
...
<form:form action="processForm" modelAttribute="employee">
<form:input type="text" path="name"/>
<br>
<form:input type="text" path="lastName"/>
<form:errors path="lastName"/>
<input type="submit" value="Submit"/>
</form:form>
...
Controller for scenario 1:
#RequestMapping("processForm")
public String processFormData(#Valid #ModelAttribute("employee") Employee employee){
return "employee-confirmation-page";
}
In this scenario, after submitting your form with an empty lastName field, you'll get an error page since you're applying validation rules but you're not handling it whatsoever.
Example of said error:
Exception page
Controller for scenario 2:
#RequestMapping("processForm")
public String processFormData(#Valid #ModelAttribute("employee") Employee employee,
BindingResult bindingResult){
return bindingResult.hasErrors() ? "employee-form" : "employee-confirmation-page";
}
In this scenario, you're passing all the results from that validation to the bindingResult, so it's up to you to decide what to do with the validation results of that form.
Controller for scenario 3:
#RequestMapping("processForm")
public String processFormData(#Valid #ModelAttribute("employee") Employee employee){
return "employee-confirmation-page";
}
#ExceptionHandler(MethodArgumentNotValidException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
public Map<String, String> invalidFormProcessor(MethodArgumentNotValidException ex){
//Your mapping of the errors...etc
}
In this scenario you're still not handling the errors like in the first scenario, but you pass that to another method that will take care of the exception that #Valid triggers when processing the form model. Check this see what to do with the mapping and all that.
To sum up: #Valid on its own with do nothing more that trigger the validation of validation JSR 303 annotated fields (#NotNull, #Email, #Size, etc...), you still need to specify a strategy of what to do with the results of said validation.
Hope I was able to clear something for people that might stumble with this.
Just adding to the above answer, In a web application
#valid is used where the bean to be validated is also annotated with validation annotations e.g. #NotNull, #Email(hibernate annotation) so when while getting input from user the values can be validated and binding result will have the validation results.
bindingResult.hasErrors() will tell if any validation failed.
Another handy aspect of #Valid not mentioned above is that (ie: using Postman to test an endpoint) #Valid will format the output of an incorrect REST call into formatted JSON instead of a blob of barely readable text. This is very useful if you are creating a commercially consumable API for your users.

Categories

Resources