服务或控制器中的 Spring DTO 验证?

Posted

技术标签:

【中文标题】服务或控制器中的 Spring DTO 验证?【英文标题】:Spring DTO validation in Service or Controller? 【发布时间】:2013-10-06 16:13:45 【问题描述】:

我正在使用 Spring 构建一个简单的 AJAX/JSON Web 服务。常见的数据流是:

  some DTO from browser

            v

Spring @Controller method

            v

  Spring @Service method

我正在寻找处理数据验证的最简单方法。

我知道 @Valid 注释在 @Controller 方法中运行良好。 为什么@Valid 不能@Service 方法中工作?

我的意思是:任何其他服务和控制器都可以使用服务方法。那么在@Service 级别进行验证不是更有意义吗?


让我们举个简单的例子:

MyDTO.java:

public class MyDTO 
   @NotNull
   public String required
   @Min(18)
   public int age;

MyServiceImpl.java:

public MyDomainObject foo(MyDTO myDTO) 
  // persist myDTO
  // and return created domain object

MyController.java:

@Autowired
MyService myService;

@Autowired     // some simple bean mapper like Dozer or Orika
Mapper mapper; // for converting domain objects to DTO

@RequestMapping(...)
public MyDomainObjectDTO doSomething(@RequestBody MyDTO myDTO) 
  mapper.map(myService.foo(myDTO), MyDomainObjectDTO.class);


服务方法接收 DTO 是常见的做法吗?

如果yes:在服务方法中验证 DTO 的最佳做法是什么? 如果no:控制器是否应该操作域对象并让服务保存该对象? (这对我来说似乎毫无用处)

在我看来,服务应该只负责数据一致性。

你是怎么解决这个问题的?

【问题讨论】:

Check preconditions in Controller or Service layer 的可能重复项 【参考方案1】:

我的回答?两者都有。

服务必须检查自己的合同的有效性。

控制器是 UI 的一部分。它应该验证和绑定以获得更好的用户体验,但服务不应该依赖它。

服务无法知道它是如何被调用的。如果将其包装为 REST 服务会怎样?

该服务还以任何 UI 都无法了解的方式了解业务逻辑违规。它需要验证以确保用例得到适当的满足。

双袋吧;两者都做。

【讨论】:

但是为什么服务不允许@Valid 注释呢? ...我明白了,服务和控制器可以在他们的类中使用(例如)不同的字段。在我的应用程序中,它总是一样的。因此,我只是通过 DTO 进入服务。 因为验证和防御性编程/错误处理之间存在差异。话虽如此,我确实将验证用于防御性编程。我会尽快发布答案。【参考方案2】:

查看我的另一个答案:Check preconditions in Controller or Service layer

如果您真的想在 Service 层中进行类似于 Spring MVC 的错误处理之类的验证,您可以使用 javax.validation 和 AspectJ(建议验证方法),这就是我所做的,因为我喜欢让反射完成这项工作和声明式编程(注解)。

Spring MVC 不需要执行 AspectJ/AOP 来进行错误处理,因为方法是通过反射(url 路由/调度)调用的。

最后,对于您的 MVC 代码,您应该知道 @Valid 在某种程度上已被非正式弃用。请考虑使用@Validated,它将利用更多javax.validation 功能。

【讨论】:

我现在在服务层使用@Validated,非常棒。只需注册 Spring 的 MethodValidationPostProcessor,然后在 Service 接口本身上放置一个 @Validated 注释,最后但并非最不重要的是,在 Service 接口中定义的方法签名上放置一些 @Valid@NotNull 注释。 ServiceImpl 保持干净。这非常有效!唯一的“问题”是,抛出的ConstraintViolationException 中的propertyPath 以整个调用层次结构为前缀,即controllerMethod.serviceMethod.dtoProperty。我必须以某种方式摆脱它。 你所说的“某种非官方弃用”是什么意思?

以上是关于服务或控制器中的 Spring DTO 验证?的主要内容,如果未能解决你的问题,请参考以下文章

Spring:DTO和服务层

Spring 应用程序中的应用程序层

根据 DTO、实体模型或其他东西验证服务层中的数据?

使用 DTO 和哈希映射的 Java 服务器端表单验证

Spring Boot 控制器 - 将 Multipart 和 JSON 上传到 DTO

我应该用Serializable标记spring mvc DTO吗? [复制]