How to map an input validation error to a specific error code in Spring - 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.

Related

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.

Add a global error in Spring MVC from a non-controller location

Somewhere in my Spring MVC app I need to add a global error. I no longer have access to any BindingResult, I only have my Request. My error is not the result of any binding issues, it's a global condition. Then I need to attach my new Errors object to the "errors" Req. attribute, so the JSP can display it.
But how to do this? I can't do
Errors errors = new Errors();
errors.reject(....);
request.setAttribute("errors", errors);
because Errors is an interface. The implementations all have to do with 'Binding',like BindException, AbstractBindingResult, MapBindingResult, etc. These all have nothing to do with my case, I don't have a binding field error, it's a general error msg.
Any tips?
Our resolution to this problem: The proper way to handle EXCEPTIONS (rather than VALIDATION errors) is to keep track of our own, custom Request Attribute that we display ourselves in our error area on the JSP.
BindingResult/Errors/etc. are all Binding-related, we can't hack it to handle general exception errors.
We just add a custom Request Attr. called "exceptions" and then our Error section checks for this additonal attribute list to display.
SpringMVC doesn't provide its own 'generic' Error collection.

What is the best ERROR model for REST

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.

Change Struts Validator default messages

I am new in struts2 . I have created an XML file for validations, but when I test my form I don't get the error messages that I configured in the XML file. instead I get the Struts 2 defaults messages such as this one :
invalid field value for field "capteur.ENERGIE_CAPTEUR".
Is there anyway to make struts2 prints the messages configured in the XML file instead of the default ones ?
That is not a validation error message, it is a conversion error message.
You can override the default conversion error message up to each single object, by creating an entry for it in the global .properties file, as described in Struts 2 documentation, Type Conversion Errors Handling:
By default, all conversion errors are reported using the generic i18n
key xwork.default.invalid.fieldvalue, which you can override (the
default text is Invalid field value for field "xxx", where xxx is the
field name) in your global i18n resource bundle.
However, sometimes you may wish to override this message on a
per-field basis. You can do this by adding an i18n key associated with
just your action (Action.properties) using the pattern
invalid.fieldvalue.xxx, where xxx is the field name.
If you are interested in understanding how it works in a deeper way, read the Short Story about Validation, Conversion and Friends.

Difference between an error code and a message code as far as Spring validation is concerned?

I am in reference to the following method from BindingResult:
BindingResult.html#resolveMessageCodes(java.lang.String, java.lang.String)
I am trying to figure out the difference between an error code and a message code. Can someone please provide an example, especially one that would illustrate why there could be several message codes for a given error code?
Because web applications are internationalized, when you reject an object and want to have a message displayed for it, you don't use a hardcoded text because that will show the same no matter the language.
Instead, you specify an error code that later server as a key to retrieving the proper message from the bundles (and now the error code is a message code from the point of view of the method that must find the proper message text).
Your error code resolves to more message codes because Spring (based on the implementation) adds some additional ones for you. Here is a snippet from the Spring documentation:
[...] What error codes it registers is determined by the MessageCodesResolver that is used. By default, the DefaultMessageCodesResolver is used, which for example not only registers a message with the code you gave, but also messages that include the field name you passed to the reject method. So in case you reject a field using rejectValue("age", "too.darn.old"), apart from the too.darn.old code, Spring will also register too.darn.old.age and too.darn.old.age.int (so the first will include the field name and the second will include the type of the field); this is done as a convenience to aid developers in targeting error messages and suchlike. [...]
The last statement is the reason there are more message codes, to have control on the message that is displayed to the user, from a generic one (e.g. "Value required") to a more specific one given the context (e.g. "A value is required for field XXX").
The javadoc for DefaultMessageCodesResolver explains it further and gives an example:
For example, in case of code "typeMismatch", object name "user", field "age":
try "typeMismatch.user.age"
try "typeMismatch.age"
try "typeMismatch.int"
try "typeMismatch"
This resolution algorithm thus can be leveraged for example to show specific messages for binding errors like "required" and "typeMismatch":
at the object + field level ("age" field, but only on "user");
at the field level (all "age" fields, no matter which object name);
or at the general level (all fields, on any object).

Resources