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

Spring 我应该在哪里包装我的REST响应,控制器还是服务?,spring,rest,spring-mvc,wrapper,Spring,Rest,Spring Mvc,Wrapper,我正在SpringMVC中构建一个web应用程序,它有一个控制器层和一个服务层。我的响应被包装在一个自定义包装器中,该包装器包括有效负载和关于调用结果的元数据(无论有效负载是否成功,只包括部分数据等),这样我就可以返回HTTP 200,并让元数据向客户机描述调用的结果 最初,我将响应包装在被调用的服务中,让控制器只传递响应。但是,当我需要从其他服务调用我的服务时,这种模式不能很好地工作,因为我必须首先打开数据,对其进行一些处理,然后再次包装并将其发送回控制器。因此,我开始在控制器中构建响应 问题

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

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

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

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

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

是的,这就是拥有不同层次的意义:分离出不同的关注点。 控制器虽然在大多数情况下是servicelayer的唯一使用者,但它只是其中之一。服务必须不知道消费者对其数据做了什么。想想
元服务
消费和处理多个服务的数据。如果这些服务将接收包装好的数据,它们将不得不打开数据进行处理。这是设计不好的迹象

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


因此,您有控制器,用于启动检索和包装结果,以及用于检索数据的服务:明确分离关注点

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

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

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

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

  • HTTP头-有时响应对象的一部分作为HTTP头比作为响应JSON的一部分更合适。您的内部客户机在POJO上操作要比从HTTP头中提取一些信息容易得多

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

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


我只遇到过一种情况,我不得不把这些线再模糊一点。这时我需要区分200(确定)和201(创建)。控制器代码通常调用一个服务方法,否则它不会返回任何东西(它只会在失败时抛出异常)。我不得不向这些文件中添加返回值,以区分更新和创建。它不会使内部客户端代码变得更复杂,因为调用方可以忽略返回值。但是让返回对象只用于控制器似乎有点不理想。

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