19

我正在使用 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);
}

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

4

4 回答 4

37

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

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

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

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

Spring 有一个用于验证控制器中的对象的解决方案。

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

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

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

于 2012-08-20T16:20:35.470 回答
2

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

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

于 2012-08-13T07:32:43.717 回答
0

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

@controller
public class MyController{

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

}

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

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

于 2012-08-13T07:33:16.120 回答
0

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

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

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

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

于 2012-08-16T00:25:57.570 回答