自定义验证消息适用于某些错误
custom validation message works for some errors
我有这个 Web 应用程序,我在其中设置了 bean 验证,它运行得非常好。但是对于一个字段,它不会读取我在 validation.properties 文件中设置的消息。对于其他领域,它有效。我很困惑为什么会这样?让我向您展示我的设置:
@Bean
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setCacheSeconds(0);
messageSource.setDefaultEncoding(StandardCharsets.UTF_8.name());
messageSource.setBasenames("/WEB-INF/Validation/validation"); //I have tried setBaseName as well but same difference.
return messageSource;
}
@Bean
public LocalValidatorFactoryBean localValidatorFactoryBean() {
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.setValidationMessageSource(this.messageSource());
return validator;
}
@Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
processor.setValidator(this.localValidatorFactoryBean());
return processor;
}
@Autowired
SpringValidatorAdapter validator; //global validator
@Override
public Validator getValidator() {
return this.validator;
}
我的属性文件位于正确的位置。我知道,因为对于某些错误,消息是从这个 validation.properties 文件中读取的。位置是:
src\main\webapp\WEB-INF\Validation\validation.properties
里面的内容是:
validate.user.yearsexperience = Only numbers can be used here.
validate.user.phone = Phone numbers can only be digits and should exactly be 10 digits.
validate.user.name.blank = Name cannot be left empty or Cannot contain more than 30 chars. It will accept letters only. No numbers or symbols are allowed either.
然后我设置了以下错误代码以预期 bean 验证器会捕获这些消息之一(但它没有)。
typeMismatch.user.yearsexperience = Only numbers can be used here.
typeMismatch.yearsexperience = Only numbers can be used here.
typeMismatch.java.lang.Integer = Only numbers can be used here.
typeMismatch = Only numbers can be used here.
因此,当我为 phone
或 name
字段输入错误数据时,JSP 会显示我在 validation.properties
文件中设置的那些消息,但对于 yearsexperience
字段,它显示自己的 typeMismatch
或 cannot convert String to Integer, etc exceptions
.
在这里让我向您展示我在模型上使用的注释:
public class User{
@NotBlankNoFancyLettersOrNumbers(message = "{validate.user.name.blank}") //My own custom annotation. Works like a charm and when user enters wrong data, it shows my custom error.
private String name;
@Pattern(regexp="(^$|[0-9]{10})", message = "{validate.user.phone}") //javax's builtin validation constraint
private String phone;
@So many things I have tried. But first I started with @Pattern
private Integer yearsexperience;
对于这个 Integer
字段,我不能使用 @Pattern
因为它只能放在 String
字段上(我在这里是正确的吗?)。
然后我尝试了 @Digits
。当用户输入错误的输入时,它确实有效,但不显示我的错误消息,它向用户显示 typeMismatch
异常。然后我做了我自己的自定义注解,我给它取了个名字@DigitsOnly
。同样的问题,它不会显示我的错误消息。它显示相同的旧 typeMismatch
异常。
在这一点上,我认为表单绑定可能存在问题(我应该不会认为是因为 bean 验证适用于其他字段)。无论如何,我像这样注册了自己的自定义编辑器:
@InitBinder("user")
protected void binder(WebDataBinder binder, HttpSession session) throws Exception {
binder.registerCustomEditor(Date.class, new DateConvertor()); //This is unrelated to my question but it works!
binder.registerCustomEditor(Integer.class, "yearsexperience", new IntegerConvertor(session.getAttribute("user"), this.userdao));
}
这是我的 class 扩展 PropertyEditorSupport
:
public class IntegerConvertor extends PropertyEditorSupport {
private User user;
private UserDAO userdao;
public IntegerConvertor(){}
public IntegerConvertor(Object object, UserDAO userdao) {
if (object instanceof User){
this.user= new User((User) object);
}
this.userdao = userdao;
}
@Override
public void setAsText(String value) {
if (!value.matches("[0-9]+")) { //if incoming value DOES NOT contains numbers only
setValue(this.userdao.getYearsExperienceBeforeChange(this.user.getId()));
} else {
setValue(value);
}
}
现在,我这样做是希望 typeMismatch
异常 不会 显示给用户,但它仍然会显示该异常。 (我从这个 PropertyEditorSupport
知道,否则它不会从 validation.properties
文件中读取自定义错误消息)。
无论如何,然后我尝试通过 WebDataBinder.addValidator(myCustomValidator)
添加自定义验证器,如下所示:
@InitBinder("user")
protected void binder(WebDataBinder binder, HttpSession session) throws Exception {
binder.registerCustomEditor(Date.class, new DateConvertor());
binder.addValidators(new myCustormValidator());
}
这是我的自定义验证器的样子,
public class MyCustormValidator implements Validator {
@Override
public boolean supports(Class<?> clazz) {
return User.class.equals(clazz); //return User.class.isAssignableFrom(clazz);
}
@Override
public void validate(Object target, Errors errors) {
User user = (User) target;
String a = Integer.toString(user.getYearsExperience());
if(!a.matches("[0-9]+")){
errors.rejectValue("yearsexperience","validate.user.yearsexperience");
}
}
}
我将 Integer
转换为 String
因为这样我就能够检查传入值是否只包含数字而不包含字母。但是这里又引起了另一个问题:它不显示传入值,它显示旧值。由于旧值已经正确,因此不会引发任何问题。所以我添加了 else 语句,只是为了抛出一个错误,希望现在至少它会显示我的自定义错误。但事实并非如此!
所以特地来求助。非常感谢任何帮助。
TL;DR 为什么自定义错误消息没有显示在一个字段中。但对于其他领域,它工作得很好。
Why the custom error doesn't show up for some fields but for others it works perfectly fine
因为涉及两个不同的组件:
- 用于 bean 验证的 hibernate-validator
- spring-mvc 将用户值从 a 绑定到 POJO class
您已经为 bean 验证正确配置了消息源并且它有效。
但是您正在自定义 typeMismatch
消息,看起来 Spring MVC 看不到这个。通常定义名称为 messageSource
的 bean 就足够了,但由于某些奇怪的原因,在您的情况下您还需要其他东西。
我的猜测是您有 2 个上下文,并且 messageSource
是在 Spring MVC 不可见的上下文中定义的。
更新:
我查看了 GitHub (http://github.com/farazdurrani/sscce) 上的示例项目,我发现非工作 typeMismatch
消息的问题不是因为配置。发生这种情况是因为您没有使用标准标签并尝试手动显示错误消息(通过对来自 Errors.getAllErrors()
的每个错误调用 getDefaultMessage()
方法)。如果您尝试使用 <form:errors path="*"/>
,您会看到所有错误消息都已正确本地化。
我有这个 Web 应用程序,我在其中设置了 bean 验证,它运行得非常好。但是对于一个字段,它不会读取我在 validation.properties 文件中设置的消息。对于其他领域,它有效。我很困惑为什么会这样?让我向您展示我的设置:
@Bean
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setCacheSeconds(0);
messageSource.setDefaultEncoding(StandardCharsets.UTF_8.name());
messageSource.setBasenames("/WEB-INF/Validation/validation"); //I have tried setBaseName as well but same difference.
return messageSource;
}
@Bean
public LocalValidatorFactoryBean localValidatorFactoryBean() {
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.setValidationMessageSource(this.messageSource());
return validator;
}
@Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
processor.setValidator(this.localValidatorFactoryBean());
return processor;
}
@Autowired
SpringValidatorAdapter validator; //global validator
@Override
public Validator getValidator() {
return this.validator;
}
我的属性文件位于正确的位置。我知道,因为对于某些错误,消息是从这个 validation.properties 文件中读取的。位置是:
src\main\webapp\WEB-INF\Validation\validation.properties
里面的内容是:
validate.user.yearsexperience = Only numbers can be used here.
validate.user.phone = Phone numbers can only be digits and should exactly be 10 digits.
validate.user.name.blank = Name cannot be left empty or Cannot contain more than 30 chars. It will accept letters only. No numbers or symbols are allowed either.
然后我设置了以下错误代码以预期 bean 验证器会捕获这些消息之一(但它没有)。
typeMismatch.user.yearsexperience = Only numbers can be used here.
typeMismatch.yearsexperience = Only numbers can be used here.
typeMismatch.java.lang.Integer = Only numbers can be used here.
typeMismatch = Only numbers can be used here.
因此,当我为 phone
或 name
字段输入错误数据时,JSP 会显示我在 validation.properties
文件中设置的那些消息,但对于 yearsexperience
字段,它显示自己的 typeMismatch
或 cannot convert String to Integer, etc exceptions
.
在这里让我向您展示我在模型上使用的注释:
public class User{
@NotBlankNoFancyLettersOrNumbers(message = "{validate.user.name.blank}") //My own custom annotation. Works like a charm and when user enters wrong data, it shows my custom error.
private String name;
@Pattern(regexp="(^$|[0-9]{10})", message = "{validate.user.phone}") //javax's builtin validation constraint
private String phone;
@So many things I have tried. But first I started with @Pattern
private Integer yearsexperience;
对于这个 Integer
字段,我不能使用 @Pattern
因为它只能放在 String
字段上(我在这里是正确的吗?)。
然后我尝试了 @Digits
。当用户输入错误的输入时,它确实有效,但不显示我的错误消息,它向用户显示 typeMismatch
异常。然后我做了我自己的自定义注解,我给它取了个名字@DigitsOnly
。同样的问题,它不会显示我的错误消息。它显示相同的旧 typeMismatch
异常。
在这一点上,我认为表单绑定可能存在问题(我应该不会认为是因为 bean 验证适用于其他字段)。无论如何,我像这样注册了自己的自定义编辑器:
@InitBinder("user")
protected void binder(WebDataBinder binder, HttpSession session) throws Exception {
binder.registerCustomEditor(Date.class, new DateConvertor()); //This is unrelated to my question but it works!
binder.registerCustomEditor(Integer.class, "yearsexperience", new IntegerConvertor(session.getAttribute("user"), this.userdao));
}
这是我的 class 扩展 PropertyEditorSupport
:
public class IntegerConvertor extends PropertyEditorSupport {
private User user;
private UserDAO userdao;
public IntegerConvertor(){}
public IntegerConvertor(Object object, UserDAO userdao) {
if (object instanceof User){
this.user= new User((User) object);
}
this.userdao = userdao;
}
@Override
public void setAsText(String value) {
if (!value.matches("[0-9]+")) { //if incoming value DOES NOT contains numbers only
setValue(this.userdao.getYearsExperienceBeforeChange(this.user.getId()));
} else {
setValue(value);
}
}
现在,我这样做是希望 typeMismatch
异常 不会 显示给用户,但它仍然会显示该异常。 (我从这个 PropertyEditorSupport
知道,否则它不会从 validation.properties
文件中读取自定义错误消息)。
无论如何,然后我尝试通过 WebDataBinder.addValidator(myCustomValidator)
添加自定义验证器,如下所示:
@InitBinder("user")
protected void binder(WebDataBinder binder, HttpSession session) throws Exception {
binder.registerCustomEditor(Date.class, new DateConvertor());
binder.addValidators(new myCustormValidator());
}
这是我的自定义验证器的样子,
public class MyCustormValidator implements Validator {
@Override
public boolean supports(Class<?> clazz) {
return User.class.equals(clazz); //return User.class.isAssignableFrom(clazz);
}
@Override
public void validate(Object target, Errors errors) {
User user = (User) target;
String a = Integer.toString(user.getYearsExperience());
if(!a.matches("[0-9]+")){
errors.rejectValue("yearsexperience","validate.user.yearsexperience");
}
}
}
我将 Integer
转换为 String
因为这样我就能够检查传入值是否只包含数字而不包含字母。但是这里又引起了另一个问题:它不显示传入值,它显示旧值。由于旧值已经正确,因此不会引发任何问题。所以我添加了 else 语句,只是为了抛出一个错误,希望现在至少它会显示我的自定义错误。但事实并非如此!
所以特地来求助。非常感谢任何帮助。
TL;DR 为什么自定义错误消息没有显示在一个字段中。但对于其他领域,它工作得很好。
Why the custom error doesn't show up for some fields but for others it works perfectly fine
因为涉及两个不同的组件:
- 用于 bean 验证的 hibernate-validator
- spring-mvc 将用户值从 a 绑定到 POJO class
您已经为 bean 验证正确配置了消息源并且它有效。
但是您正在自定义 typeMismatch
消息,看起来 Spring MVC 看不到这个。通常定义名称为 messageSource
的 bean 就足够了,但由于某些奇怪的原因,在您的情况下您还需要其他东西。
我的猜测是您有 2 个上下文,并且 messageSource
是在 Spring MVC 不可见的上下文中定义的。
更新:
我查看了 GitHub (http://github.com/farazdurrani/sscce) 上的示例项目,我发现非工作 typeMismatch
消息的问题不是因为配置。发生这种情况是因为您没有使用标准标签并尝试手动显示错误消息(通过对来自 Errors.getAllErrors()
的每个错误调用 getDefaultMessage()
方法)。如果您尝试使用 <form:errors path="*"/>
,您会看到所有错误消息都已正确本地化。