We are developing a REST service with Spring Boot and got stuck wondering if every error response should have the same JSON structure?
For error cases our service responds with a simple JSON format. For example, if a parameter is malformed we respond with HTTP status 400 and a JSON:
{
"errorCode": 05,
"message": "provided paramter XY is malformed"
}
The errorCode is our custom code id. One may argue whether this design is good or not, but it is simple and can be easily handled by the service consumer.
Now, Spring Boot creates some error responses automatically. For example, for a TypeMismatchException and response with HTTP status 400 is created. But of course, these automatically generated responses do not have the error format.
So... we have the situation that the service consumer does a prior not know for a HTTP status 400 whether it has the simple JSON error format in the body or not. Should we really overwrite all Spring Boot default exception handling to put our format in each response or should the service consumer swallow the bitter pill and identify if the simple JSON format is used or not?
It depends on the scale of your project. If your API is used by many applications you should go for the "catch everything and use the JSON format"-approach. Yes you would have more to do but when every other application in your company can use your standard way, they can save a lot of time.
In most of the projects where i was involved, we also had a "standard-way" to give our error-responses back (also JSON):
#RestControllerAdvice
public class GlobalResourceExceptionHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(GlobalResourceExceptionHandler.class);
// the class ValidationError contains the properties the json should contain.
#ExceptionHandler(Exception.class)
public List<ValidationError> exceptionHandler(Exception e, HttpServletResponse response) {
LOGGER.warn("Exception thrown in a resource", e);
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
return Collections.singletonList(new ValidationError(null, "unexpected exception"));
}
}
You can extend the class with more #ExceptionHandlers.
It turned out that this is a pretty good way because it is easy to implement (also for small applications) and it covers a lot. A lot means basically everything which had to do with a rest-request. Excluded from this was a resource-handler, which provided an angular-app and the security-layer.
UPDATE:
Conclusion: Catch everything when you have an api used by many applications. You should use the way shown above to start your error-handling in both cases (small or big app).
Related
We are building a RESTful API with several #RestControllers which return all kinds of objects including byte arrays (actually application/pdf).
When an exception occurs we handle those with #RestControllerAdvices which return a custom ErrorView object. Still spring insists of rendering those as application/pdf which of course is not possible. The client is sending application/json in the accept header but this does not seem to help. Any pointers how to fix this?
Ok, I made a mistake which I think I should share.
Basically most of the time the content type negotiation between Spring and the client works like a charm. If the client accepts (as is the case for our app) "application/pdf, application/json" then spring will try to work it our for all responses AKA return values from #RestController functions. Except, of course, when you explicitly tell Spring to only produce a certain type of response e.g. with
#GetMapping(value = "render/document/{docId}", produces = arrayOf(MediaType.APPLICATION_PDF_VALUE))
After changing this to
#GetMapping(value = "render/document/{docId}", produces = arrayOf(MediaType.APPLICATION_PDF_VALUE, MediaType.APPLICATION_JSON_VALUE))
the ErrorView gets rendered as JSON as intended.
I'm working with messaging on Spring and I had a simple question.
When another services sends a message requesting an information that does not exists for the service that are able to answer, the first thing that I thoutght was pass a "null" do the payload:
MyResponse myResponse = service.find(id); //ops, the information with this id does not exists
Message<MyResponse> message = MessageBuilder
.withPayload(myResponse) // the information does not exists, so null
.copyHeadersIfAbsent(request.getHeaders())
.build();
But the method withPayload not accept null. So, what is the good practice or alternative to fill this message with a "empty" value to the original request receive the result and know that this information does not exists?
For now I'm passing a empty object (new MyResponse()) to the payload, but this could create a confusion for who consumes the message. I could create another class to represent this "not exists" state, but I'm trying to understand my options now.
Thanks!
The null payload doesn't bring too much information and isn't supported by many network protocols. More over there are many places in the framework which are based on the payload type, but if it is a null we might not have any information what and how to do with it. In some components the null return value is a signal to stop the flow and don't produce any messages downstream to reply.
The solution you may go is like constant object (MyNullResponse) to indicate that it is about a null.
You might also consider a way with an exception instead of an attempt to return null. Let's consider that you do some post-search processing and a bunch of filtering and conversion steps. In case of null your message will still travel all the flow down. But when we deal with an exception (like it is with the javax.persistence.EntityNotFoundException) we just bubble the problem to end-user immediately. And that's already the target service responsibility to represent that exception as a comprehensible message for end-user.
We have a JIRA ticket about null payload support. You can read there about more reasons and what other people think on the matter. My idea to allow something on the matter is like Optional.empty(). Then we can map it to null easily on the target end-user code.
You must clearly differentiate between The response itself ( in your case MyResponse object) and the existence or not of the information which something relative to you business logic, the message that you construct must be as generic as possible not hard coupled to your service layer, simple reason => the message is just a message you send to consumers , so if possible try to embed the existence or not of the information in your object MyResponse (Boolean Flag) , and construct it on the fly after invoking your service
instead of
MyResponse myResponse = service.find(id);
you can try this :
CustomResponse myResponse = service.find(id);
// use helper class to respect DRY principal if you need it somewhere
MyResponse messageReponse = ResponseBuilder.constructResponse(myReponse);
Message<MyResponse> message =// .. construct you message
In the example above ResponseBuilder take care of myResponse if it null, and fully create the response ( you could integrate all cases .. )
I would like to share with you guys my solution after read the #Artem answer.
I created an OptionalMessage class, very similar of Optional class from Java 8+. As I'm using application/json as content-type for messages.
I can use this OptionalMessage in different messages:
OptionalMessage optionalMessage = messaging.find(id);
if (optionalMessage.isPresent()) {
MyMessage myMessage = optionalMessage.getContent();
}
It has also the methods of() and empty(), used in the another side to populate the OptionalMessage.
The Json structure generated follow this example:
{
"content": { /*attributes */}
}
When we have no content (my "null" return), the Json look like this:
{
"content": null
}
I tried to use generics (OptionalMessage<MyMessage>), but I would need to change my Jackson setup (on IntegrationFlow DSL) to not receive the error java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to MyMessage when calling the getContent method.
I tried to search for this but I could not find a solution anywhere. What I really want to do is that I have a rest service implemented on the cloud (3rd party). From my application I just have a rest client implemented (CXF 2.7.2) which calls this service on the cloud and does some processing before returing the response to the client (primarily native mobile app). So basically it acts as a middleware.
Bascically, I want ot implement a generic exception handling at the middleware (rs-client).
The service implementation (3rd party) is kind of a black box for me and I have no access to their code. When I play with the service using the client, I somtimes get response 400 with a proper message (I finally get a javax.ws.rs.BadRequestException with a message e.g. the input is not valid; and sometimes in certain cases I get a null as a response.
What I want to do is handle all possible (all that I can think of anyway) such cases of responses / codes and convert them to a custom exception. Alongwith this, I also want to handle other cases like timeout etc and convert these also to a custom exception and return it to let the user do what they want to with this exception. Also, log the error along the way.
I saw at various places maybe to handle it using handlemessage but I am not able to arrive at a full implementation.
Your question can have multiple ways for writing right answers. But to help you in first step I provide you a partly answer where we can achieve more to get your problem solved. But please provide more information in your question.
If I understood your question in a right way you can do the following:
For the first part of your question:
What I want to do is handle all possible (all that I can think of anyway) such cases of responses / codes and convert them to a custom exception
If your response code is not a HTTP 200 status code you can catch this and throw a custom exception. After your know its not a HTTP 200 status code its your choice how you want to handle the status code. You can:
just throw a custom exception with a default message or
you can lookup the status code and throw a custom exception with a
specific custom message based on the status code.
Simple code snippet:
if(yourResponse.getStatus() != Response.Status.OK.getStatusCode()){
throw new YourCustomException(yourResponse.getStatus() + " Your message");
}
Your custom Exception Class:
public class YourCustomException extends Exception {
private static final long serialVersionUID = 5724760031556064083L;
public YourCustomException(){
super();
}
public YourCustomException(String message){
super(message);
}
public YourCustomException(String message, Throwable cause){
super(message, cause);
}
public YourCustomException(Throwable cause){
super(cause);
}
}
You should give Retrofit a try, easy to use and light-weight REST client library which has exactly the error handling capabilities you are looking for.
Here is an example for the error handling with the 1.x version https://gist.github.com/benvium/66bf24e0de80d609dac0
If instead you are stuck with JAX-RS/CXF then you need to get a hold of the response object and do your checks there.
http://cxf.apache.org/docs/jax-rs-client-api.html#JAX-RSClientAPI-Handlingexceptions.1
Is there a standard error message model for REST services?
I'm building a REST service, with Spring REST backend, and Backbone JS based frontend. On all projects, that I was there were some home made error messages mechanism, with home made client interpretation of those errors. This usually had a limited scope and use for this specific project.
Now in my current project I came to a point, where I want to introduce field errors for invalid JSON fields in POST requests. So I need to extend my model to support this.
From my point of view, it's yet another bicycle, since field validation is pretty standard requirement our days, so I wonder, if there is already an Error model, that I could reuse in my project.
I'm well familiar with HandlerExceptionResolver in Spring, and #Valid annotation in Java. In fact I'm using them allot. This is more of an architecture kind Question, what is the best model to communication this kind of errors to an independent JS client.
What I usually do is create a ValidationError model. The model is returned to the client as JSON in the case of a validation error, along with an HTTP status code, and it contains everything the client needs to know about the error.
The specifics of the model varies, but something like this would be a good start:
public class ValidationError {
private int code;
private String message;
private Map<String, String> fields;
/*...*/
}
Now when an invalid form is submitted, the JSON response will be something like:
{
"code": 400,
"message": "invalid form value(s)",
"fields": {
"username": "the username already exists",
"password": "password must be more than 6 characters"
}
}
This ties up nicely with Spring's #ExceptionHandler. When you get something like a BindException, all you have to do is:
#ExceptionHandler(BindException.class)
#ResponseBody
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
private ValidationError handleBindException(BindException ex) {
ValidationError validationError = new ValidationError();
/* set code and message */
for (FieldError error : ex.getFieldErrors()) {
/* set fields' errors */
}
return validationError;
}
BindException makes it easy to get the fields' errors. More work is needed to dig up the errors from exceptions like JsonMappingException.
Presumably the best approach is validation both on the server and on the client. I'll limit my advice to the client, as I'm not familiar with Spring: Backbone does have a validate method for use with your Backbone models. The Backbone docs encourage you to override this method with your own validation logic. validate gets called by default whenever you do a backbone model save (i.e. POST), and you can also trigger the invalid event whenever you want to run the validate code (for example, after a key-up, before the user clicks the submit button).
If you're not keen on writing a lot of custom front-end validation, there are some plugins available, for example backbone.validation.
I'm using Jersey's integrated Jackson processing to transform incoming JSON to a POJO, e.g.:
#POST
#Consumes(MediaType.APPLICATION_JSON)
public Response newCustomer( CustomerRepresentation customer)
{
...
}
If a client sends JSON with invalid fields Jersey currently returns a 500 Internal Server Error. Instead, I'd like to return a 400 Bad Request, preferably with some meaningful detail indicating which fields are in error.
Any insight into how this could be accomplished? (At least returning a generic 400 instead of the completely inappropriate 500?)
Update:
Here's the exception being generated server-side, before my handler is invoked:
javax.servlet.ServletException: org.codehaus.jackson.map.exc.UnrecognizedPropertyException:
Unrecognized field "this_isnt_a_known"_field" (Class com.redacted....), not marked as ignorable
I was finally able to work around this problem by implementing an ExceptionMapper to catch the UnrecognizedPropertyException thrown by Jackson and map it to a 400 Bad Request response:
#Provider
public class UnrecognizedPropertyExceptionMapper implements ExceptionMapper<UnrecognizedPropertyException>
{
#Override
public Response toResponse(UnrecognizedPropertyException exception)
{
return Response
.status(Response.Status.BAD_REQUEST)
.entity( "'" + exception.getUnrecognizedPropertyName() + "' is an unrecognized field.")
.type( MediaType.TEXT_PLAIN)
.build();
}
}
I tried mapping status 500 to status 400 with HolySamosa's answer but the exception was not caught by this mapper, and status 500 was still being returned.
After debugging I found that JsonParseException is being thrown and not UnrecognizedPropertyException. This is because I was sending some garbage text (that was not JSON at all).
When I sent a proper JSON from client side, with format that was not appropriate for my DTO on the server side, then I got UnrecognizedPropertyException.
So there are two cases for this:
when you send garbage that is not JSON and
when you send JSON, but it is not a match for your DTO class.
Now I am returning status 400 for both.
In dropwizard land there is an ExceptionMapper called JsonProcessingExceptionMapper that has similar functionality as to what you are looking for. Maybe you can use that for inspiration on how to address your specific issue in a non-dropwizard world.
I've had this same problem... Unfortunately, there's no good way that I know of to intercept the Jackson exception and generate your own error code.
One option you have is to use #JsonIgnoreProperties and then strictly validate the deserialized object. This won't tell you if your sender transmitted junk, but if they missed required fields, you'll catch that.
I cannot find any way to access the actual JSON passed in, other than creating an #Provider class to trap the JSON, validate it, then pass it to Jackson for deserialization.