在控制器或服务层的春季 MVC 中进行验证?

2022-09-01 11:02:23

在很长一段时间里,我试图弄清楚在Spring MVC应用程序中应该在哪里进行用户输入的验证。在许多在线博客和教程中,我基本上读到控制器应该验证用户输入,如果无效,则通过显示包含错误消息的页面来响应用户。然而,我目前对Spring和Spring MVC分层系统的理解是,控制器是应用程序逻辑(服务层)和“Web世界”之间的唯一浅接口,允许从Web使用服务层。另外,据我所知,Spring MVC只为控制器中的验证提供了合理的工具。

如果现在验证在控制器中进行,如果在稍后的某个时刻我想从“Web世界”中解绑应用程序逻辑,则必须在新环境中重新实现验证逻辑(例如,使用Swing的桌面应用程序)。在我看来,决定哪些操作对域对象是“有效的”,以及这些对象可能具有哪些“有效”状态的能力是服务层的核心部分,而不是应用程序的其他部分(例如控制器)所关注的问题。

在这种情况下,为什么将输入验证逻辑放在控制器层而不是服务层是“好的做法”?


答案 1

一种常见的方法是在这两个地方进行验证。但是,如果您正在谈论@Valid,根据我的经验,将控制器级别放在一起会更好。

这也取决于我们正在谈论的验证逻辑。假设您有一个豆子:

@Data
public class MyBean {
    @NotNull private UUID someId;
    @NotEmpty private String someName; 
}

在控制器级别对此 Bean 进行注释是有意义的,这样它甚至不会到达服务。将 on 放在服务方法上没有任何好处,因为为什么还要进一步传播它,而您可以立即在控制器中决定它是否有效。@Valid@Valid

然后还有第二种类型的验证:业务逻辑验证。假设对于同一个 bean,someId 属性是 timeUUid,其时间戳最多需要在某些事件发生后 2 天,在其他情况下,该 Bean 应该被服务丢弃。

这似乎是一个业务逻辑验证案例,因为仅通过查看Bean,您将无法对其进行验证,除非您对它应用一些逻辑。

由于这两种验证方法实际上验证了不同的东西,因此很明显,您的每个MVC组件(模型,视图和控制器)都执行自己的验证,并且它应该合理地验证它所验证的内容,而不会引入对其他组件的依赖关系。

至于向用户显示错误,是的,Errors对象确实打算用于控制器级别的Bean验证,但是您可以设计一些过滤器来捕获任何级别的异常,然后为用户设置其格式。有很多方法可以解决它,我不确定Spring是否规定任何方法都比另一种更好

根据不同的解析机制(例如,jstl或jackson或其他),您可能倾向于以不同的方式处理验证。例如,传统的 jstl 视图解析器可以很好地与使用 Errors 的装置配合使用,而 jackson 解析器可能会更好地使用@ResponseBody和一些过滤器来捕获错误并将其放在响应对象的预定义错误部分中。


答案 2

在我们之前的一个项目中,我们有巨大的表单和非常复杂的逻辑,这意味着大量的验证代码。所以我们使用了第三种解决方案。对于每个控制器,我们自动连接了一个帮助程序类。例:

myview <-> MyController <- MyService <- MyDAO
                 ^
                 |
               MyHelper

Controllers处理视图解析。
处理从dto-s到视图模型对象的映射,反之亦然,
处理数据库事务,
并处理包括验证在内的其他所有事情。ServicesDAO-sHelpers

如果现在有人想将前端从Web更改为其他内容,那将变得容易得多,同时,我们并没有过度膨胀服务实现类。


推荐