Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@Pattern error message "{0}" is not resolved. [SPR-17217] #21750

Open
spring-issuemaster opened this issue Aug 27, 2018 · 6 comments

Comments

@spring-issuemaster
Copy link
Collaborator

@spring-issuemaster spring-issuemaster commented Aug 27, 2018

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: spring-projects/spring-boot#14163

@rstoyanchev

This comment has been minimized.

Copy link
Contributor

@rstoyanchev rstoyanchev commented Jan 29, 2019

I've updated the description with the description from the Boot ticket.

@jayanth1007

This comment has been minimized.

Copy link

@jayanth1007 jayanth1007 commented Mar 6, 2019

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"});
@sbrannen

This comment has been minimized.

Copy link
Member

@sbrannen sbrannen commented Mar 6, 2019

@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.

@johnlinp

This comment has been minimized.

Copy link
Contributor

@johnlinp johnlinp commented Aug 23, 2019

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}"."
}
johnlinp added a commit to johnlinp/spring-framework that referenced this issue Aug 23, 2019
johnlinp added a commit to johnlinp/spring-framework that referenced this issue Aug 23, 2019
johnlinp added a commit to johnlinp/spring-framework that referenced this issue Aug 24, 2019
@johnlinp

This comment has been minimized.

Copy link
Contributor

@johnlinp johnlinp commented Aug 25, 2019

Here is a demonstration of how to use a custom MessageInterpolator to fix the problem: yoshikawaa/spring-boot-pattern-demo#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?

@johnlinp

This comment has been minimized.

Copy link
Contributor

@johnlinp johnlinp commented Sep 3, 2019

I guess this issue is related to #18167.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
5 participants
You can’t perform that action at this time.