spring-framework
spring-framework copied to clipboard
@Pattern error message "{0}" is not resolved. [SPR-17217]
yoshikawaa opened SPR-17217 and commented
Application
Spring Boot Web MVC Application Spring Boot 2.0.4.RELEASE Sample : https://github.com/yoshikawaa/spring-boot-pattern-demo
Problem
There are cases where @Pattern error message {0} is not resolved.
- ValidationMessages.properties
javax.validation.constraints.Pattern.message = {0} must match "{regexp}".
- application.properties
spring.messages.basename = ValidationMessages
- Form
public class DemoForm {
@Pattern(regexp = "\\d{3}") // -> message [ valid must match "\d{3}". ]
private String valid;
@Pattern(regexp = "\\d{1,3}") // -> message [ {0} must match "\d{1,3}". ]
private String invalid;
// omit getter and setter
}
My validation is that if a range of digits is used in a regular expression, the mechanism for resolving {0} does not work properly.
Controller
@PostMapping
public String post(@Valid DemoForm form, BindingResult result) {
result.getFieldErrors().forEach(e -> logger.info("field:{},error:{}", e.getField(), e.getDefaultMessage()));
// log [ field:valid,error:{0} must match "\d{3}". ]
// log [ field:invalid,error:{0} must match "\d{1,3}". ]
return "demo";
}
When logging BindingResult with Controller, you can see that LocalValidatorFactoryBean (Hibernate Validator) resolves message variable {regexp} beforehand.
After that, when Spring MVC resolve the message, it is considered that the regular expression in the message is obstructing resolution of {0}.
Affects: 5.0.8
Reference URL: https://github.com/spring-projects/spring-boot/issues/14163
I've updated the description with the description from the Boot ticket.
This is Java issue which throws java.lang.IllegalArgumentException: unknown format type: 3. Spring core silently proceeding with the raw message in this case. org.springframework.context.support.MessageSourceSupport#formatMessage
MessageFormat messageFormat = new MessageFormat("{0} must match \"\\d{1,3}\"");
messageFormat.format(new Object[] {"valid"});
@jayanth1007, thanks for investigating this and providing your findings.
You're completely right: executing the following...
public static void main(String[] args) {
new MessageFormat("{0} must match \"\\d{1,3}\"");
}
... results in:
Exception in thread "main" java.lang.IllegalArgumentException: unknown format type: 3
at java.text.MessageFormat.makeFormat(MessageFormat.java:1526)
at java.text.MessageFormat.applyPattern(MessageFormat.java:479)
at java.text.MessageFormat.<init>(MessageFormat.java:362)
So, the MessageFormat constructor throws an exception immediately.
The above mentioned IllegalArgumentException is because of that MessageFormat tried to parse {1,3} as a FormatElement with the format of { ArgumentIndex , FormatType }, which is described in the java doc of MessageFormat.
Currently, the original message in @yoshikawaa's example is:
{0} must match "{regexp}".
it would be interpolated by a MessageInterpolator as:
{0} must match "\d{1,3}".
When MessageFormat sees it, it would start parsing {1,3} and then throw an exception.
I suggest that we should use single quotes to quote the "non-FormatElement" part of the message when interpolating the message, so that it won't be parsed by MessageFormat. For example, we can provide a custom MessageInterpolator which interpolate the message as:
{0} must match "'\d{1,3}'".
so that it can be successfully formatted by MessageFormat.format() as:
invalid must match "\d{1,3}".
One can test the above behavior by the code snippet:
public static void main(String[] args) {
MessageFormat messageFormat = new MessageFormat("{0} must match \"'\\d{1,3}'\"");
String formattedString = messageFormat.format(new Object[] {"invalid"});
System.out.println(formattedString); // prints "invalid must match "\d{1,3}"."
}
Here is a demonstration of how to use a custom MessageInterpolator to fix the problem: https://github.com/yoshikawaa/spring-boot-pattern-demo/pull/1
However, I am not very sure about where to put the custom MessageInterpolator; should I put it in spring-context, spring-boot, or even hibernate-validator?
I guess this issue is related to https://github.com/spring-projects/spring-framework/issues/18167.
Hi, may i know this problem was resolved or not? And another two issues was occurred:
MessageFormat.format("{0} is a number, {1} is a number, but i want to show single quote \"'\"", 1, 2) //print 1 is a number, 2 is a number, but i want to show single quote ""
Single quote was missing, what i want to display is:
1 is a number, 2 is a number, but i want to show single quote "'"
However, upper problems can be fixed by adding "'" before single quote as below:
MessageFormat.format("{0} is a number, {1} is a number, but i want to show single quote \"''\"", 1, 2) print 1 is a number, 2 is a number, but i want to show single quote "'"
And PR is needed or not?