java Spring MVC + Hibernate:数据验证策略

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12660262/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 09:44:00  来源:igfitidea点击:

Spring MVC + Hibernate: data validation strategies

javavalidationspring-mvchibernate-validator

提问by Andrey Balaguta

We all know, that Spring MVC integrate well with Hibernate Validator and JSR-303 in general. But Hibernate Validator, as someone said, is something for Bean Validation only, which means that more complex validations should be pushed to the data layer. Examples of such validations: business key uniqueness, intra-records dependence (which is usually something pointing at DB design problems, but we all live in an imperfect world). Even simple validations like string field length may be driven by some DB value, which makes Hibernate Validator unusable.

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

So my question is, is there something Spring or Hibernate or JSR offers to perform such complex validations? Is there some establishedpattern or technology piece to perform such a validation in a standard Controller-Service-Repository setup based on Spring and Hibernate?

所以我的问题是,有没有 Spring、Hibernate 或 JSR 提供​​的东西来执行如此复杂的验证?在基于 Spring 和 Hibernate 的标准 Controller-Service-Repository 设置中,是否有一些既定的模式或技术来执行这样的验证?

UPDATE:Let me be more specific. For example, there's a form which sends an AJAX save request to the controller's savemethod. If some validation error occurs -- either simple or "complex" -- we should get back to the browser with some json indicating a problematic field and associated error. For simple errors I can extract the field (if any) and error message from BindingResult. What infrastructure (maybe specific, not ad-hoc exceptions?) would you propose for "complex" errors? Using exception handler doesn't seem like a good idea to me, because separating single process of validation between savemethod and @ExceptionHandlermakes things intricate. Currently I use some ad-hoc exception (like, ValidationException):

更新:让我更具体一点。例如,有一个表单向控制器的save方法发送 AJAX 保存请求。如果出现一些验证错误——无论是简单的还是“复杂的”——我们应该返回浏览器并使用一些 json 指示有问题的字段和相关错误。对于简单的错误,我可以从BindingResult. 对于“复杂”错误,您会建议哪些基础设施(可能是特定的,而不是临时的例外?)?使用异常处理程序对我来说似乎不是一个好主意,因为在save方法之间分离单个验证过程@ExceptionHandler并使事情变得复杂。目前我使用了一些特别的例外(比如,ValidationException):

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

Can you offer some more optimal approach?

你能提供一些更优化的方法吗?

回答by Jerome Dalbert

Yes, there is the good old established Java pattern of Exception throwing.
Spring MVC integrates it pretty well (for code examples, you can directly skip to the second part of my answer).

是的,有一个很好的老式 Java异常抛出模式。
Spring MVC 集成的很好(代码示例可以直接跳到我回答的第二部分)。

What you call "complex validations" are in fact exceptions : business key unicity error, low layer or DB errors, etc.

你所谓的“复杂验证”实际上是例外:业务关键单一性错误、低层或数据库错误等。



Reminder : what is validation in Spring MVC ?

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

Validation should happen on the presentation layer. It is basically about validating submitted form fields.

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

We could classify them into two kinds :

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

1) Light validation(with JSR-303/Hibernate validation) : checking that a submitted field has a given @Size/@Length, that it is @NotNullor @NotEmpty/@NotBlank, checking that it has an @Emailformat, etc.

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

2) Heavy validation, or complex validationare more about particular cases of field validations, such as cross-field validation :

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

  • Example 1 : The form has fieldA, fieldBand fieldC. Individually, each field can be empty, but at least one of them must not be empty.
  • Example 2 : if userAgefield has a value under 18, responsibleUserfield must not be null and responsibleUser's age must be over 21.
  • 示例 1:表单具有fieldA,fieldBfieldC。单独地,每个字段可以为空,但至少其中一个不能为空。
  • 示例 2:如果userAgefield 的值低于 18,则responsibleUserfield 不能为 null,并且responsibleUser的年龄必须超过 21。

These validations can be implemented with Spring Validator implementations, or custom annotations/constraints.

这些验证可以使用Spring Validator implementations自定义 annotations/constraints 来实现

Now I understand that with all these validation facilites, plus the fact that Spring is not intrusive at all and lets you do anything you want (for better or for worse), one can be tempted to use the "validation hammer" for anything vaguely related to error handling.
And it would work : with validation only, you check every possible problem in your validators/annotations (and hardly throw any exception in lower layers). It is bad, because you pray that you thought about all the cases. You don't leverage Java exceptions that would allow you to simplify your logic and reduce the chance of making a mistake by forgetting to check that something had an error.

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

So in the Spring MVC world, one should not mistake validation (that is to say, UI validation) for lower layer exceptions, such has Service exceptions or DB exceptions (key unicity, etc.).

所以在Spring MVC的世界里,千万不要把验证(也就是UI验证)误认为是低层异常,比如Service异常或者DB异常(key唯一性等)。



How to handle exceptions in Spring MVC in a handy way ?

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

Some people think "Oh god, so in my controller I would have to check all possible checked exceptions one by one, and think about a message error for each of them ? NO WAY !". I am one of those people. :-)

有些人认为“天哪,所以在我的控制器中,我必须一个一个检查所有可能的已检查异常,并考虑每个异常的消息错误?不可能!”。我是那些人的其中一个。:-)

For most of the cases, just use some generic checked exception class that all your exceptions would extend. Then simply handle it in your Spring MVC controller with @ExceptionHandlerand a generic error message.

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

Code example :

代码示例:

public class MyAppTechnicalException extends Exception { ... }

and

@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";
    }

}

Simple, quick, easy and clean !

简单,快速,容易和干净!

For those times when you need to display error messages for some specific exceptions, or when you cannot have a generic top-level exception because of a poorly designed legacy system you cannot modify, just add other @ExceptionHandlers.
Another trick : for less cluttered code, you can process multiple exceptions with

对于那些需要为某些特定异常显示错误消息的时候,或者当由于旧系统设计不佳而无法修改而无法使用通用顶级异常时,只需添加其他@ExceptionHandlers。
另一个技巧:对于不那么混乱的代码,您可以使用

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


Bottom line : when to use validation ? when to use exceptions ?

底线:何时使用验证?什么时候使用异常?

  • Errors from the UI = validation = validation facilities (JSR-303 annotations, custom annotations, Spring validator)
  • Errors from lower layers = exceptions
  • 来自 UI 的错误 = 验证 = 验证工具(JSR-303 注释、自定义注释、Spring 验证器)
  • 来自较低层的错误 = 异常

When I say "Errors from the UI", I mean "the user entered something wrong in his form".

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

References :

参考 :