How to validate a MultipartFile using HibernateValidator? - spring

I have a field of type MultipartFile in a backing bean which is bound to a Spring form (I'm using MultipartFilter),
<form:form htmlEscape="false" action="${pageContext.request.contextPath}/admin_side/Category.htm" id="dataForm" name="dataForm" method="post" commandName="categoryBean" enctype="multipart/form-data">
<input type="file" id="txtCatImage" name="txtCatImage"/>
</form:form>
Backing Bean,
final public class CategoryBean
{
private MultipartFile txtCatImage=null;
public MultipartFile getTxtCatImage()
{
return txtCatImage;
}
public void setTxtCatImage(MultipartFile txtCatImage)
{
this.txtCatImage = txtCatImage;
}
}
I have tried to apply annotations like #NotEmpty but didn't work. They ended up with an exception.
javax.validation.UnexpectedTypeException: No validator could be found
for type: org.springframework.web.multipart.MultipartFile
I'm using Validation-API 1.0.0. Is this possible to perform a validation, if a user doesn't upload a file and press a submit button using HibernateValidator?

Now I understand what you are trying to accomplish specifically. Well you could implement your own custom validation. For example, instead of implementing the Spring validator interface, implement a Hibernate ConstraintValidator, for a specific annotation that you define for this specific case. Check this link. In this case you could add an implementation for a #NotNull validator for a MultipartFile object.

Putting constraint on bean method checking file on emptiness worked for me:
final public class CategoryBean
{
private MultipartFile txtCatImage = null;
public MultipartFile getTxtCatImage() { return txtCatImage; }
public void setTxtCatImage(MultipartFile txtCatImage) { this.txtCatImage = txtCatImage; }
#AssertTrue(message = "File must be provided")
public boolean isFileProvided() {
return (txtCatImage != null) && ( ! txtCatImage.isEmpty());
}
}

Related

form:checkbox not working in Spring MVC JSP page

I have the following Model class:
public class BlocchettiScratchCards implements Serializable {
...
private boolean flagNuovaGestione;
public boolean isFlagNuovaGestione() {
return flagNuovaGestione;
}
public void setFlagNuovaGestione(boolean flagNuovaGestione) {
this.flagNuovaGestione = flagNuovaGestione;
}
}
and the Bean class:
public class BlocchettiScratchCardsBean implements Serializable {
...
private boolean flagNuovaGestione;
public boolean isFlagNuovaGestione() {
return flagNuovaGestione;
}
public void setFlagNuovaGestione(boolean flagNuovaGestione) {
this.flagNuovaGestione = flagNuovaGestione;
}
}
in the JSP we have a <form:form> tag with the following:
<form:checkbox path="flagNuovaGestione"/>
The controller class initialize the property as follows:
BlocchettiScratchCardsBean elencoCards = new BlocchettiScratchCardsBean();
elencoCards.setFlagNuovaGestione(true);
but when the page is loaded, the checkbox is not checked by default as I expect.
Inspecting HTML of page, I see the following:
<input id="flagNuovaGestione1" name="flagNuovaGestione" type="checkbox" value="true">
<input type="hidden" name="_flagNuovaGestione" value="on">
but the checked property is not set.
Why do I have this behaviour and how could I solve this issue?
I've found the solution on my own.
The problem was into the Controller class, in particular the instruction that adds the formBean into the jsp view was executed before the setting of the flagNuovaGestione boolean variable.
This was the old / not working code:
BlocchettiScratchCardsBean elencoCards = new BlocchettiScratchCardsBean();
view.addObject("formBean", elencoCards);
elencoCards.setFlagNuovaGestione(true);
and the following is the right one:
BlocchettiScratchCardsBean elencoCards = new BlocchettiScratchCardsBean();
elencoCards.setFlagNuovaGestione(true);
view.addObject("formBean", elencoCards);

Spring Validation Errors for RequestParam

I want to pass org.springframework.validation.Errors to CodeValidator class.
But, since I am not using RequestBody/RequestPart/ModelAttribute, I cannot put Errors in method param after variable.
I use #RequestParam for code variable, and I want to validate that using CodeValidator class that implement org.springframework.validation.Validator.
Here is my code
#RequestMapping(value = "/check-code", method = RequestMethod.POST)
public ResponseEntity<Object> checkCode(#RequestParam("code") String code, Errors errors) {
codeValidator.validate(code, errors);
if(errors.hasErrors()) {
return ResponseEntity.badRequest().body("Errors");
}
return ResponseEntity.ok("");
}
and here error result for my code:
An Errors/BindingResult argument is expected to be declared immediately after the model attribute, the #RequestBody or the #RequestPart arguments to which they apply: public org.springframework.http.ResponseEntity com.example.myapp.controller.CodeController.checkCode(java.lang.String,org.springframework.validation.BindingResult)
what should I do to be able using CodeValidator with #RequestParam?
Updated:
Code for CodeValidator
#Service
public class CodeValidator implements Validator {
#Override
public void validate(Object target, Errors errors) {
String code = ((String) target);
if(code == null || code.isEmpty()) {
errors.rejectValue("code", "", "Please fill in Code.");
}
}
}
Did you create an annotation with your validator?
Otherwise take a look at a small example/tutorial for custom validating with spring: https://www.baeldung.com/spring-mvc-custom-validator
(edit) if you are using spring boot you might need add a MethodValidationPostProcessor bean to your spring config to enable custom valdation for the #requesParam

Spring Boot binding and validation error handling in REST controller

When I have the following model with JSR-303 (validation framework) annotations:
public enum Gender {
MALE, FEMALE
}
public class Profile {
private Gender gender;
#NotNull
private String name;
...
}
and the following JSON data:
{ "gender":"INVALID_INPUT" }
In my REST controller, I want to handle both the binding errors (invalid enum value for gender property) and validation errors (name property cannot be null).
The following controller method does NOT work:
#RequestMapping(method = RequestMethod.POST)
public Profile insert(#Validated #RequestBody Profile profile, BindingResult result) {
...
}
This gives com.fasterxml.jackson.databind.exc.InvalidFormatException serialization error before binding or validation takes place.
After some fiddling, I came up with this custom code which does what I want:
#RequestMapping(method = RequestMethod.POST)
public Profile insert(#RequestBody Map values) throws BindException {
Profile profile = new Profile();
DataBinder binder = new DataBinder(profile);
binder.bind(new MutablePropertyValues(values));
// validator is instance of LocalValidatorFactoryBean class
binder.setValidator(validator);
binder.validate();
// throws BindException if there are binding/validation
// errors, exception is handled using #ControllerAdvice.
binder.close();
// No binding/validation errors, profile is populated
// with request values.
...
}
Basically what this code does, is serialize to a generic map instead of model and then use custom code to bind to model and check for errors.
I have the following questions:
Is custom code the way to go here or is there a more standard way of doing this in Spring Boot?
How does the #Validated annotation work? How can I make my own custom annotation that works like #Validated to encapsulate my custom binding code?
This is the code what i have used in one of my project for validating REST api in spring boot,this is not same as you demanded,but is identical.. check if this helps
#RequestMapping(value = "/person/{id}",method = RequestMethod.PUT)
#ResponseBody
public Object updatePerson(#PathVariable Long id,#Valid Person p,BindingResult bindingResult){
if (bindingResult.hasErrors()) {
List<FieldError> errors = bindingResult.getFieldErrors();
List<String> message = new ArrayList<>();
error.setCode(-2);
for (FieldError e : errors){
message.add("#" + e.getField().toUpperCase() + ":" + e.getDefaultMessage());
}
error.setMessage("Update Failed");
error.setCause(message.toString());
return error;
}
else
{
Person person = personRepository.findOne(id);
person = p;
personRepository.save(person);
success.setMessage("Updated Successfully");
success.setCode(2);
return success;
}
Success.java
public class Success {
int code;
String message;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
Error.java
public class Error {
int code;
String message;
String cause;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getCause() {
return cause;
}
public void setCause(String cause) {
this.cause = cause;
}
}
You can also have a look here : Spring REST Validation
Usually when Spring MVC fails to read the http messages (e.g. request body), it will throw an instance of HttpMessageNotReadableException exception. So, if spring could not bind to your model, it should throw that exception. Also, if you do NOT define a BindingResult after each to-be-validated model in your method parameters, in case of a validation error, spring will throw a MethodArgumentNotValidException exception. With all this, you can create ControllerAdvice that catches these two exceptions and handles them in your desirable way.
#ControllerAdvice(annotations = {RestController.class})
public class UncaughtExceptionsControllerAdvice {
#ExceptionHandler({MethodArgumentNotValidException.class, HttpMessageNotReadableException.class})
public ResponseEntity handleBindingErrors(Exception ex) {
// do whatever you want with the exceptions
}
}
You can't get BindException with #RequestBody. Not in the controller with an Errors method parameter as documented here:
Errors, BindingResult For access to errors from validation and data
binding for a command object (that is, a #ModelAttribute argument) or
errors from the validation of a #RequestBody or #RequestPart
arguments. You must declare an Errors, or BindingResult argument
immediately after the validated method argument.
It states that for #ModelAttribute you get binding AND validation errors and for your #RequestBody you get validation errors only.
https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-ann-methods
And it was discussed here:
https://github.com/spring-projects/spring-framework/issues/11406?jql=text%2520~%2520%2522RequestBody%2520binding%2522
For me it still does not make sense from a user point of view. It is often very important to get the BindExceptions to show the user a proper error message. The argument is, you should do client side validation anyway. But this is not true if a developer is using the API directly.
And imagine your client side validation is based on an API request. You want to check if a given date is valid based on a saved calendar. You send the date and time to the backend and it just fails.
You can modify the exception you get with an ExceptionHAndler reacting on HttpMessageNotReadableException, but with this exception I do not have proper access to which field was throwing the error as with a BindException. I need to parse the exception message to get access to it.
So I do not see any solution, which is kind of bad because with #ModelAttribute it is so easy to get binding AND validation errors.
I've given up on this; it is just not possible to get the binding errors using #RequestBody without a lot of custom code. This is different from controllers binding to plain JavaBeans arguments because #RequestBody uses Jackson to bind instead of the Spring databinder.
See https://jira.spring.io/browse/SPR-6740?jql=text%20~%20%22RequestBody%20binding%22
One of the main blocker for solving this problem is the default eagerly-failing nature of the jackson data binder; one would have to somehow convince it to continue parsing instead of just stumble at first error. One would also have to collect these parsing errors in order to ultimately convert them to BindingResult entries. Basically one would have to catch, suppress and collect parsing exceptions, convert them to BindingResult entries then add these entries to the right #Controller method BindingResult argument.
The catch & suppress part could be done by:
custom jackson deserializers which would simply delegate to the default related ones but would also catch, suppress and collect their parsing exceptions
using AOP (aspectj version) one could simply intercept the default deserializers parsing exceptions, suppress and collect them
using other means, e.g. appropriate BeanDeserializerModifier, one could also catch, suppress and collect the parsing exceptions; this might be the easiest approach but requires some knowledge about this jackson specific customization support
The collecting part could use a ThreadLocal variable to store all necessary exceptions related details. The conversion to BindingResult entries and the addition to the right BindingResult argument could be pretty easily accomplished by an AOP interceptor on #Controller methods (any type of AOP, Spring variant including).
What's the gain
By this approach one gets the data binding errors (in addition to the validation ones) into the BindingResult argument the same way as would expect for getting them when using an e.g. #ModelAttribute. It will also work with multiple levels of embedded objects - the solution presented in the question won't play nice with that.
Solution Details (custom jackson deserializers approach)
I created a small project proving the solution (run the test class) while here I'll just highlight the main parts:
/**
* The logic for copying the gathered binding errors
* into the #Controller method BindingResult argument.
*
* This is the most "complicated" part of the project.
*/
#Aspect
#Component
public class BindingErrorsHandler {
#Before("#within(org.springframework.web.bind.annotation.RestController)")
public void logBefore(JoinPoint joinPoint) {
// copy the binding errors gathered by the custom
// jackson deserializers or by other means
Arrays.stream(joinPoint.getArgs())
.filter(o -> o instanceof BindingResult)
.map(o -> (BindingResult) o)
.forEach(errors -> {
JsonParsingFeedBack.ERRORS.get().forEach((k, v) -> {
errors.addError(new FieldError(errors.getObjectName(), k, v, true, null, null, null));
});
});
// errors copied, clean the ThreadLocal
JsonParsingFeedBack.ERRORS.remove();
}
}
/**
* The deserialization logic is in fact the one provided by jackson,
* I only added the logic for gathering the binding errors.
*/
public class CustomIntegerDeserializer extends StdDeserializer<Integer> {
/**
* Jackson based deserialization logic.
*/
#Override
public Integer deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
try {
return wrapperInstance.deserialize(p, ctxt);
} catch (InvalidFormatException ex) {
gatherBindingErrors(p, ctxt);
}
return null;
}
// ... gatherBindingErrors(p, ctxt), mandatory constructors ...
}
/**
* A simple classic #Controller used for testing the solution.
*/
#RestController
#RequestMapping("/errormixtest")
#Slf4j
public class MixBindingAndValidationErrorsController {
#PostMapping(consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
public Level1 post(#Valid #RequestBody Level1 level1, BindingResult errors) {
// at the end I show some BindingResult logging for a #RequestBody e.g.:
// {"nr11":"x","nr12":1,"level2":{"nr21":"xx","nr22":1,"level3":{"nr31":"xxx","nr32":1}}}
// ... your whatever logic here ...
With these you'll get in BindingResult something like this:
Field error in object 'level1' on field 'nr12': rejected value [1]; codes [Min.level1.nr12,Min.nr12,Min.java.lang.Integer,Min]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [level1.nr12,nr12]; arguments []; default message [nr12],5]; default message [must be greater than or equal to 5]
Field error in object 'level1' on field 'nr11': rejected value [x]; codes []; arguments []; default message [null]
Field error in object 'level1' on field 'level2.level3.nr31': rejected value [xxx]; codes []; arguments []; default message [null]
Field error in object 'level1' on field 'level2.nr22': rejected value [1]; codes [Min.level1.level2.nr22,Min.level2.nr22,Min.nr22,Min.java.lang.Integer,Min]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [level1.level2.nr22,level2.nr22]; arguments []; default message [level2.nr22],5]; default message [must be greater than or equal to 5]
where the 1th line is determined by a validation error (setting 1 as the value for a #Min(5) private Integer nr12;) while the 2nd is determined by a binding one (setting "x" as value for a #JsonDeserialize(using = CustomIntegerDeserializer.class) private Integer nr11;). 3rd line tests binding errors with embedded objects: level1 contains a level2 which contains a level3 object property.
Note how other approaches could simply replace the usage of custom jackson deserializers while keeping the rest of the solution (AOP, JsonParsingFeedBack).
enter code here
public class User {
#NotNull
#Size(min=3,max=50,message="min 2 and max 20 characters are alllowed !!")
private String name;
#Email
private String email;
#Pattern(regexp="[7-9][0-9]{9}",message="invalid mobile number")
#Size(max=10,message="digits should be 10")
private String phone;
#Override
public String toString() {
return "User [name=" + name + ", email=" + email + ", phone=" + phone + "]";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
}
Controller.java
#Controller
public class User_Controller {
#RequestMapping("/")
public String showForm(User u,Model m)
{
m.addAttribute("user",new User());
m.addAttribute("title","Validation Form");
return "register";
}
#PostMapping("/")
public String register(#Valid User user,BindingResult bindingResult ,Model m)
{
if(bindingResult.hasErrors())
{
return "register";
}
else {
m.addAttribute("message", "Registration successfully... ");
return "register";
}
}
}
register.html
<div class="container">
<div class="alert alert-success" role="alert" th:text="${message}">
</div>
<h1 class="text-center">Validation Form </h1>
<form action="/" th:action="#{/}" th:object="${user}" method="post">
<div class="mb-3">
<label for="exampleInputEmail1" class="form-label">Name</label>
<input type="text" class="form-control" id="exampleInputEmail1" aria-
describedby="emailHelp" th:field="*{name}">
<br>
<p th:if="${#fields.hasErrors('name')}" th:errors="*{name}" class="alert alert-
danger"></p>
</div>
<div class="mb-3">
<label for="exampleInputPassword1" class="form-label">Email</label>
<input type="email" class="form-control" id="exampleInputPassword1" th:field="*
{email}">
<br>
<p th:if="${#fields.hasErrors('email')}" th:errors="*{email}" class="alert alert-
danger"></p>
</div>
<div class="mb-3">
<label for="exampleInputPassword1" class="form-label">Phone</label>
<input type="text" class="form-control" id="exampleInputPassword1" th:field="*
{phone}">
<p th:if="${#fields.hasErrors('phone')}" th:errors="*{phone}" class="alert alert-
danger"></p>
<br>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
According to this post https://blog.codecentric.de/en/2017/11/dynamic-validation-spring-boot-validation/ - you can add an extra parameter "Errors" to your controller method - eg.
#RequestMapping(method = RequestMethod.POST)
public Profile insert(#Validated #RequestBody Profile profile, Errors errors) {
...
}
to then get validation errors, if any, in that.
I think I should answer your questions in reverse order.
For your second question,
The #Validate annotation throws MethodArgumentNotValidException if there is an error during field validation. The object of this annotation contains two methods, getBindingResult(),getAllErrors() which gives details of validation error. You may create your custom annotation with AspectJ (AOP). But that's not needed here. Because your situation can be solved using the ExceptionHandler of SpringBoot.
Now your first question,
Please go through section 5 of this link Link. Actually it covers whole bean validation in spring boot. Your problem can be solved by section 5. Basic knowledge on general exception handling in spring boot may be good to understand it better. For that, I can share the query link on google for this topic ExceptionHandling.Please go through the first few results of it.

JSR 303 set custom validator for cascade #valid

I want to validate a form object which is contained in another form object. I have something like this:
#Controller
public class FormController {
#RequestMapping(method = RequestMethod.POST)
public void process(#ModelAttribute("form") #Valid FormObject formObject,
BindingResult result) {
...
#InitBinder("form")
protected void initBinder(WebDataBinder binder) {
binder.setValidator(customFormValidator);
}
}
public class FormObject {
#Valid
private FormObject2 formObject2;
}
// This is the class that needs to be validated.
public class FormObject2 {
#NotEmpty
private String name;
}
}
The problem I'm having is that I want the object formObject2 to be validated by another custom validator (e.g. "customFormValidator2"), but I can't find how to register it. If I let it like this, the spring validator will validate the second form.
I have tried inside customFormValidator to validate the second form, but then the paths for the errors in the second form are not relative to the first form and I can't display the errors in the jsp page.
I have structured my form object like this, because I might need the second form inside other forms and by doing this I make it more modularized.
Is it possible what I'm trying to do? Do you have better suggestions?
You can use custom validator to validate form Object2 like
public void process(#ModelAttribute("form") #Valid FormObject formObject,
BindingResult result) {
customFormValidator2.validate(formObject.getFormObject2(), result);
}
And remove #Valid to remove JSR validation
public class FormObject {
// #Valid REMOVE THIS
private FormObject2 formObject2;
}
Also you can use path attribute for nested classes like
<form:input path="formObject2.any_property_name">
You can also use same path for errors without any problem.
Check this link for more details.

jsr-303 validation in spring mvc application doesn't validate

I'm having some trouble setting up validation for a form in spring.
The bean I would like to validate look like this:
public class RegistrationForm extends ProjectXUser {
#NotEmpty
private String password2;
#NotBlank
#AssertTrue
private Boolean agreedToConditions;
...
ProjectXUser inherits from BaseUser which has some more properties which are also annotated.
My controller looks like this:
#Controller
public class RegistrationController {
private static final String REGISTRATION_JSP = "registration";
#ModelAttribute("registrationForm")
public RegistrationForm getRegistrationForm() {
return new RegistrationForm();
}
#RequestMapping(value = { "/registratie/jaar", "registratie/proef" }, method = RequestMethod.GET)
public String year() {
return "registration";
}
#RequestMapping(value = { "/registratie/jaar", "registratie/proef" }, method = RequestMethod.POST)
public ModelAndView register(#Valid RegistrationForm registrationForm, BindingResult result) {
if (result.hasErrors()) {
return new ModelAndView(REGISTRATION_JSP);
} else {
return new ModelAndView("redirect:/registratie/success");
}
}
}
My spring configuration file contains:
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />
<mvc:annotation-driven />
I've read in the spring documentation that if a jsr-303 validator is present in the class path spring will detect it automatically and use it. So i've added hibernate-validator to my pom.
But when I debug my controller I can see the registrationForm contains the values I've filled in. But results always has 0 errors. Even if I enter some explicit wrong input in my form fields.
You need to return the result in the ModelAndView in the case where there are errors. All you are returning is an empty mav.
See: http://forum.springsource.org/showthread.php?117436-Spring-MVC-3-and-returning-validation-errors-to-page-from-Valid&highlight=bindingresult for an example.
If this bit of your code is firing and redirecting back to your registration page
if (result.hasErrors()) {
return new ModelAndView(REGISTRATION_JSP);
}
Then your JSR-303 validation is being picked up. You need to display your errors on your JSP page like this
<form:errors path="password2" cssClass="error" />
where cssClass="error" is the CSS you want to display the error with. It's automatically put into a <div> for you.

Resources