JAX-RS Bean Validation Error Message Internationalization

Introduction to Bean Validation

JavaBeans Validation (Bean Validation) is a new validation model available as part of Java EE 6 platform. The Bean Validation model is supported by constraints in the form of annotations placed on a field, method, or class of a JavaBeans component, such as a managed bean.

Several built-in constraints are available in the javax.validation.constraints package. The Java EE 6 Tutorial lists all the built-in constraints.

Bean Validation and RESTful web services

JAX-RS 1.0 provides great support for extracting request values and binding them into Java fields, properties and parameters using annotations such as @HeaderParam, @QueryParam, etc. It also supports binding of request entity bodies into Java objects via non-annotated parameters (i.e., parameters that are not annotated with any of the JAX-RS annotations). Currently, any additional validation on these values in a resource class must be performed programmatically.

The next release, JAX-RS 2.0, includes a proposal to enable validation annotations to be combined with JAX-RS annotations. For example, given the validation annotation @Pattern, the following example shows how form parameters could be validated.

However, at the moment, the only solution is to use a proprietary implementation. What is presented next is a solution based on the RESTEasy framework from JBoss that complies with the JAX-RS specification and adds a RESTful validation interface through the annotation @ValidateRequest.

The exported interface allows us to create our own implementation. However, there is already one widely used and to which RESTEasy also provides a seamless integration. This implementation is Hibernate Validator.

This provider can be added to the project through the following Maven dependencies:

After applying the annotation, the parameter id will be automatically validated when a request is made.
You can of course validate entire entities instead of single fields by using the annotation @Valid.
We could for example have one method that accepts a Person object and validates it.

Note:

By default, when validation fails an exception is thrown by the container and a HTTP 500 status is returned to the client. This default behavior can/should be overridden, allowing us to customize the Response that is returned to the client through exception mappers.

Internationalization

Until now we have been using the default or hard-coded error messages, but this is both a bad practice and not flexible at all. I18N is part of the Bean Validation specification and allows us to specify custom error messages using a resource property file. The default resource file name is ValidationMessages.properties and must include pairs of properties/values like:

person.id.pattern=The person id must be a valid number
person.name.size=The person name must be between {min} and {max} chars long

Note:{min}, {max} refer to properties of the constraint to which the message will be associated with.

Those defined messages can then be injected on the validation constraints as:

To provide translations to other languages, one must create a new ValidationMessages_XX.properties file with the translated messages, where XX is the code of the language being provided.

Unfortunately Hibernate Validator provider doesn’t supports I18N based on a specific HTTP request. It does not take Accept-Language HTTP header into account and always uses the default Locale as provided by Locale.getDefault(). To be able to change the Locale using the Accept-Language HTTP header, a custom implementation must be provided.

Custom validator provider

The code below intends to address this issue and has been tested with JBoss AS 7.1.

The first thing to do is to remove the Maven resteasy-hibernatevalidator-provider dependency, since we are providing our own provider, and add Hibernate Validator dependency:

Mapping Exceptions

The Bean Validation API reports error conditions using exceptions of type javax.validation.ValidationException or any of its subclasses. Applications can supply custom exception mapping providers for any exception. A JAX-RS implementation MUST always use the provider whose generic type is the nearest superclass of the exception, with application-defined providers taking precedence over built-in providers.

The above example shows the implementation of an ExceptionMapper that maps exceptions of type MethodConstraintViolationException. This exception is thrown by Hibernate Validator implementation when the validation of one or more parameters of a method annotated with the @ValidateRequest fails. This ensures that the client receives a formatted response instead of just the exception being propagated from the resource.

Source code

The source code used for this post is available on GitHub.Warn: make sure you change the resource property file name to have the file ValidationMessages.properties (i.e., without any suffix) to map to the Locale as returned by Locale.getDefault().

Author: Samuel Santos is a Java and Open Source evangelist and a JUG leader of PT.JUG, the Portuguese Java User Group. He is the Technical Lead at Present Technologies in Coimbra, Portugal, where he is responsible for stimulating innovation, knowledge sharing, coaching and technology choices activities. Samuel is the author of the blog samaxes.com and tweets as @samaxes.

Do you have any suggestions or recommendations how to handle this? I need so ‘save’ the locale not only for bean-validation but also vor generating internationalized error-messages (unfortunately the rest-client isn’t able to do this).

Join Us

With 1,240,600 monthly unique visitors and over 500 authors we are placed among the top Java related sites around. Constantly being on the lookout for partners; we encourage you to join us. So If you have a blog with unique and interesting content then you should check out our JCG partners program. You can also be a guest writer for Java Code Geeks and hone your writing skills!

Disclaimer

All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners. Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries. Examples Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.