我应该在哪里包装我的REST响应,控制器或服务?

时间:2014-03-21 22:20:19

标签: spring rest spring-mvc wrapper

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

最初我将响应包装在调用的服务中,并让控制器只传递响应。但是,当我需要从其他服务调用我的服务时,这种模式不能很好地工作,因为我必须首先打开数据,对它做一些工作,然后再次包装并将其发送回控制器。因此,我开始在控制器中构建响应。

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

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

3 个答案:

答案 0 :(得分:3)

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

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

以下是将HTTP相关代码放入控制器的其他一些方法。

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

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

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

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

只有一个案例我已经遇到了我不得不模糊这些线条的地方。那时我需要区分200(OK)和201(Created)。控制器代码通常调用一个服务方法,否则不会返回任何内容(它只会在失败时抛出异常)。我必须为这些添加返回值以区分更新和创建。它不会使内部客户端代码更复杂,因为调用者可以忽略返回值。但是只为控制器设置返回对象似乎不太理想。

答案 1 :(得分:1)

  

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

是的,这就是拥有不同层次的关键点:分离出不同的问题。 控制器只有一个 - 尽管大部分时间是唯一的 - 服务层的消耗。无论消费者对其数据做了什么,服务部门都不得知道。考虑Meta-Services消费和处理多个服务的数据。如果这些服务将接收包装数据,则他们必须打开数据才能处理。这是糟糕设计的标志。

控制器(在Spring @Controller下)是Web世界的接口。这是以您想要的任何格式包装数据的正确位置:它通过线路,因此它必须以某种方式包装。消费者希望包装数据。

所以你有控制器,它启动检索并包装检索数据的结果和服务:明确区分关注点。

答案 2 :(得分:1)

考虑以下情况:假设您的数据的一些新的外部使用者需要您在REST控制器中返回的完全相同的数据(假设您正在返回JSON)但只能处理XML。 你还想做什么?编写一个新的服务层,如果当前的返回JSON,将返回XML,或者只编写一个新的Controller方法,该方法将从通用服务层返回XML?