Validating Spring REST controllers’ beans using the Bean Validation API… and writing the tests for it!

The other day I was working on a feature that required data to be submitted to a REST service. Even though the data is validated on the client-side, it is a good practice to also perform server-side validation. I have done that thousands of time on Spring MVC using the Bean Validation API… But can Spring handle @Valid annotations outside of MVC?

Yes it can!

Since version 3.1, Spring can actually validate a @RequestBody-annotated controller method argument in a similar fashion to what you would validate @ModelAttribute-annotated arguments in a Spring MVC application.

Let’s take the following bean as an example:

The bean’s property is annotated with @NotEmpty: a very useful annotation provided by Hibernate Validator‘s reference implementation of the Bean Validation API 1.1 (or JSR-349).

Now let’s imagine the following REST service implementation:

Here we want to save a Movie whose JSON representation we receive when the service is invoked. But we want to make sure that it’s name is not empty, which is why we annotated the bean’s property above using @NotEmpty.

So when the service is invoked, Spring builds the Movie bean using its JSON representation, courtesy of MappingJackson2HttpMessageConverter (if Jackson is present on the class path!).

Then Spring triggers the validation of the reconstructed Movie bean, but only if it can find a JSR-303/JSR-349 provider such as Hibernate Validator on its class path.

My data is invalid… now what?

Let’s say the submitted movie data is missing the movie name. How does Spring inform you of the detected validation errors?

You can add a BindingResult argument right after the bean argument to be validated, exactly the same way you would do for model attributes in Spring MVC. The object is filled with the validation errors’ details and consequently we enter the method’s body, where you can then test the BindingResult object for errors and react accordingly.

But this also means that you have to test for validation errors inside every method whose argument needs to be validated. In other words: “with great power comes great… code duplication“. ‘Nuff said!

The other option is to let the validation process throw an exception, more specifically a MethodArgumentNotValidException. This way you can implement an exception handler for that specific exception and react accordingly. This is the typical REST service situation: when a validation issue is detected, the service sends back a 400 Bad Request HTTP code.

Here’s an example implementation of this:

Every time a validation exception is raised, this bit of code kicks in and ensures you get back a decent error response.

Have you noticed that you can access the whole BindingResult object from that exception? The code above is very simplistic and only sends back the first error in the list… But since you have access to that BindingResult object, you can imagine wrapping all the validation errors into a bean and send that back using a ResponseEntity. The client will then receive a JSON that lists of all the validation errors detected at the server-side.

Isn’t that neat?

Testing my validated REST service using Spring Boot

If we really want to test this right, we probably need to submit a request to the REST service as if we were the client. Then we can check if the validation process kicked in or not by looking at what response comes out.

Intuitively, I’d like to run a test suite that would start my application programmatically and run the tests against that application by considering it as a “black-box“: send my REST request and see what comes back.

By writing our code on top of Spring Boot we are able to embed a Tomcat or Jetty server and programmatically run our application on it! Let’s try that out.

We start by setting up our pom.xml as follows:

Pay attention to that spring-boot-starter-data-jpa dependency: it so happens that I’m using JPA for my project, and that also sets a dependency towards Hibernate Validator. That’s good, because Spring needs to have an implementation of the Bean Validation Framework in the class path for validation to work. If you are not using JPA in your project, make sure you add a dependency to Hibernate Validator or to any other valid JSR-303/JSR-349 provider!

We define our Spring Boot application using Java Configuration like this:

An important note here: when using XML we would normally add an <mvc:annotation-driven> element to our configuration file in order for Spring to take our @Valid annotations into account. To do the same with Java Configuration, we would use @EnableWebMvc. However if we rely on Spring Boot‘s @EnableAutoConfiguration this is taken care of already!

We now move to our test, which could look like:

Don’t worry about that TestRestTemplate class: it’s a nice convenience class you get with Spring Boot.

If you run the test now you’ll get… most likely a ResourceAccessException! And that’s normal, because we’re trying to connect to a server (in fact our application) which is not running. So how do we make it start?

One solution is to extend these tests with a special class who starts our application. We should then have a running Jetty server (thanks to Spring Boot‘s starter POM!) on http://localhost:8080 to which we can send the requests to test:

The static code will run only once per test suite execution. It stores in a static field the application context returned by Spring Boot when running our SpringApplication. The application context is indeed required later on by the shutdown hook we register just below, so that we can close our programmatically-started application. Strictly speaking this last step is not mandatory, but I found it cleaner this way.

Oh, and let’s not forget to extend our test class:

So now if we run our test we should see our beloved Spring Boot logo appear in the logs, initiating our application on its embedded Jetty server, then the test would send the request to it and hopefully obtain the right behavior: a validation error!

And we’re still in control

This is slightly off-topic, but anyway: now what if you want to access your programmatically-started application “from the inside“, for example to setup and clean the database for your acceptance tests?

Thankfully, Spring Boot allows you to access your application’s beans through the application context that is returned by its run() method. So you could imagine writing something like:

The advantage is of course that you haven’t exposed that code through the official API: this database cleanup code is only accessible by your acceptance test, not by your application users. I can only imagine what would happen if that wasn’t the case…

That’s all for this time

Spring keeps on surprising me at times: I only recently discovered the possibility of leveraging @Valid to trigger the validation of my REST methods’ arguments, and that’s an absolute plus.

Combined with the simplicity with which, using Spring Boot, I can start my application on a Jetty server to black-box-test the validation process, I’d say it’s a winning combination for developing strong, reliable (and validated) REST services.

Any remarks? Spotted an error somewhere? Want to suggest an improvement? By all means, feel free to drop me a comment here below.

Cheers!

 

10 comments

  1. Tajbeer Singh Rawat says:

    what if you have write your custom message for @NotEmpty .I have tried messages.properties for Spring boot rest way but does not work.however it works for Spring MVC. Please let me know if you have a solution

    • Hey there, I’ll be honest with you: I haven’t tried using custom messages for validation annotations for Spring Boot.
      If it works on Spring MVC, then it’s probably Spring Boot trying to handle the static resources automatically and not finding your messages.properties file…
      This should definitely be the topic of one of my future posts.
      I’m sorry I don’t have a solution for you at the moment, but I’ll try to come up with one in a near future 🙁

    • Mannu says:

      Hi Tajbeer, I am also trying to implement the same for custom annotations, I want to fetch error messages from message.properties file for Spring MVC. can you share your implementation ?

    • Mannu says:

      Hi Tajbeer,
      can you share your solution ? I tried message.properties for Spring MVC, but its not working.

  2. Kalyan says:

    WebServiceError webServiceError = WebServiceError.build(WebServiceError.Type.VALIDATION_ERROR, errors.get(0).getObjectName() + ” ” + errors.get(0).getDefaultMessage());

    can you please give details of class WebServiceError

    • Yes of course. It’s merely a java bean modeling the JSON to be sent back when an error occurs. It looks like this:

  3. Joel says:

    What if you want to require Movie.name during a POST but have it be an optional field during a PATCH?

    • Hi Joel,
      It all depends on what you are trying to achieve.
      You will probably end up having two Java methods in your Spring controller: one that POSTs and another that PATCHes. You could disable validation on the PATCH method by omitting the @Valid annotation on Movie. But then of course all the validation by annotation on that Movie object would be disabled…
      The simplest next thing I can think of would be to write two Movie data objects: remember, in an ideal word we use data objects in a controller, and once validated we convert them to matching “entities” (usually with Hibernate persistence annotations or whatnot). We could take advantage of that by implementing MovieForPost and MovieForPatch data objects. In one you make the movie name mandatory, and in the other one not. To keep it clean you could put common properties in a MovieCommon object and have MovieForPost and MovieForPatch extend that one…

  4. Neha says:

    Hello All,

    How can I download this whole source code? Please share GIT link etc?

    Thanks,
    Neha

    • Hello Neha,
      No accompanying code for that blog post, I’m afraid! I usually write something on GitHub when discussing more complex matters…
      Good news though: most of the background material discussed in the post, like creating REST web services with Spring or validating entities, can be found on the spring.io website. I merely explain that combining a REST controller and entity validation works!
      Cheers!

Comments are closed.