6

最新的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. 在两个模型(model1、model2)上调用 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 注释,如果我还能使用这些注释那就太好了。

更新二

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

4

4 回答 4

8

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

第 1 步:形成 Bean

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

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

更精确的方法是实际定义MyModel这样我只借用需要的字段,Model1Model2我不确定这是否适合你的方式。

第 2 步:数据绑定

如果您的视图中有这样form的结构,Spring 会为您执行此操作:

<form:form modelAttribute="myModel">
  <form:input path="model1.somePropertyToBeSet" />
</form:form>

第 3 步:验证

使用Spring 自定义验证,您可以定义自定义约束:

@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 注册验证器;否则,您应该配置它们。

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

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

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

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

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

我希望这有帮助。

于 2012-04-08T17:27:05.807 回答
1

这里有很棒的解释,请检查Integrating Spring MVC 3.0 with JSR 303 (aka javax.validation.*)

也检查以下示例JSR 303 Bean Validation Using Spring 3

于 2012-04-13T12:05:28.747 回答
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 上吐出错误,那么使用 aSet<ConstraintViolation>可能足以满足您的需求:

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

如果您尝试使用 Spring 的<form:errors>标签,则需要使用绑定结果。

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

于 2012-04-09T15:36:40.230 回答
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 ...
}   
于 2012-04-09T13:27:29.447 回答