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
Comment From: rstoyanchev
I've updated the description with the description from the Boot ticket.
Comment From: jayanth1007
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"});
Comment From: sbrannen
@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.
Comment From: johnlinp
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}"."
}
Comment From: johnlinp
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
?
Comment From: johnlinp
I guess this issue is related to https://github.com/spring-projects/spring-framework/issues/18167.
Comment From: chenghaoharvey
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?