【问题标题】:Confused as to how to validate spring mvc form, what are my options?对如何验证 spring mvc 表单感到困惑,我有什么选择?
【发布时间】:2012-04-18 10:28:16
【问题描述】:

最新的spring 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. 在两个模型(模型 1、模型 2)上调用 validate,并将这些错误消息附加到错误集合中,如果事情不起作用,我需要将其传递回同一视图页面。

    李>
  4. 当表单发布时,我必须进行一些数据库调用,并且基于这些结果可能需要向错误集合添加额外的消息。

谁能告诉我如何进行这种验证?

伪代码如下:

   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 注释,如果我还能使用这些注释那就太好了。

更新二

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

【问题讨论】:

    标签: java spring validation spring-mvc freemarker


    【解决方案1】:

    基于类似的经验,我会提出以下建议,同时我对您要​​采取的方法的最后一步发表评论。我使用你编号的步骤列表。

    第 1 步:表单 Bean

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

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

    更精确的方法是实际定义MyModel,这样我只借用Model1 和Model2 中需要的字段,但我不确定这是否适合您的方式。

    第 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 传递到请求处理方法之前需要确保的逻辑。我还假设您已经使用 &lt;mvc:annotation-driven /&gt; 让 Spring 注册验证器;否则,您应该配置它们。

    第 4 步:处理请求之前的自定义模型处理

    您最初的想法是使用一些数据绑定器来完成这项工作。在您的描述中,您还提到此数据处理不依赖于来自表单数据的数据。

    关于设计和模块化,我不认为数据绑定器是实现此目的的好地方。其次,由于表单没有数据依赖,你的主要原因是允许数据绑定错误处理。

    所以,我的建议是,假设您现在在public ModelAndView myMethod(@Valid MyModel model, BindingResult bindingResult)。大概,您可以在此处访问其他服务 bean。因此,您可以在某个服务 bean 中拥有一个方法,该方法可以 refine 或 prepare(只是命名)您在这一点上填充的 model。根据exceptions 或任何其他适合您的机制,您可以使用bindingResult 再次返回错误。

    作为另一个建议,如果您想要更多的 DI/IoC 方式,您也可以利用 Spring interceptors。但是这样,你应该在拦截中从ModelAndView中提取MyModel并继续。

    我希望这会有所帮助。

    【讨论】:

      【解决方案2】:

      【讨论】:

        【解决方案3】:

        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 ...
        }   
        

        【讨论】:

          【解决方案4】:

          这是一个不寻常的问题,因为验证用户输入通常最有意义,因为这是我们无法控制的数据。话虽如此,而且我敢肯定你已经知道了……

          一种选择是在从用户输入、数据库等填充模型对象后,直接使用 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&lt;ConstraintViolation&gt; 可能足以满足您的需求:

          <c:forEach items="${violations}" var="violation">
              ${violation.propertyPath}: ${violation.message} <br/>
          </c:forEach>
          

          如果您尝试使用 Spring 的 &lt;form:errors&gt; 标签,则需要使用绑定结果。

          HTH!如果您还有其他问题,请告诉我。或者如果我完全错过了这个答案的标记,我会尝试澄清。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2023-04-06
            • 1970-01-01
            • 2013-06-02
            • 1970-01-01
            • 2014-01-04
            • 2011-07-09
            • 2013-11-21
            • 2016-11-18
            相关资源
            最近更新 更多