Handle Bean Validation Failure
This article shows two methods of handling bean validation failures, while sending the client a more informative response.
Join the DZone community and get the full member experience.
Join For FreeHow JAX-RS Handles Validation Failure
A bean validation failure results is a “400 Bad Request” response from the server. Let’s learn how to respond to the client with a more informative response.
Two Ways to Handle Failures
Two techniques to report data integrity violations to the client are
- Respond to the client with a list of problem fields in a custom key-value pair HTTP header, and
- Use the response’s body to encapsulate the error response.
Both techniques have their pros and cons. The choice you make will depend on your business model and clients requirements. In either case, you need to document it well, so that a front end developer knows how to use the error response.
Learn Bean Validation with JAX-RS.
Custom Key-Value Pair HTTP Header
Let’s look at the first technique.
I am going to present one way to handle the list of problem fields in the response body. As there is no accepted convention you are free to design your own structure.
I will use is a map of the property names and a message detailing what caused the validation failure.
Implement an Exception Manager
In both cases, we must implement an exception manager for the ConstraintViolationException. This exception contains a set of ConstraintViolation objects each representing the violation.
The Constraint Violation Exception
The ConstraintViolation instance contains a lot of useful information about the violation, such as the field name and the violation message. We can use this information to construct a response to the client. To do so, extract the field names and the error messages, turning the set into a stream, and collecting it to a map like so.
final Map<String, String> errorResponse =
exception.getConstraintViolations()
.stream()
.collect(Collectors.toMap(o -> o.getPropertyPath().toString(), o -> o.getMessage()));
Add Errors to Response Body
We then wrap the map in a custom POJO which has one field, a map. I call the custom POJO DataIntegrityValidation. Then add it to the response body in the same way we have done so before.
return Response
.status(Response.Status.BAD_REQUEST)
.entity(new DataIntegrityValidation(errorResponse))
.build();
Now the response HTTP body will contain a response similar to that which you see here.
{
"errorResponse": {
"entry": [{
"key": "description",
"value": "size must be between 100 and 2147483647"
},
{
"key": "published",
"value": "must be in the past"
},
{
"key": "link",
"value": "must match \"^(https?:\\/\\/)?([\\da-z\\.-]+)\\.([a-z\\.]{2,6})([\\/\\w \\.-]*)*\\/?$\""
}]
}
}
You will notice that there is a message for each bean violation. The Bean Validation API adds this for us, so we don’t have to.
However, we can customize this messages. Pass the custom message to the constraint annotations as metadata. It is possible to internationalize it so that the response appears in the client’s own language. This is beyond the scope of this article, but I encourage you to seek out examples of how to do this.
Encapsulate the Error Response
Let’s look at the second way to report violation issues to the client.
This way collects that same information as before and puts it in a custom HTTP header field. As before, extract violation data from the ConstraintViolationException and produce a comma-separated String of the errors.
final String message =
exception
.getConstraintViolations()
.stream()
.map(cv -> extractPropertyName(cv.getPropertyPath().toString()) + " : " + cv.getMessage())
.collect(Collectors.joining(", "));
We can then add this to the response:
return Response
.status(Response.Status.BAD_REQUEST)
.header("X-Validation-Failure", message)
.build();
Use a Custom Header
The response includes the custom header “X-Validation-Failure,” whose value is the message string. It is the convention to prefix custom headers with a capital X followed by a string that describes the headers purpose.
This then adds the following string to the HTTP header response:
link : must match "^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$",
published : must be in the past,
description : size must be between 100 and 2147483647
You will see that it contains the same data as the previous example.
What Next?
Lynda.com offers online video training for a wide range of Java EE technologies. The ideal course for someone just starting out in enterprise Java is the Learning Java Enterprise Edition presented by myself. The course is just over 2hrs and covers all the most important Java EE APIs including JAX-RS for RESTful APIs, JavaServerFaces, Enterprise Java Beans and much more.
Once you have completed this course you can dive deeper into the Java the Java EE APIs and take a course on how to build a RESTful API with JAX-RS, build a chat application with WebSocket API and handle JSON with Java EE's own JSON-Processing API. There are more courses coming so why not take a take a look and get ready to give your Java EE career a boost.
Further Reading
I blog regularly about Java EE on readlearncode.com and have just posted a series of articles examining the JAX-RS API in more detail and answering the question What is javax.ws.rs.core.context?. It is a five part series digging deeper into the multiple uses of the @Context annotation.
If you want to boost your knowledge of this technology, my articles on working with MediaTypes and JAX-RS and Resource Entities, examine this essential API in more detail.
Published at DZone with permission of Alex Theedom, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Trending
-
WireMock: The Ridiculously Easy Way (For Spring Microservices)
-
Simplifying SAP Data Integration With Google Cloud
-
Using DuckDB With CockroachDB
-
Enriching Kafka Applications With Contextual Data
Comments