Using Spring's MessageSource for setting FieldError default messages - spring

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.

Related

Spring jms listener overwrites id header when converting to Message<Object>

I have a jms message with an attribute named id in the header.
In my listener when this jms message is converted to Message
#JmsListener(id = "cis-listener", destination = "${amiga.service.jms.cis-listener.destination-fqdn}",
containerFactory = "containerFactoryListener")
public void receiveMessage(Message<Object> event) throws UnknownMessageException {
...
this attribute is overwrited with a generated UUID, when the MessageHeaders are instantiated:
protected MessageHeaders(#Nullable Map<String, Object> headers, #Nullable UUID id, #Nullable Long timestamp) {
this.headers = (headers != null ? new HashMap<>(headers) : new HashMap<>());
if (id == null) {
this.headers.put(ID, getIdGenerator().generateId());
}
...
Is there some option to rename the id header attribute i receive in the message to keep the value?
I can access to this attribute by changing the object received by the listener, using the javax.jms.Message but i prefer to use the spring-jms Message implementation.
You can add a custom JmsHeaderMapper to the listener adapter.
set the container factory autoStartup to false.
get the container from the JmdListenerContainerEndpointRegistry (by id).
get the message listener from the container and cast it to AbstractAdaptableMessageListener.
set the custom header mapper.
start the container.
Your mapper should map your external id header to something else. id is reserved in spring-messaging. It can be a subclass of SimpleJmsHeaderMapper.
Hello Gary Russell first of all thank you for your responses and your help, it's a very big pleasure to be answered by one of the developers of the part of spring i'm having doubts.
In your solution i don't like to be forced to disable the autoStartup. In our project we don't force the autoStartup but if we need to this property will be ignored because we force the start after the custom header mapper is set. We should to define other property and check its value before the container start. It's a little bit odd to change the meaning of the container autostartup property.
I'm also having doubts about the MessageHeader definition. I consider the Message and MessageHeader classes as wrappers / decorators of the jms Message class. I understand that those two classes are defined to make easier the instantiation and access to message and header properties.
That's why i don't get why the timestamp and id values are overwriten. I think that a wrapper shouldn't add this kind of logic, especially when is not so rare to receive an id attribute in the header of the message. If spring needs an id to work with the message maybe, before the creation, should check if an attribute id exists and use other name instead id, the same with timestamp.
Those two attribute names are reserved by spring but the jms deffinition doesn't disallow its use.
I don't like the solution but i think that we will be forced to change our listener to receive a jms message, even though it's more difficult to instantiate the messages to testing it, I think it is a better option.
Thank you again for your time and your help.

Missing Converter when using Spring LdapTemplate with Grails Validateable annotation

I'm using the Spring LDAP (docs) library in a Grails application. I have a class annotated with the #Entry annotation, so it is mapped to an LDAP server. This all works quite beautifully.
However, when I add the Grails #Validateable annotation (to enable validating the LDAP class similarly to Grails domain classes) and attempt to retrieve data from LDAP (i.e. a findAll operation on the LdapUserRepo, or similar), I get the following exception:
Message: Missing converter from class java.lang.String to interface org.springframework.validation.Errors, this is needed for field errors on Entry class com.ldap.portal.LdapUser
Basically, it seems like the AST transformation performed by the #Validateable annotation is producing extra fields (namely the errors field) on the LdapUser object. It appears that Spring LDAP, in processing the #Entry logic, assumes a default mapping for the fields property (probably interpreting it as a string field on the LDAP object). When it gets nothing from the LDAP server, it attempts to set the field of type ValidationErrors to a value of type String -- an empty string.
I did some looking in github and found this code that seems relevant and may support my theory.
My question is: is this behavior expected for annotations, and how can one prevent fields added by one annotation from being inappropriately processed by another annotation?
At present the best workaround I've come up with for my specific issue is to add an errors field to my LdapUser object and mark it as transient (so that LDAP ignores it):
#Transient
ValidationErrors errors

How to use different error codes for validation messages?

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.

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

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

Resources