我应该在哪里包装 REST 响应、控制器还是服务?

csp*_*pin 5 rest spring spring-mvc wrapper

我正在 Spring MVC 中构建一个 Web 应用程序,它有一个控制器层和一个服务层。我的响应被包装在一个自定义包装器中,其中包括有关调用结果的有效负载和元数据(无论有效负载是否成功,仅包括部分数据等),以便我可以返回 HTTP 200 并让元数据向客户端描述调用的结果。

最初,我将响应包装在调用的服务中,并让控制器仅传递响应。但是,当我需要从其他服务调用我的服务时,这种模式效果不佳,因为我必须首先解开数据,对其进行一些处理,然后再次包装并将其发送回控制器。因此,我开始在控制器中构建响应。

问题是我想让我的控制器尽可能小,以便它们作为 URL 到服务的映射清晰易读。

控制器是构建 REST 响应包装器的正确位置,还是应该将其留给服务?

Dav*_*d V 6

通过将 HTTP 相关部分放入控制器中,您将获得更好的关注点分离。这就是我实现 Spring Web 服务的方式,它对我来说非常有效。根据我的经验,当这些分离到位时,重构服务就变得非常容易。

您提到您希望控制器尽可能小。控制器往往非常简单,而服务代码往往更加复杂。因此,您可能会发现您的服务代码更易于维护,而在控制器中添加更多代码的成本非常小。复杂性分布更加均匀。

以下是将 HTTP 相关代码放入控制器中的一些其他方法会有所帮助。

  • 异常处理 - 您可能不希望通过在返回的对象中放置特殊标志来处理错误,但这就是 HTTP 的工作方式。Spring MVC 提供了一种很好的机制,可以通过注释在失败时返回 HTTP 错误@ExceptionHandler。对于这种分离来说,这是一个令人难以置信的好处。

  • HTTP 标头 - 有时,响应对象的一部分比响应 JSON 的一部分更适合作为 HTTP 标头。您的内部客户端在 POJO 上操作将比知道他们必须从 HTTP 标头中提取一些信息要容易得多。

  • 返回列表 - 有时 API 应该简单地返回一个列表。但是,作为一项规则,我从不从 HTTP 服务返回 JSON 数组作为根 JSON 对象。如果您需要返回的不仅仅是一个列表,重构内部代码比创建新 URL 或破坏现有客户端要容易得多。您可以在控制器级别将列表包装在 JSON 对象中。

  • Spring 解耦 - 我们的服务不依赖于 Spring 框架。一个常见的例外是 RestTemplate,它不是 Spring MVC 的一部分。如果我们愿意,我们的代码可以轻松地转换到另一个 Web 框架。(并不是说不会有其他大的变化,比如我们的应用程序上下文)。

我只遇到过一种情况,我不得不进一步模糊这些界限。这就是我需要区分 200(OK)和 201(Created)的时候。控制器代码经常调用一个服务方法,否则该方法不会返回任何内容(它只会在失败时抛出异常)。我必须向其中添加返回值以区分更新和创建。它不会使内部客户端代码变得更加复杂,因为调用者可以忽略返回值。但是只为控制器提供返回对象似乎有点不太理想。