Spring MVC + Hibernate:数据验证策略 [英] Spring MVC + Hibernate: data validation strategies

查看:129
本文介绍了Spring MVC + Hibernate:数据验证策略的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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或Hibernate还是JSR提供的东西执行这样复杂的验证?是否有一些已建立的模式或技术部分在基于Spring和Hibernate的标准Controller-Service-Repository设置中执行此类验证?

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

更新:让我更具体一点。例如,有一个表单向控制器的 save 方法发送AJAX保存请求。如果发生一些验证错误 - 简单或复杂 - 我们应该回到浏览器,其中一些json指示有问题的字段和相关错误。对于简单错误,我可以从 BindingResult 中提取字段(如果有)和错误消息。您会针对复杂错误提出什么样的基础设施(可能是特定的,而不是临时的例外?)?使用异常处理程序对我来说似乎不是一个好主意,因为在 save 方法和 @ExceptionHandler 让事情变得复杂。目前我使用了一些临时异常(例如, ValidationException ):

UPDATE: Let me be more specific. For example, there's a form which sends an AJAX save request to the controller's save method. 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 save method and @ExceptionHandler makes things intricate. Currently I use some ad-hoc exception (like, 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?

推荐答案

是的,有一个很好的旧的Java模式异常抛出

Spring MVC将它整合得很漂亮(对于代码示例,您可以直接跳到我的答案的第二部分)。

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

您所谓的复杂验证实际上是异常:业务键unicity错误,低层或DB错误等。

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

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

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

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

We could classify them into two kinds :

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

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

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

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


  • 示例1:表单 fieldA fieldB fieldC 。单独地,每个字段可以为空,但至少其中一个字段不能为空。

  • 示例2:如果 userAge 字段已包含值小于18, responsibleUser 字段不得为空且 responsibleUser 的年龄必须超过21岁。

  • Example 1 : The form has fieldA, fieldB and fieldC. Individually, each field can be empty, but at least one of them must not be empty.
  • Example 2 : if userAge field has a value under 18, responsibleUser field must not be null and responsibleUser's age must be over 21.

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

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

现在我明白了所有这些验证设施,加上Spring根本没有侵入性,让你做任何你想做的事情(无论好坏),一个人可以很想使用验证锤来处理与错误处理有关的任何事情。

它可以工作:只有验证,你检查验证器/注释中的每个可能的问题(并且几乎没有在较低层中排除任何异常)。这很糟糕,因为你祈祷你想到了所有的情况。您不会利用允许您简化逻辑的Java异常,并且忘记检查某些内容是否有错误,从而减少出错的可能性。

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 MVC世界中,不应该对下层异常进行验证(也就是说, UI验证),例如服务异常或数据库异常(关键单一性等) 。)。

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

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

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. :-)

对于大多数情况,只需使用一些通用的检查异常类,所有异常都会扩展。然后使用 @ ExceptionHandler 和一般错误消息。

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 @ExceptionHandler and a generic error message.

代码示例:

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

}

简单,快捷,简单和干净!

Simple, quick, easy and clean !

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

另一个技巧:对于较少混乱的代码,您可以使用

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

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






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


  • 来自UI =验证=验证工具的错误(JSR-303注释,自定义注释,Spring验证器)

  • 来自较低层的错误=例外

  • 当我说来自UI的错误时,我的意思是用户在表单中输错了。

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

    参考文献:

    • Passing errors back to the view from the service layer
    • Very informative blog post about bean validation

    这篇关于Spring MVC + Hibernate:数据验证策略的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆