How to use different error codes for validation messages? - spring

I have a Spring Boot REST server which should return specific error codes when invalid input is provided. I don't need any i18n here, just plain English error codes like 'missing' is sufficient.
Using Spring Boot with Hibernate Validator, after validation I get a back Spring Errors object.
For each error I can get the code and defaultMessage. For a #NotBlank constraint this would return NotBlank and may not be null resp.
Basically I want to translate this error to just missing as I'm not interested in i18n translation. Also other constraints I want to more REST friendly error codes.
I though to use use a simple messages.properties or ValidationMessages.properties inside src/main/resources but this wouldn't work unfortunately. Note I tried both adding a general NotBlank=missing and specific NotBlank.entity.field=missing properties.
I'm not sure why it's not working... maybe because resolving i18n messages (in jsp world) does not go directly via Spring Errors but through the MessageCodesResolver implementation (just guessing).
Probably I could get the error code from the Spring Error and do a lookup from the message code resolver.
But I wonder why error.getDefaultMessage does not return the appropriate value form the ValidationMessages.properties.
Any suggestion is welcome.

The default message is the message as stated by the programmer. In the case of those JSR-303 annotations probably the ones as Hibernate thought of them. The default message including the code is passed to the MessageSource.getMessage method, which contains a parameter defaultMessage
When you look at the Errors object or actually the ObjectError method you will see that it implements the MessageSourceResolvable interface. This means you can just pass the error, as is, to the MessageSource, including the Locale if you want.
#RequestMapping
public Object foo(#Valid #RequestBody MyObject obj, Errors errors, Locale locale) {
for (ObjectError err : errors.getAllErrors()) {
String msg = messageSource.getMessage(err, locale);
// Do something with msg
}
}
Something like the above should resolve the message. The advantage of using the ObjectError itself is that next to the code you also have the parameters (for #Range or #Min) which you can then use in your message with placeholders.

Related

SmartValidator - manually calling validate with groups

Spring 4.3.2
I need to call SmartValidator.validate() manually and I need it utilize the validation groups that I have defined on the target entity. The javadoc says this...
"This variant of validate() supports validation hints, such as
validation groups against a JSR-303 provider (in which case, the
provided hint objects need to be annotation arguments of type Class)."
void validate(Object target,
Errors errors,
Object... validationHints)
For some reason, I cannot find much information or examples on using "validationHints". So I have been trying things like the following...
validator.validate(targetEntity, errors, new Class[]{ValidationGroup1.class});
validator.validate(targetEntity, errors, ValidationGroup1.class);
So far, it just completely ignores my groupings. It always calls all validators. Any ideas?
Thanks!
===================================
Update: The javadoc also says this..
"Note: Validation hints may get ignored by the actual target
Validator, in which case this method should behave just like its
regular Validator.validate(Object, Errors) sibling."
This sounds like what's happening. But it doesn't give any clue as to why it might ignore it.
Alright then.
It seems the 'answer' is to not use Spring for this. Here is my workaround...
import javax.validation.Validator;
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
Set<ConstraintViolation> violations = validator.validate(targetEntity, new Class[]{group1.class, group2.class});
Then I convert Set to Spring FieldErrors (since everything is already configured to run Spring). Kind of a clusterf***, but at least it's working now.

Custom validation message

I have a problem with validating some data types.
There are int, short, DateTime and so on except string.
Suppose i have following property in my view model class
public int? LineNumber { get; set; }
When i input incorrect value i get mvc error "The value 'balblabl' is not valid for LineNumber."
But what if i want just out something like "Value incorrect"? Or what if i want to use other language? I have no idea how to do it(of course i can use string instead of int but it is painfull workaround)
i already tried dataannotationsextensions [DataAnnotationsExtensions.Integer(ErrorMessage = "Please enter a valid number.")] attribute. It is not working. I cannt to use custom validation attribute because of after binder convertation i get null value in all cases with incorrect value. I just cannt to do my own validation. I can to write my own binder but it looks like a joke. Really i think custom validation message is one of must have featerus and i cannt belive asp.net mvc doesnt has a simple way to do it.
I would like to add another, in my opinion, easy way to add and maintain custom error messages.
using the FluentValidation NuGet package.
It hooks up with mvc pretty easy have a look here
You can easily specify numerous rules for your models and display custom error messages that can use a resource file an example:
public class CreateProductCommandValidator : AbstractValidator<CreateProductCommand>
{
public CreateAgendaPointCommandValidator()
{
RuleFor(cmd => cmd.price)
.NotEmpty()
.WithMessage(Translations.CreateProduct_Price)
}
}
}
Documentation: FluentValidationDocumentation
Those errors are automatically added by the default model binder when it cannot parse the input string into its destination type. They are not like data annotations, where you can specify a resource instead of a harcoded string error message.
However you can use your own resource files, see this other question. Once you have created the resource file, you will update the global.asax for the default model binder to use it:
DefaultModelBinder.ResourceClassKey = "MyResources";
After a custom resource file has been set in the property ResourceClassKey of the default model binder, values will be resolved according to the following criteria (as per the MSDN):
If the property is not set, MVC uses the standard MVC resources.
If the property is set to an invalid class key (such as a resource
file that does not exist), MVC throws an exception.
If the property is set and the class key exists but the resource
names in the file do not exist in the application, MVC uses the
standard MVC resources.
If the property is set and the specified resources are available,
MVC uses the resources in the file.
If you need to know the key values for a particular message check this. For the message The value '{0}' is not valid for {1}., you will need to add a value with the key DefaultModelBinder_ValueInvalid

Using Spring's MessageSource for setting FieldError default messages

After my form backing object is validated I have a BindingResult, which contains a list of FieldError. Each FieldError has a defaultMessage. How is that message set and why doesn't it use my Spring MessageSource? I would like that default message to be derived from my Spring's MessageSource.
EDIT:
I see that the error codes are being set correctly in the FieldError object. It's just the default message in that object is not coming from my MessageSource. For instance, when I enter a string for a field that is an int I want it to get my message from messages.properties:
typeMismatch=Invalid type was entered.
The only way I can get that message is if I take my FieldError object and pass it into the MessageSource manually like so:
messageSource.getMessage(fieldError, null); // This gets my message from messages.properties.
If you're using a Validator, you can specify the keys for the messages in the MessageSource in the Validator implementing class, usually using ValidationUtils methods. Section 6.2 of the Spring documentation has a good example.
Spring will also try to resolve error codes by convention if you're using something other than a Validator like JSR-303 Bean Validation.
Let's say you had a form backing object called 'Address' with an int field called 'zipcode.' If the user entered a string for the zipcode field, by default Spring will use the DefaultMessageCodesResolver and look in the MessageSource for a key called 'typeMismatch.address.zipcode.' If it doesn't find that key, it will try 'typeMismatch.zipcode,' then 'typeMismatch.int,' then 'typeMismatch.'
Or, you can implement your own MessageCodesResolver.

How to internationalize spring-mvc form error messages?

I want to translate the default error messages for form validation, for example, when I annotate a field as #NotEmpty, I should receive an error like:
may not be empty
when I left the field empty. Now I want this message in other languages, so what should I do in order to translate them? I want this to work with every field annotated with #NotEmpty, and also for other messages...
Thanks!
Spring can hook up into your internationalized error codes I think ...
My validator class extends Validator and implements setting a MessageSource and then can pass in errorCode like below and Im guessing it autolooks it up via the MessageSource set (which i set via a Spring Bean):
public static void rejectIfEmptyOrWhitespace(Errors errors,
String field,
String errorCode,
String defaultMessage)
http://docs.spring.io/spring-framework/docs/4.2.x/javadoc-api/org/springframework/validation/ValidationUtils.html#rejectIfEmptyOrWhitespace-org.springframework.validation.Errors-java.lang.String-java.lang.String-java.lang.String-
Here is a pretty close example to what I have (minus they use LocaleResolver):
http://vardhan-java2java.blogspot.com/2010/08/spring-internationalization-i18n.html
You can add custom messages to #NotEmpty by using it like this:
#NotEmpty( message = "Your error message")

Spring - how to handle exception

I am using Spring with Velocity. At times velocity produces an exception like
Error in interpolating string literal : org.apache.velocity.exception.MethodInvocationException: Invocation of method 'getMessage' in class org.springframework.web.servlet.support.RequestContext threw exception class org.springframework.context.NoSuchMessageException : No message found under code 'XX' for locale 'en_US'.
Question is - can I instruct spring to suppress NoSuchMessageException ? I am pretty new to Spring so do not know if I can create a exception handler which will not throw a exception if the message is not found? In my use case, it is a valid use case of not finding some of the messages in the messages.properties file.
[EDIT] - I found a way org.apache.velocity.app.event.MethodExceptionEventHandler to supply an even handler to velocity. I am however not sure how to register it with Spring.
It would be better, I think, to address the problem directly, rather than trying to suppress the exception, which could lead to avoid behaviour and uncompleted operations.
However, you didn't tell us how you'd want the system to respond in cases where the message is not defined for a given code. Do you want a blank String, or should the code itself be used as the default message?
I'm assuming that somewhere in your Spring context you have a defined a messageSource bean of some sort. These MessageSource beans usually have a property useCodeAsDefaultMessage. This defaults to false, but when set to true they will return the code itself as the message, in cases where no message is defined.
Alternatively, you can subclass the MessageSource class (whichever one you're using), and override the getDefaultMessage() method to handle cases where the code cannot be resolved.

Resources