对于如何验证spring mvc表单感到困惑,我的选择是什么?

时间:2012-04-04 16:53:38

标签: java spring validation spring-mvc freemarker

最新的春季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;
  }

}

现在,如果我的表单直接映射到表单,这会有效,但在我的情况下,我有:

  1. 表单字段未映射到任何特定模型,它们具有2个模型中的一些属性。

  2. 在验证发生之前,我需要手动创建2个模型,从表单中设置值,并手动设置一些属性:

  3. 在两个模型(model1,model2)上调用validate,并将这些错误消息附加到错误集合中,如果事情不起作用,我需要将其传递回同一个视图页面。

  4. 当表单发布时,我必须做一些数据库调用,并根据这些结果可能需要向错误集合添加其他消息。

  5. 有人可以告诉我如何进行此类验证吗?

    下面的伪代码:

       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

    请阅读下面的赏金说明,了解我要找的内容。

4 个答案:

答案 0 :(得分:8)

基于类似的经验,我建议以下内容,并且我会对您要采取的方法的最后一步发表评论。我使用您编号的步骤列表。

  

第1步:表单Bean

这有两种方法。简单的方法是定义一个表单bean(我认为你已经完成了):

class MyModel {
  private Model1 model1;
  private Model2 model2;
  // standard Java bean stuff
}

更精确的方法是实际定义MyModel,以便我只借用Model1Model2所需的字段,但我不确定这是否符合您的要求。< / 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中使用一个方法,该方法可以refineprepare(只是命名)此时填充的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!如果您还有其他问题,请与我们联系。或者如果我完全错过了这个答案的标记,我会试着澄清一下。