Ste*_*Kuo 13 java validation spring
我正在使用Spring进行表单输入和验证.表单控制器的命令包含正在编辑的模型.某些模型的属性是自定义类型.例如,Person的社会安全号码是自定义SSN类型.
public class Person {
public String getName() {...}
public void setName(String name) {...}
public SSN getSocialSecurtyNumber() {...}
public void setSocialSecurtyNumber(SSN ssn) {...}
}
Run Code Online (Sandbox Code Playgroud)
并在Spring表单编辑命令中包装Person:
public class EditPersonCommand {
public Person getPerson() {...}
public void setPerson(Person person) {...}
}
Run Code Online (Sandbox Code Playgroud)
由于Spring不知道如何将文本转换为SSN,因此我使用表单控制器的binder注册了一个客户编辑器:
public class EditPersonController extends SimpleFormController {
protected void initBinder(HttpServletRequest req, ServletRequestDataBinder binder) {
super.initBinder(req, binder);
binder.registerCustomEditor(SSN.class, "person.ssn", new SsnEditor());
}
}
Run Code Online (Sandbox Code Playgroud)
和SsnEditor只是一个java.beans.PropertyEditor可以将文本转换为SSN对象的自定义:
public class SsnEditor extends PropertyEditorSupport {
public String getAsText() {...} // converts SSN to text
public void setAsText(String str) {
// converts text to SSN
// throws IllegalArgumentException for invalid text
}
}
Run Code Online (Sandbox Code Playgroud)
如果setAsText遇到无效且无法转换为SSN的文本,则抛出IllegalArgumentException(按照PropertyEditor setAsText规范).我遇到的问题是文本到对象转换(via PropertyEditor.setAsText())发生在我的Spring验证器被调用之前.当setAsText抛出IllegalArgumentException,春只显示在定义的一般错误消息errors.properties.我想要的是一个特定的错误消息,取决于输入的SSN无效的确切原因.PropertyEditor.setAsText()会确定原因.我已经尝试在文本中嵌入错误原因文本IllegalArgumentException,但Spring只是将其视为一般错误.
这个问题有方法解决吗?重复一遍,我想要的是PropertyEditorSpring表单上的表面生成的特定错误消息.我能想到的唯一选择是将SSN作为文本存储在命令中,并在验证器中执行验证.SSN对象转换的文本将以表单形式发生onSubmit.这是不太理想的,因为我的表单(和模型)具有许多属性,我不想创建和维护一个将每个模型属性作为文本字段的命令.
以上只是一个例子,我的实际代码不是Person/SSN,所以没有必要回复"为什么不将SSN存储为文本......"
您正尝试在活页夹中进行验证.这不是活页夹的目的.绑定器应该将请求参数绑定到您的后备对象,仅此而已.属性编辑器将字符串转换为对象,反之亦然 - 它不是为了做其他事情而设计的.
换句话说,你需要考虑关注点的分离 - 你试图将功能强加到一个对象中,而这个对象除了将字符串转换为对象之外从不打算做任何事情,反之亦然.
您可以考虑将SSN对象分解为多个易于绑定的可验证字段(String对象,日期等基本对象).这样,您可以在绑定后使用验证程序来验证SSN是否正确,或者您可以直接设置错误.使用属性编辑器,抛出IllegalArgumentException,Spring将其转换为类型不匹配错误,因为它就是这样 - 字符串与预期的类型不匹配.就是这样.另一方面,验证器可以做到这一点.只要填充了SSN实例,就可以使用spring绑定标记绑定到嵌套字段 - 必须首先使用new()初始化它.例如:
<spring:bind path="ssn.firstNestedField">...</spring:bind>
Run Code Online (Sandbox Code Playgroud)
但是,如果你真的想坚持这条路径,那么让你的属性编辑器保留一个错误列表 - 如果要抛出IllegalArgumentException,将它添加到列表然后抛出IllegalArgumentException(如果需要,捕获并重新抛出).因为您可以在与绑定相同的线程中构造属性编辑器,所以如果您只是覆盖属性编辑器的默认行为,它将是线程安全的 - 您需要找到它用于进行绑定的钩子,并覆盖它 - 执行相同的属性编辑器您现在正在进行注册(除了使用相同的方法,以便您可以保留对编辑器的引用),然后在绑定结束时,如果您提供公共访问器,则可以通过从编辑器中检索列表来注册错误.检索到列表后,您可以对其进行处理并相应地添加错误.
如上所述:
我想要的是PropertyEditor生成的特定错误消息,表示Spring表单上的错误消息
在幕后,Spring MVC使用BindingErrorProcessor策略处理丢失的字段错误,并将PropertyAccessException转换为FieldError.因此,如果要覆盖默认的Spring MVC BindingErrorProcessor策略,则必须根据以下内容提供BindingErrorProcessor策略:
public class CustomBindingErrorProcessor implements DefaultBindingErrorProcessor {
public void processMissingFieldError(String missingField, BindException errors) {
super.processMissingFieldError(missingField, errors);
}
public void processPropertyAccessException(PropertyAccessException accessException, BindException errors) {
if(accessException.getCause() instanceof IllegalArgumentException)
errors.rejectValue(accessException.getPropertyChangeEvent().getPropertyName(), "<SOME_SPECIFIC_CODE_IF_YOU_WANT>", accessException.getCause().getMessage());
else
defaultSpringBindingErrorProcessor.processPropertyAccessException(accessException, errors);
}
}
Run Code Online (Sandbox Code Playgroud)
为了测试,让我们做以下几点
protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) {
binder.registerCustomEditor(SSN.class, new PropertyEditorSupport() {
public String getAsText() {
if(getValue() == null)
return null;
return ((SSN) getValue()).toString();
}
public void setAsText(String value) throws IllegalArgumentException {
if(StringUtils.isBlank(value))
return;
boolean somethingGoesWrong = true;
if(somethingGoesWrong)
throw new IllegalArgumentException("Something goes wrong!");
}
});
}
Run Code Online (Sandbox Code Playgroud)
现在我们的Test类
public class PersonControllerTest {
private PersonController personController;
private MockHttpServletRequest request;
@BeforeMethod
public void setUp() {
personController = new PersonController();
personController.setCommandName("command");
personController.setCommandClass(Person.class);
personController.setBindingErrorProcessor(new CustomBindingErrorProcessor());
request = new MockHttpServletRequest();
request.setMethod("POST");
request.addParameter("ssn", "somethingGoesWrong");
}
@Test
public void done() {
ModelAndView mav = personController.handleRequest(request, new MockHttpServletResponse());
BindingResult bindingResult = (BindingResult) mav.getModel().get(BindingResult.MODEL_KEY_PREFIX + "command");
FieldError fieldError = bindingResult.getFieldError("ssn");
Assert.assertEquals(fieldError.getMessage(), "Something goes wrong!");
}
}
Run Code Online (Sandbox Code Playgroud)
问候,
| 归档时间: |
|
| 查看次数: |
17247 次 |
| 最近记录: |