我使用Spring进行表单输入和验证。表单控制器的命令包含正在编辑的模型。模型的一些属性是自定义类型。例如,个人的社会保险号码是自定义SSN类型。
public class Person {
public String getName() {...}
public void setName(String name) {...}
public SSN getSocialSecurtyNumber() {...}
public void setSocialSecurtyNumber(SSN ssn) {...}
}并将Person包装在Spring窗体编辑命令中:
public class EditPersonCommand {
public Person getPerson() {...}
public void setPerson(Person person) {...}
}由于Spring不知道如何将文本转换为SSN,所以我向表单控制器的绑定器注册了一个客户编辑器:
public class EditPersonController extends SimpleFormController {
protected void initBinder(HttpServletRequest req, ServletRequestDataBinder binder) {
super.initBinder(req, binder);
binder.registerCustomEditor(SSN.class, "person.ssn", new SsnEditor());
}
}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
}
}如果setAsText遇到无效且不能转换为SSN的文本,那么它会抛出IllegalArgumentException (按照PropertyEditor setAsText的规范)。我遇到的问题是,文本到对象的转换(通过PropertyEditor.setAsText())发生在调用Spring验证器的之前。当setAsText抛出IllegalArgumentException时,Spring只显示errors.properties中定义的通用错误消息。我想要的是一个特定的错误消息,它取决于输入的SSN无效的确切原因。PropertyEditor.setAsText()会决定原因。我尝试将错误原因文本嵌入到IllegalArgumentException的文本中,但Spring只是将其视为一个通用错误。
有解决办法吗?重复一遍,我想要的是PropertyEditor生成的特定错误消息,以显示到Spring上的错误消息。我能想到的唯一选择是将SSN作为文本存储在命令中,并在验证器中执行验证。文本到SSN对象的转换将在表单的onSubmit中进行。这不太理想,因为我的表单(和模型)有许多属性,而且我不想创建和维护一个命令,该命令将每个模型属性都作为文本字段。
以上只是一个例子,我的实际代码不是Person/ SSN,所以不需要回答“为什么不将SSN存储为文本……”
发布于 2009-03-28 02:11:26
你想在活页夹里做验证。这不是活页夹的目的。绑定器应该将请求参数绑定到支持对象,仅此而已。属性编辑器将String转换为对象,反之亦然--它不是用来做任何其他事情的。
换句话说,您需要考虑关注点的分离--您试图将功能压缩到一个除了将字符串转换为对象之外,从来没有做过任何事情的对象。
您可以考虑将SSN对象分解为多个易于绑定的可验证字段(字符串对象、基本对象(如日期等))。通过这种方式,您可以在绑定后使用验证器来验证SSN是否正确,或者可以直接设置错误。使用属性编辑器,抛出一个IllegalArgumentException,Spring将其转换为类型不匹配错误,因为它就是--字符串与预期的类型不匹配。仅此而已。另一方面,验证器可以做到这一点。只要填充了SSN实例,就可以使用spring标记绑定到嵌套字段--必须首先用new()对其进行初始化。例如:
<spring:bind path="ssn.firstNestedField">...</spring:bind>但是,如果您确实希望在此路径上持久化,请让属性编辑器保存一个错误列表--如果要抛出一个IllegalArgumentException,则将其添加到列表中,然后抛出IllegalArgumentException (如果需要,请立即捕获并重新抛出)。因为您可以在与绑定相同的线程中构造属性编辑器,所以如果您只需重写属性编辑器的默认行为--您需要找到它用于绑定的钩子,并重写它--就可以构造属性编辑器(除了在相同的方法中,这样您可以保留对编辑器的引用),然后在绑定结束时,如果您提供了一个公共访问器,就可以从编辑器检索列表,从而注册错误。一旦检索到列表,您就可以处理它并相应地添加错误。
发布于 2009-08-19 20:47:23
如前所述:
我想要的是PropertyEditor生成的特定于PropertyEditor的错误消息,以显示到Spring表单上的错误消息。
在幕后,Spring使用BindingErrorProcessor策略处理丢失的字段错误,使用将PropertyAccessException转换为FieldError。因此,如果您想要覆盖默认的Spring策略,则必须根据以下步骤提供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);
}
}为了进行测试,让我们执行以下操作
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!");
}
});
}现在我们的考试课
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!");
}
}打招呼,
发布于 2016-01-18 23:00:22
作为对“亚瑟·罗纳德”答案的跟进,这是我最后如何实现的:
在控制器上:
setBindingErrorProcessor(new CustomBindingErrorProcessor());然后是绑定错误处理器类:
public class CustomBindingErrorProcessor extends DefaultBindingErrorProcessor {
public void processPropertyAccessException(PropertyAccessException accessException,
BindingResult bindingResult) {
if(accessException.getCause() instanceof IllegalArgumentException){
String fieldName = accessException.getPropertyChangeEvent().getPropertyName();
String exceptionError = accessException.getCause().getMessage();
FieldError fieldError = new FieldError(fieldName,
"BINDING_ERROR",
fieldName + ": " + exceptionError);
bindingResult.addError(fieldError);
}else{
super.processPropertyAccessException(accessException, bindingResult);
}
}
} 因此,在此版本上,处理器方法的签名采用BindingResult而不是BindException。
https://stackoverflow.com/questions/691790
复制相似问题