很长一段时间,我试图找出应该在Spring MVC应用程序中进行用户输入验证的位置。在许多在线博客和教程中,我基本上读到控制器应验证用户输入,如果无效,则通过显示包含错误消息的页面来响应用户。然而,我目前对Spring和Spring MVC分层系统的理解是,Controller是应用程序逻辑(服务层)和“web世界”之间唯一的浅层接口,允许从Web使用服务层。另外,据我所知,Spring MVC仅提供合理的Controller验证工具。
如果现在验证发生在Controller中,如果稍后我想从“web世界”解开应用程序逻辑,则必须在新环境(例如使用Swing的桌面应用程序)中重新实现验证逻辑。在我看来,决定哪些操作在域对象上“有效”以及这些对象可能具有哪些“有效”状态的能力是服务层的核心部分,而不是应用程序的其他部分的关注(例如控制器)。
在这种情况下,为什么将输入验证逻辑放在控制器层而不是服务层是“好的做法”?
一种常见的方法是在两个地方进行验证。但是如果你在谈论@Valid,根据我的经验,将控制器级别提升是更好的。
它还取决于我们所讨论的验证逻辑。假设你有一个豆子:
@Data
public class MyBean {
@NotNull private UUID someId;
@NotEmpty private String someName;
}
对这个bean进行注释是有意义的 @Valid
在控制器级别,所以它甚至没有达到服务。把它放在一起没有任何好处 @Valid
关于服务方法,因为为什么你会进一步传播它,而你可以立即在控制器中决定它是否是那种有效的。
然后是第二种类型的验证:业务逻辑验证。假设对于同一个bean,someId属性是timeUUid,并且它的时间戳需要在某个事件发生后最多2天,在其他情况下,该bean应该被服务丢弃。
这似乎是一个业务逻辑验证案例,因为通过查看bean,您将无法验证它,除非您对其应用某些逻辑。
由于两种验证方法实际上都验证了不同的东西,因此很明显可以看到每个MVC组件 - 模型,视图和控制器都进行自己的验证,并且在不对其他组件引入依赖性的情况下验证它的验证应该是合理的。
至于向用户显示错误,是的, 错误 对象确实打算用于控制器级别的bean验证,但是您可以设计一些过滤器来捕获任何级别的异常,然后为用户进行相当格式化。有很多方法,我是 不确定Spring是否规定任何一个比另一个好。
根据不同的解析机制(例如,jstl或jackson或其他东西),你 可能倾向于以不同的方式处理验证。例如,传统的 jstl视图解析器 很好地使用一个使用错误的装置,而a 杰克逊左轮手枪 结合使用可能会更好 @ResponseBody 和一些捕获错误的过滤器,并将它们放在响应对象的预定义错误部分。
在我们之前的一个项目中,我们使用了非常复杂的逻辑形式的大型表单,这意味着许多验证代码。所以我们使用了第三种解决方案。对于每个控制器,我们都自动启动了一个辅助类。
例:
myview <-> MyController <- MyService <- MyDAO
^
|
MyHelper
控制器处理视图解析。服务处理从dto-s映射到模型对象以进行查看,反之亦然,DAO处理数据库事务,Helper处理包括验证在内的所有其他事务。如果现在有人想要将前端从Web更改为其他东西,那将会更容易,同时我们也不会过度使用服务实现类。