我们都知道,Spring MVC通常与Hibernate Validator和JSR-303很好地集成。但正如有人所说,Hibernate Validator只适用于Bean Validation,这意味着应该将更复杂的验证推送到数据层。此类验证的示例:业务密钥唯一性,内部记录依赖性(通常指向数据库设计问题,但我们都生活在一个不完美的世界)。即使像字符串字段长度这样的简单验证也可能由某个DB值驱动,这使得Hibernate Validator无法使用。
所以我的问题是,Spring或Hibernate或JSR是否提供了执行此类复杂验证的功能?是否有一些已建立的模式或技术部分在基于Spring和Hibernate的标准Controller-Service-Repository设置中执行此类验证?
更新:让我更具体一点。例如,有一个表单向控制器的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;
}
您能提供更优化的方法吗?
答案 0 :(得分:39)
是的,有一个很好的旧的Java模式异常抛出 Spring MVC很好地集成了它(对于代码示例,你可以直接跳到我的答案的第二部分)。
您所谓的“复杂验证”实际上是例外:业务密钥单一性错误,低层或DB错误等。
验证应该在表示层上进行。它基本上是关于验证提交的表单字段。
我们可以将它们分为两类:
1)光验证(使用JSR-303 / Hibernate验证):检查提交的字段是否具有给定的@Size
/ @Length
,即{{1} }}或@NotNull
/ @NotEmpty
,检查它是否有@NotBlank
格式等。
2)重度验证或复杂验证更多是关于字段验证的特定情况,例如跨领域验证:
@Email
,fieldA
和fieldB
。单独地,每个字段可以为空,但至少其中一个字段不能为空。fieldC
字段的值低于18,则userAge
字段不得为空,且responsibleUser
的年龄必须超过21。可以使用Spring Validator implementations或custom annotations/constraints实现这些验证。
现在我明白了所有这些验证设施,加上Spring根本没有侵入性并让你做任何你想要的事情(无论好坏),人们可能会想要使用“验证锤”与错误处理有关的任何事情。
并且它可以工作:仅通过验证,您可以检查验证器/注释中的每个可能的问题(并且几乎不会在较低层中抛出任何异常)。这很糟糕,因为你祈祷你想到了所有的情况。您不会利用允许您简化逻辑的Java异常,并通过忘记检查某些错误来减少出错的可能性。
所以在Spring MVC世界中,不应该对下层异常进行验证(即 UI验证),例如服务异常或数据库异常(关键的单一性等)。
有些人认为“哦,上帝,所以在我的控制器中,我必须逐个检查所有可能的检查异常,并考虑每个人的消息错误?没有办法!”。我是那些人的其中一个。 : - )
对于大多数情况,只需使用一些通用的检查异常类,所有异常都会扩展。然后只需使用@ExceptionHandler和一般错误消息在Spring MVC控制器中处理它。
代码示例:
responsibleUser
和
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
当我说“用户界面的错误”时,我的意思是“用户在表单中输错了”。
参考文献: