What is the best ERROR model for REST - spring

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.

Related

Spring MVC - Error handling with redirect if download not possible

in my Spring Boot application you can download some documents, by pressing a button. This leads to the following user experience: If the user presses the button, either a download is triggered or nothing happens.
Currently, the backend is either returning the bytes or 404:
#GetMapping("/download")
public ResponseEntity<byte[]> download() {
Optional<byte[]> data = service.getDocumentData();
return data.map(bytes -> new ResponseEntity<>(bytes, headers(), OK))
.orElseGet(() -> ResponseEntity.notFound().build());
}
Now we want to achieve, that the user is redirected to an error page, if no file can be downloaded.
With Spring MVC I would just return the error template like
public String notFound() {
return "error/404"; // the custom template
}
But now I need to mix two different concerns. Returning template or returning a ResponseEntity. For this i stumbled above the following answer, which uses Generics. However, I don't think its a good practice. So i thought about other ways.
My first idea was to use HTTP location header for redirecting on the frontend side, but I never used it before. Do you think this is a good way? Do you have other ideas?
Suggestion 1:
In the download() method, add HttpServletResponse response as the parameter and call response.sendRedirect("http://somewhere/404.html"); when the document requested is not found.
In addition, you can also change the status code in the response as response.setStatus(HttpServletResponse.SC_NOT_FOUND);
Suggestion 2:
Throw a custom exception, for e.g. FileNotFoundException and handle the exception in the #ExceptionHandler to return the error page in the ResponseEntity.
Refer - https://howtodoinjava.com/spring-core/spring-exceptionhandler-annotation/

How to map an input validation error to a specific error code in Spring

I am having a case in which I would like to do some input validation on the #RequestParams of an endpoint.
I know about Validators and Custom Validators, and my current strategy implies creating a wrapper object around the RequestParams, a custom validator and apply class level the annotation that triggers the custom validation.
My problem is that the custom validation is implementing ConstraintValidator, which means that the validator will either return true or false, and an error will be created by Spring with some text (I also know that I can change this text). My desire, however, is to create a custom payload back to the client. An example could be
class MyError {
int code;
String message;
}
The way to return this object is through a #ControllerAdvice Error handler, which understands that a ConstraintValidationException should return my custom payload format. However, I need to return different codes and messages for different reasons on the input validation failed. For example:
A Field is empty -> code XXX
A Field is formatted incorrectly -> code YYY
As far as I know, there is little customization possible on the exception that is reachable from my #ControllerAdvice, I can get a list of errors that happened but I cannot easily determine what happened. (Technically I can, but it would have to be based on the message string, which is pretty weak).
Is there a way to provide extra data to the Exception so I can distinguish from the #ControllerAdvice what happened and create my custom error response accordingly?
Am I approaching it the wrong way?
You can intercept the BindException (https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/validation/BindException.html) with the #ExceptionHandler. This contains detailed information about all validation errors. For example, with e.getFieldErrors() you can access all errors associated with fields.
For example, for a field
#MyConstraint
#Length(min = 3)
private String field;
that failed validation you get the following information in the exception:
Field error in object data on field field: rejected value [XY]; codes [Length.data.field,Length.field,Length.java.lang.String,Length].
Field error in object data on field field: rejected value [XY]; codes [MyConstraint.data.field,MyConstraint.field,MyConstraint.java.lang.String,MyConstraint].
From this you can see that it failed the #Length constraint and the custom #MyConstraint constraint.

Should all HTTP error responses have the same JSON structure?

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).

How return a Message (from Spring) to represent that the information was not found?

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.

Handle exception in rs client cxf

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

Resources