In Java, how do I return meaningful, formatted JSON errors from a Reteasy check?

I have a RESTFul API that uses / returns JSON in the body of a request / response. When a client sends invalid data (valid JSON, but invalid values ​​for fields), I want to be able to return a JSON structure (as well as the corresponding 400 + code).

This structure will then allow the interface to analyze errors based on each field and display errors along with input fields.

eg. ideal output:

{ "errors":{ "name":["invalid chars","too long","etc"] "otherfield":["etc"] } } 

I use Resteasy for the API and using exceptions for exceptions, it's pretty easy to get it to handle JSON errors:

 @Provider @Component public class ValidationExceptionHandler implements ExceptionMapper<ResteasyViolationException> { public Response toResponse(ResteasyViolationException exception) { Multimap<String,String> errors = ArrayListMultimap.create(); Consumer<ResteasyConstraintViolation> consumer = (violation) -> { errors.put(violation.getPath(), violation.getMessage()); }; exception.getParameterViolations().forEach(consumer); Map<String, Map<String, Collection<String>>> top = new HashMap<>(); top.put("errors", errors.asMap()); return Response.status(Status.BAD_REQUEST).entity(top) .build(); } } 

However, error paths ( violation.getPath() ) have a centricity property, not an XmlElement-name-centric property.

eg. outputs:

 { "errors":{"createCampaign.arg1.name":["invalid chars","etc"]} } 

I tried to remove the index from the last point to get the "name", but there are other problems with this hack.

eg. if my "name" property is not a "name", it does not work:

 @XmlElement(name="name") @NotNull private String somethingelse; 

"somethingelse" will be returned to the client, but they do not know what it is:

 { "errors":{"somethingelse":["cannot be null"]} } 

The client wants a "name", as this is what was called up by the field when it was sent.

My resource:

 package com.foo.api; import org.springframework.stereotype.Service; import javax.validation.Valid; import javax.ws.rs.Consumes; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import com.foo.dto.CarDTO; @Service @Path("/car") public class CarResource { @POST @Produces({MediaType.APPLICATION_JSON}) @Consumes(MediaType.APPLICATION_JSON) public CarDTO create( @Valid CarDTO car ) { //do some persistence return car; } } 

Dto example:

 package com.foo.dto; import javax.validation.constraints.NotNull; import javax.validation.constraints.Min; import javax.validation.constraints.Max; import javax.xml.bind.annotation.XmlElement; public class CarDTO { @Min(1) @Max(10) @NotNull @XmlElement(name="gears") private int cogs; } 
+5
source share
2 answers

This article describes quite well what you need to do.

Basically you should implement ExceptionMapper .

 @Provider public class ValidationExceptionMapper implements ExceptionMapper<ValidationException> { @Override public Response toResponse(ValidationException exception) { Response myResponse; // build your Response based on the data provided by the exception return myResponse; } } 
+1
source

A custom error message can be used, so you don’t have to look for a path

 @NotNull(message="name cannot be null") 
0
source

Source: https://habr.com/ru/post/1208597/


All Articles