Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

I'm using Java configuration for Spring MVC. I can't get Bean Validation to work. I have a domain class that I've annotated and I want to use @Valid in my Controller. I know that with XML configuration, I would set the validator this way <mvc:annotation-driven validator="validator"/>

How can I do this with Java configuration. I'm not getting any errors, the validation just doesn't work. Thanks in advance!

Here is my set up:

Domain class with the annotations:

public class Product {

    @Pattern(regexp="P[1-9]+", message="{Pattern.Product.productId.validation}")
    @ProductId 
    private String productId;

    @Size(min=4, max=50, message="{Size.Product.name.validation}")
    private String name;

    @Min(value=0, message="Min.Product.unitPrice.validation}")
    @Digits(integer=8, fraction=2, message="{Digits.Product.unitPrice.validation}")
    @NotNull(message= "{NotNull.Product.unitPrice.validation}")
    private BigDecimal unitPrice;
    private String description;
    private String manufacturer;
    private String category;
    private long unitsInStock;

Here is my Controller using @Valid:

@Controller
@RequestMapping("/products")
public class ProductController {

..... (shortened)

@RequestMapping(value = "/add", method = RequestMethod.GET)
    public String getAddNewProductForm(@ModelAttribute("newProduct") Product newProduct) {
       return "addProduct";
    }

    @RequestMapping(value = "/add", method = RequestMethod.POST)
    public String processAddNewProductForm(@ModelAttribute("newProduct") @Valid Product productToBeAdded, BindingResult result, HttpServletRequest request) {
        if(result.hasErrors()) {
            return "addProduct";
        }

        String[] suppressedFields = result.getSuppressedFields();

        if (suppressedFields.length > 0) {
            throw new RuntimeException("Attempting to bind disallowed fields: " + StringUtils.arrayToCommaDelimitedString(suppressedFields));
        }

        MultipartFile productImage = productToBeAdded.getProductImage();
        String rootDirectory = request.getSession().getServletContext().getRealPath("/");

            if (productImage!=null && !productImage.isEmpty()) {
               try {
                productImage.transferTo(new File(rootDirectory+"resources\\images\\"+productToBeAdded.getProductId() + ".png"));
               } catch (Exception e) {
                throw new RuntimeException("Product Image saving failed", e);
           }
           }


        productService.addProduct(productToBeAdded);
        return "redirect:/products";
    }

Here is my Config class with @EnableWebMVC: (***Updated to get validator***)

@SuppressWarnings("deprecation")
@Configuration
@ComponentScan(basePackages = {"com.nam.webstore"})
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {

..... (shortened)

    @Bean(name = "validator")
    public LocalValidatorFactoryBean localValidatorFactoryBean() {
        LocalValidatorFactoryBean lvfb = new LocalValidatorFactoryBean();

        lvfb.setValidationMessageSource(resourceBundleMessageSource());

        return lvfb;
    }

    (***** Updated *****)
    @Override
    public Validator getValidator() {
        return localValidatorFactoryBean();
    }
}

Here is the jsp with the error tags:

..... (shortened)

<section class="container">
    <form:form  modelAttribute="newProduct" class="form-horizontal" enctype="multipart/form-data">
        <fieldset>
            <legend>Add new product</legend>

            <form:errors path="*" cssClass="alert alert-danger" element="div"/>
            <div class="form-group">
                <label class="control-label col-lg-2 col-lg-2" for="productId"><spring:message code="addProduct.form.productId.label"/></label>
                <div class="col-lg-10">
                    <form:input id="productId" path="productId" type="text" class="form:input-large"/>
                    <form:errors path="productId" cssClass="text-danger"/>
                </div>
            </div>

            <div class="form-group">
                <label class="control-label col-lg-2" for="name"><spring:message code="addProduct.form.name.label"/></label>
                <div class="col-lg-10">
                    <form:input id="name" path="name" type="text" class="form:input-large"/>
                    <form:errors path="name" cssClass="text-danger"/>
                </div>
            </div>

            <div class="form-group">
                <label class="control-label col-lg-2" for="unitPrice"><spring:message code="addProduct.form.unitPrice.label"/></label>
                <div class="col-lg-10">
                    <div class="form:input-prepend">
                        <form:input id="unitPrice" path="unitPrice" type="text" class="form:input-large"/>
                        <form:errors path="unitPrice" cssClass="text-danger"/>
                    </div>
                </div>
            </div>

            <div class="form-group">
                <label class="control-label col-lg-2" for="description"><spring:message code="addProduct.form.description.label"/></label>
                <div class="col-lg-10">
                    <form:textarea id="description" path="description" rows = "2"/>
                </div>
            </div>

Updated After setting the Logger to DEBUG, this is what I'm seeing in the console. I can see that it's firing off the validation, but I don't know why it's saying I'm returning null to the DispatcherServlet? I'm returning the view name.

Field error in object 'newProduct' on field 'unitPrice': rejected value [null]; codes [NotNull.newProduct.unitPrice,NotNull.unitPrice,NotNull.java.math.BigDecimal,NotNull]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [newProduct.unitPrice,unitPrice]; arguments []; default message [unitPrice]]; default message [Unit price is Invalid. It cannot be empty.] Field error in object 'newProduct' on field 'productId': rejected value []; codes [Pattern.newProduct.productId,Pattern.productId,Pattern.java.lang.String,Pattern]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [newProduct.productId,productId]; arguments []; default message [productId],[Ljavax.validation.constraints.Pattern$Flag;@3641ef8a,P[1-9]+]; default message [Invalid product ID. It should start with character P followed by number.] Field error in object 'newProduct' on field 'name': rejected value []; codes [Size.newProduct.name,Size.name,Size.java.lang.String,Size]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [newProduct.name,name]; arguments []; default message [name],50,4]; default message [Invalid product name. It should be minimum 4 characters to maximum 50 characters long.] 2014-07-25 15:03:36 DEBUG ResponseStatusExceptionResolver:134 - Resolving exception from handler [public java.lang.String com.nam.webstore.controller.ProductController.processAddNewProductForm(com.nam.webstore.domain.Product,org.springframework.ui.ModelMap,org.springframework.validation.BindingResult,javax.servlet.http.HttpServletRequest)]: org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 3 errors Field error in object 'newProduct' on field 'unitPrice': rejected value [null]; codes [NotNull.newProduct.unitPrice,NotNull.unitPrice,NotNull.java.math.BigDecimal,NotNull]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [newProduct.unitPrice,unitPrice]; arguments []; default message [unitPrice]]; default message [Unit price is Invalid. It cannot be empty.] Field error in object 'newProduct' on field 'productId': rejected value []; codes [Pattern.newProduct.productId,Pattern.productId,Pattern.java.lang.String,Pattern]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [newProduct.productId,productId]; arguments []; default message [productId],[Ljavax.validation.constraints.Pattern$Flag;@3641ef8a,P[1-9]+]; default message [Invalid product ID. It should start with character P followed by number.] Field error in object 'newProduct' on field 'name': rejected value []; codes [Size.newProduct.name,Size.name,Size.java.lang.String,Size]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [newProduct.name,name]; arguments []; default message [name],50,4]; default message [Invalid product name. It should be minimum 4 characters to maximum 50 characters long.] 2014-07-25 15:03:36 DEBUG DefaultHandlerExceptionResolver:134 - Resolving exception from handler [public java.lang.String com.nam.webstore.controller.ProductController.processAddNewProductForm(com.nam.webstore.domain.Product,org.springframework.ui.ModelMap,org.springframework.validation.BindingResult,javax.servlet.http.HttpServletRequest)]: org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 3 errors Field error in object 'newProduct' on field 'unitPrice': rejected value [null]; codes [NotNull.newProduct.unitPrice,NotNull.unitPrice,NotNull.java.math.BigDecimal,NotNull]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [newProduct.unitPrice,unitPrice]; arguments []; default message [unitPrice]]; default message [Unit price is Invalid. It cannot be empty.] Field error in object 'newProduct' on field 'productId': rejected value []; codes [Pattern.newProduct.productId,Pattern.productId,Pattern.java.lang.String,Pattern]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [newProduct.productId,productId]; arguments []; default message [productId],[Ljavax.validation.constraints.Pattern$Flag;@3641ef8a,P[1-9]+]; default message [Invalid product ID. It should start with character P followed by number.] Field error in object 'newProduct' on field 'name': rejected value []; codes [Size.newProduct.name,Size.name,Size.java.lang.String,Size]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [newProduct.name,name]; arguments []; default message [name],50,4]; default message [Invalid product name. It should be minimum 4 characters to maximum 50 characters long.] 2014-07-25 15:03:36 DEBUG DispatcherServlet:1012 - Null ModelAndView returned to DispatcherServlet with name 'DispatcherServlet': assuming HandlerAdapter completed request handling 2014-07-25 15:03:36 DEBUG DispatcherServlet:991 - Successfully completed request

share|improve this question

1 Answer 1

up vote 1 down vote accepted

In your WebMvcConfigurerAdapter you can override the getValidator() method to have it return your custom Validator.

With LocalValidatorFactoryBean, you can either call afterPropertiesSet() and getObject() to get the real Validator.

share|improve this answer
    
Sotirios, I've updated the code as you've suggested. Now, I'm getting a 400 status. If I remove the Valid annotation, I can submit, but the validation still doesn't work. I've also tried using Valid annotation as the first param. –  NuAlphaMan Jul 25 at 15:54
    
@NuAlphaMan Turn your logger level to debug. Spring will report why it returned a 400. –  Sotirios Delimanolis Jul 25 at 16:18
    
I've changed it to debug, and it's says I'm returning null which I don't get. In the controller, I'm returning the view name. –  NuAlphaMan Jul 25 at 19:10
    
Ok, I've solved the problem! Thanks @Sotirios for your suggestions! I will give you a check for that. To fix BindException, I found the answer here: forum.spring.io/forum/spring-projects/web/…. The BindingResult should follow the object your binding it to. –  NuAlphaMan Jul 26 at 1:04

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.