最新的春季mvc,使用freemarker。
希望有人可以告诉我在使用spring mvc验证表单时我的选择是什么,以及推荐的方法是做什么。
我有一个不直接映射到模型的表单,它有输入字段,当发布时,将用于初始化2个模型对象,然后我需要验证,如果它们通过,我将保存它们。
如果失败,我想返回表单,用用户输入的内容预填充值并显示错误消息。
我已经在这里和那里阅读了大约2种方法,其中一次我已经完成并理解它是如何工作的:
@RequestMapping(...., method = RequestMethod.POST)
public ModelAndView myMethod(@Valid MyModel, BindingResult bindingResult) {
ModelAndView mav = new ModelAndView("some/view");
mav.addObject("mymodel", myModel);
if(bindingResult.hasErrors()) {
return mav;
}
}
现在,如果我的表单直接映射到表单,这会有效,但在我的情况下,我有:
表单字段未映射到任何特定模型,它们具有2个模型中的一些属性。
在验证发生之前,我需要手动创建2个模型,从表单中设置值,并手动设置一些属性:
在两个模型(model1,model2)上调用validate,并将这些错误消息附加到错误集合中,如果事情不起作用,我需要将其传递回同一个视图页面。
当表单发布时,我必须做一些数据库调用,并根据这些结果可能需要向错误集合添加其他消息。
有人可以告诉我如何进行此类验证吗?
下面的伪代码:
Model1 model1 = new Model1();
Model2 model2 = new Model2();
// manually or somehow automatically set the posted form values to model1 and model2.
// set some fields manually, not from posted form
model1.setProperty10(GlobalSettings.getDefaultProperty10());
model2.setProperty11(GlobalSettings.getDefaultProperty11());
// db calls, if they fail, add to errors collection
if(bindingResult.hasErrors()) {
return mav;
}
// validation passed, save
Model1Service.save(model1);
Model2Service.save(model2);
redirect to another view
更新
我现在已经在我的模型上使用了JSR 303注释,如果我可以使用它们,它会很棒。
更新II
请阅读下面的赏金说明,了解我要找的内容。
答案 0 :(得分:8)
基于类似的经验,我建议以下内容,并且我会对您要采取的方法的最后一步发表评论。我使用您编号的步骤列表。
第1步:表单Bean
这有两种方法。简单的方法是定义一个表单bean(我认为你已经完成了):
class MyModel {
private Model1 model1;
private Model2 model2;
// standard Java bean stuff
}
更精确的方法是实际定义MyModel
,以便我只借用Model1
和Model2
所需的字段,但我不确定这是否符合您的要求。< / p>
第2步:数据绑定
如果你的视图中有form
结构,那么Spring会为你做这件事:
<form:form modelAttribute="myModel">
<form:input path="model1.somePropertyToBeSet" />
</form:form>
第3步:验证
使用Spring custom validations,您可以定义自定义约束:
@interface Model1Constraint {}
@interface Model2Constraint {}
class MyModel1 {
@Model1Constraint
private Model1 model1;
@Model2Constraint
private Model2 model2;
// ...
}
然后为自定义约束注册自定义验证器:
class Model1ConstraintValidator implements ConstraintValidator<Model1Constraint, Model1> {
// implementation of isValid and initalize
}
Model2Constraint
也一样。使用自定义验证器,您可以检查在将MyModel
传递到请求处理方法之前需要确保的逻辑。我还假设您已经使用<mvc:annotation-driven />
让Spring注册验证器;否则,你应该配置它们。
步骤4:请求处理前的自定义模型处理
您最初的想法是为此作业使用一些数据绑定器。在您的描述中,您还提到此数据处理不取决于来自表单数据的数据。
关于设计和模块化,我不认为数据绑定器是出于此目的的好地方。其次,由于表单没有数据依赖性,因此主要原因是允许数据绑定错误处理。
所以,我的建议是,让我们说现在你在public ModelAndView myMethod(@Valid MyModel model, BindingResult bindingResult)
。据推测,您可以在此处访问其他服务bean。因此,您可以在某个服务bean中使用一个方法,该方法可以refine
或prepare
(只是命名)此时填充的model
。基于例外或任何其他适合您的机制,您可以使用bindingResult
再次返回错误。
作为另一个建议,如果您想要更多的DI / IoC方法,您也可以利用Spring interceptors。但是通过这种方式,您应该在拦截中从MyModel
中提取ModelAndView
并继续。
我希望这会有所帮助。
答案 1 :(得分:1)
答案 2 :(得分:0)
Hibernate Validator 4.2支持方法级别验证。您可以稍微重构代码以在方法中传递两个模型,并验证它们。
http://java.dzone.com/articles/method-validation-spring-31
你可以有这样的东西
public void persistUser(@NotNull @Valid Model1 model1, @NotNull @Valid Model2 model2) {
//continue ...
}
答案 3 :(得分:0)
这是一个不寻常的问题,因为验证用户输入通常是最有意义的,因为这是我们无法控制的数据。话虽如此,因为我相信你已经知道了......
一种选择是在从用户输入,数据库等填充模型对象之后直接使用JSR 303验证API来验证它们。
以下是一个例子:
@RequestMapping(value=...)
public String myMethod(MyForm form, Model m) {
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Model1 model1 = createModel1FromUserInput(form);
Model2 model2 = createModel2FromUserInput(form);
Set<ConstraintViolation<?>> modelViolations = validator.validate(model1);
modelViolations.add(validator.validate(model2));
if(modelViolations.size() != 0) {
m.addAttribute("violations", modelViolations);
m.addAttribute("form", myForm); // add the form back to the model so it is populated
return "formPage";
}
return "successPage";
}
您可能更喜欢绑定到Spring BindingResult或错误集合。我没有测试过以下代码,并且不习惯直接使用BindingResult,因此需要调整:
BindingResult result = ... // not sure about the constructor
for(ConstraintViolation<?> violation : modelViolations) {
result.addError(new ObjectError(violation.getPropertyPath().toString(),violation.getMessage()));
}
如果您只是试图吐出JSP上的错误,那么使用Set<ConstraintViolation>
可以很好地满足您的需求:
<c:forEach items="${violations}" var="violation">
${violation.propertyPath}: ${violation.message} <br/>
</c:forEach>
如果您尝试使用Spring的<form:errors>
标记,则需要使用绑定结果。
HTH!如果您还有其他问题,请与我们联系。或者如果我完全错过了这个答案的标记,我会试着澄清一下。