Spring MVC + Hibernate:数据验证策略

我们都知道,Spring MVC通常与Hibernate Validator和JSR-303集成得很好。但是,正如有人所说,Hibernate Validator仅适用于Bean Validation,这意味着应该将更复杂的验证推送到数据层。此类验证的示例:业务密钥唯一性,记录内依赖性(这通常指向数据库设计问题,但我们都生活在一个不完美的世界中)。即使是像字符串字段长度这样的简单验证也可能由某些数据库值驱动,这使得Hibernate Validator不可用。

所以我的问题是,Spring或Hibernate或JSR提供了什么来执行如此复杂的验证吗?是否有一些既定的模式或技术部分可以在基于Spring和Hibernate的标准控制器 - 服务 - 存储库设置中执行此类验证?

更新:让我更具体地说一点。例如,有一个表单将 AJAX 保存请求发送到控制器的方法。如果发生一些验证错误 - 无论是简单的还是“复杂的” - 我们应该回到浏览器,并带有一些json,指示有问题的字段和相关错误。对于简单的错误,我可以从中提取字段(如果有)和错误消息。对于“复杂”错误,您会提出哪些基础结构(可能是特定的,而不是临时的例外?)?使用异常处理程序对我来说似乎不是一个好主意,因为在方法之间分离单个验证过程并使事情变得复杂。目前我使用一些临时异常(如:saveBindingResultsave@ExceptionHandlerValidationException

public @ResponseBody Result save(@Valid Entity entity, BindingResult errors) {
    Result r = new Result();
    if (errors.hasErrors()) {
        r.setStatus(Result.VALIDATION_ERROR);     
        // ...   
    } else {
        try {
            dao.save(entity);
            r.setStatus(Result.SUCCESS);
        } except (ValidationException e) {
            r.setStatus(Result.VALIDATION_ERROR);
            r.setText(e.getMessage());
        }
    }
    return r;
}

您能提供一些更理想的方法吗?


答案 1

是的,有一个很好的旧的Java模式,即异常抛出
Spring MVC很好地集成了它(对于代码示例,您可以直接跳到我的答案的第二部分)。

您所说的“复杂验证”实际上是例外:业务密钥单一性错误,低层或数据库错误等。


提醒:春季MVC中的验证是什么?

验证应在表示层上进行。它基本上是关于验证提交的表单字段。

我们可以将它们分为两种:

1)光验证(使用JSR-303 / Hibernate验证):检查提交的字段是否具有给定的/,它是或/,检查它是否具有格式等。@Size@Length@NotNull@NotEmpty@NotBlank@Email

2)重验证或复杂验证更多地是关于字段验证的特定情况,例如跨字段验证:

  • 示例 1:窗体具有 、 和 。单独来看,每个字段都可以为空,但其中至少有一个字段不能为空。fieldAfieldBfieldC
  • 示例 2 :如果字段的值小于 18,则字段不得为 null,并且 的期限必须大于 21。userAgeresponsibleUserresponsibleUser

这些验证可以通过Spring Validator实现自定义注释/约束来实现

现在我明白了,有了所有这些验证设施,再加上Spring根本不具有侵入性,可以让你做任何你想做的事情(无论好坏),人们可能会忍不住使用“验证锤”来做任何与错误处理模糊相关的事情。
而且它可以工作:仅使用验证,您可以检查验证器/注释中的每个可能问题(并且在较低层中几乎不会引发任何异常)。这很糟糕,因为你祈祷你考虑了所有的案件。您不会利用 Java 异常来简化逻辑,并通过忘记检查某些内容是否存在错误来减少出错的机会。

因此,在Spring MVC世界中,不应将验证(即UI验证)误认为是较低层的异常,例如具有服务异常或DB异常(键单市等)。


如何以方便的方式处理Spring MVC中的异常?

有些人认为“哦,上帝,所以在我的控制器中,我必须逐个检查所有可能的检查异常,并为每个异常考虑一个消息错误?没办法!我就是其中之一。:-)

在大多数情况下,只需使用一些通用的已检查异常类,所有异常都会扩展。然后只需在Spring MVC控制器中处理它,@ExceptionHandler和一般错误消息。

代码示例:

public class MyAppTechnicalException extends Exception { ... }

@Controller
public class MyController {

    ...

    @RequestMapping(...)
    public void createMyObject(...) throws MyAppTechnicalException {
        ...
        someServiceThanCanThrowMyAppTechnicalException.create(...);
        ...
    }

    ...

    @ExceptionHandler(MyAppTechnicalException.class)
    public String handleMyAppTechnicalException(MyAppTechnicalException e, Model model) {

        // Compute your generic error message/code with e.
        // Or just use a generic error/code, in which case you can remove e from the parameters
        String genericErrorMessage = "Some technical exception has occured blah blah blah" ;

        // There are many other ways to pass an error to the view, but you get the idea
        model.addAttribute("myErrors", genericErrorMessage);

        return "myView";
    }

}

简单,快速,简单,干净!

对于那些需要显示某些特定异常的错误消息,或者由于设计不佳的旧系统而无法修改的通用顶级异常时,只需添加其他异常。
@ExceptionHandler

@ExceptionHandler({MyException1.class, MyException2.class, ...})
public String yourMethod(Exception e, Model model) {
    ...
}

底线:何时使用验证?何时使用例外?

  • 来自 UI 的错误 = 验证 = 验证工具(JSR-303 注释、自定义注释、Spring 验证器)
  • 来自较低层的错误 = 异常

当我说“来自UI的错误”时,我的意思是“用户在他的表单中输入了错误的东西”。

引用:


答案 2

推荐