【问题标题】:Check preconditions in Controller or Service layer检查控制器或服务层中的先决条件
【发布时间】:2012-08-13 07:22:09
【问题描述】:

我正在使用 Google 的 Preconditions 类来验证用户的输入数据。
但我担心使用 Preconditions 类检查用户输入数据的最佳点在哪里。
首先,我在 Controller 中编写了验证检查代码,如下所示:

@Controller
...
public void register(ProductInfo data) {
    Preconditions.checkArgument(StringUtils.hasText(data.getName()),
        "Empty name parameter.");
    productService.register(data);
}

@Service
...
public void register(ProductInfo data) {
    productDao.register(data);
}

但我认为服务层中的register 方法会使用另一个控制器方法,如下所示:

@Controller
...
public void register(ProductInfo data) {
    productService.register(data);
}
public void anotherRegister(ProductInfo data) {
    productService.register(data);
}

@Service 
...
public void register(ProductInfo data) {
    Preconditions.checkArgument(StringUtils.hasText(data.getName()),
        "Empty name parameter.");
    productDao.register(data);
}

另一方面,服务层的方法将只在一个控制器中使用。
我很困惑。在控制器或服务中检查先决条件的更好方法是什么?
提前致谢。

【问题讨论】:

    标签: spring validation service controller


    【解决方案1】:

    理想情况下,您会在两个地方都这样做。但是您混淆了两件事:

    • 验证(带错误处理)
    • 防御性编程(又名断言,又名合同设计)。

    您绝对应该在控制器中进行验证,并在您的服务中进行防御性编程。这就是原因。

    您需要对表单和 REST 请求进行验证,以便将合理的错误发送回客户端。这包括哪些字段不好,然后对错误消息进行本地化等...(如果 ProductInfo.name 属性为空,您当前的示例将向我发送带有堆栈跟踪的可怕 500 错误消息)。

    Spring 在控制器中有一个solution for validating objects

    防御性编程是在服务层完成的,但不是验证,因为您无权访问语言环境来生成正确的错误消息。有些人会这样做,但 Spring 并不能真正帮助您。

    在服务层中没有进行验证的另一个原因是 ORM 通常已经通过 JSR Bean 验证规范(休眠)进行了验证,但它不会生成合理的错误消息。

    人们采用的一种策略是创建自己的前置条件实用程序库,该库抛出自定义派生的RuntimeExceptions,而不是 guava(和 commons lang)IllegalArgumentExceptionIllegalStateException,然后是 try...catch控制器中的异常将它们转换为验证错误消息。

    【讨论】:

      【解决方案2】:

      没有“更好”的方法。如果您认为该服务将被多个控制器(或其他代码段)使用,那么在那里进行检查可能很有意义。如果您的应用程序在无效请求仍在控制器中时检查它们很重要,那么在那里进行检查可能很有意义。正如您所注意到的,这两者并不相互排斥。您可能需要检查两次才能涵盖这两种情况。

      另一种可能的解决方案:使用 Bean Validation (JSR-303) 将检查(前提条件)放在 ProductInfo bean 本身上。这样您只需指定一次检查,任何需要的东西都可以快速验证 bean。

      【讨论】:

      • 绝对同意您应该使用 bean 验证,特别是如果您的 DAO 使用 ORM。
      【解决方案3】:

      我认为在您的特殊情况下,您需要在服务层检查它并在数据完整性错误的情况下向控制器返回异常。

      @controller
      public class MyController{
      
      @ExceptionHandler(MyDataIntegrityExcpetion.class)
      public String handleException(MyDataIntegrityExcpetion ex, HttpServletRequest request) {
        //do someting on exception or return some view. 
      }
      
      }
      

      这也取决于你在控制器中做什么。无论您返回 View 还是仅使用 @ResponseBody Annotation。 Spring MVC 为输入/数据验证提供了很好的“开箱即用”解决方案,我建议您检查这个库。

      http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/validation.html

      【讨论】:

        【解决方案4】:

        前置条件、验证,无论是简单的还是业务的,都应该在过滤层或拦截器处理,甚至在到达控制器或服务层之前。

        如果您在控制器层检查它的危险,您违反了控制器的单一职责原则,其唯一目的是委托请求和响应。

        在服务层设置前置条件,就是将横切关注点引入核心业务。

        过滤器或接收器就是为此目的而构建的。在过滤器层或拦截器中放置先决条件还允许您“挑选并匹配”您可以为每个 servlet 请求放置在堆栈中的规则,因此不会将特定规则限制为仅一个 servlet 请求或引入重复。

        【讨论】:

          猜你喜欢
          • 2019-02-06
          • 2011-07-10
          • 2023-02-22
          • 2014-02-19
          • 2017-07-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多