Handling JAX-RS and Bean Validation Errors with MVC

Since JAX-RS 2.0 you can use Bean Validation to validate inputs received from users and outputs created in your application. It’s a handy feature and in most cases it works great. But what if you’re using also MVC and you want to display custom error page if something goes wrong? Or what if you want to handle Bean Validation issues in a slightly different way than JAX-RS implementation you’re using does? This article will give you some answers to these questions.

Topics covered by this article:

Handling Error States with Jersey MVC

In addition to @Template annotation that has been present in Jersey MVC since 2.0 release a @ErrorTemplate annotation has been introduced in Jersey 2.3. The purpose of this annotation is to bind the model to an error view in case an exception has been raised during processing of a request. This is true for any exception thrown after the resource matching phase (i.e. this not only applies to JAX-RS resources but providers and even Jersey runtime as well). The model in this case is the thrown exception itself.

All code samples in this section are taken from shortener-webapp example that is available as sources or as a live demo.

The next snippet shows how to use @ErrorTemplate on a resource method. If all goes well with the method processing, then the /short-link template is used to as page sent to the user. Otherwise if an exception is raised then the /error-form template is shown to the user.

@POST
@Produces("text/html")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Template(name = "/short-link")
@ErrorTemplate(name = "/error-form")
public ShortenedLink createLink(@FormParam("link") final String link) {
    // ...
}

@ErrorTemplate can be used on a resource class or a resource method to merely handle error states. There is no need to use @Template or Viewable with it.

The annotation is handled by custom ExceptionMapper which creates an instance of Viewable that is further processed by Jersey. This exception mapper is registered automatically with any *MvcFeature.

Handling Bean Validation Errors with Jersey MVC

@ErrorTemplate can be used in also with Bean Validation to display specific error pages in case the validation of input/output values fails for some reason. Everything works as described above except the model is not the thrown exception but rather a list of ValidationErrors. This list can be iterated in the template and all the validation errors can be shown to the user in a desirable way.

Let’s add some validation annotations to our resource method from above:

@POST
@Produces("text/html")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Template(name = "/short-link")
@ErrorTemplate(name = "/error-form")
@Valid
public ShortenedLink createLink(@ShortenLink @FormParam("link") final String link) {
    // ...
}

In case the method gets an invalid form parameter (or creates invalid output for a user) we can iterate _ValidationError_s in /error-form as follows (Mustache templating engine):

<div>
    <div class="panel-heading">
        <h3 class="panel-title">Errors occurred during link shortening.</h3>
    </div>
    <div class="panel-body">
        {{#.}}
            {{message}} "<strong>{{invalidValue}}</strong>"<br/>
        {{/.}}
    </div>
</div>

Support for Bean Validation in Jersey MVC Templates is provided by a jersey-mvc-bean-validation extension module. Coordinates of this module are:

org.glassfish.jersey.ext:jersey-mvc-bean-validation:2.5.1

The JAX-RS Feature provided by this module (MvcBeanValidationFeature) has to be registered in order to use this functionality:

@ApplicationPath("/")
public class ShortenerApplication extends ResourceConfig {

    public ShortenerApplication() {
        // Resources.
        packages(ShortenerResource.class.getPackage().getName());

        // Features.
        register(MvcBeanValidationFeature.class);

        // Providers.
        register(LoggingFilter.class);
        register(MustacheMvcFeature.class);

        // Properties.
        property(MustacheMvcFeature.TEMPLATE_BASE_PATH, "/mustache");
    }
}

Custom Handling of Bean Validation Errors in JAX-RS 2.0

In case the approach described above doesn’t work for you for some reason (i.e. you have implemented your own MVC support or you’re using other JAX-RS 2.0 implementation) you need to follow these steps to achieve similar result as with @ErrorTemplate:

  1. (Jersey specific) add the dependencies on Bean Validation module (jersey-bean-validation) and, optionally, on one of the MVC modules providing support for custom templates (jersey-mvc-*)
  2. create an ExceptionMapper for ConstraintViolationException
  3. register all your providers

I’ll skip the first step as it’s easy enough and we’re going to take a look at creating custom ExceptionMapper and registering it.

Custom ExceptionMapper

Bean Validation runtime throws a ConstraintViolationException when something goes wrong during validating an incoming entity, request parameters or even JAX-RS resource. Every JAX-RS 2.0 implementation is required to provide a standard ExceptionMapper to handle this type of exceptions (ValidationException to be precise) and based on the context return correct HTTP status code (400 or 500). If you want to handle Bean Validation issues differently, you need to write an ExceptionMapper of your own:

@Provider
@Priority(Priorities.USER)
public class ConstraintViolationExceptionMapper
                 implements ExceptionMapper<ConstraintViolationException> {

    @Override
    public Response toResponse(final ConstraintViolationException exception) {
        return Response
                // Define your own status.
                .status(400)
                // Process given Exception and set an entity
                // i.e. Set an instance of Viewable to the response
                // so that Jersey MVC support can handle it.
                .entity(new Viewable("/error", exception))
                .build();
    }
}

With the ExceptionMapper above you’ll be handling all thrown _ConstraintViolationException_s and the final response will have HTTP 400 response status. Entity set (in our case Viewable) to the response will be processed by MessageBodyWriter from Jersey MVC module and it will basically output a processed web-page. The first parameter of the Viewable is path to a template (you can use relative or absolute path), the second is the model the MVC will use for rendering. For more on this topic, refer to the section about MVC in Jersey Users Guide.

Registering Providers

The last step you need to make is to register your providers into your Application (I’ll show you an example using ResourceConfig from Jersey which extends Application class):

new ResourceConfig()
    // Look for JAX-RS reosurces and providers.
    .package("my.package")
    // Register Jersey MVC Mustache processor.
    .register(MustacheMvcFeature.class)
    // Register custom ExceptionMapper (not needed as it's annotated with @Provider).
    .register(ConstraintViolationExceptionMapper.class)
    // Register Bean Validation (this is optional as BV is automatically registered
    // when jersey-bean-validation is on the class-path but it's good to know it's happening).
    .register(ValidationFeature.class);

More information about how different ways how to register providers and resources is available in this article.

Resources

Sample Application:

Further Reading