Where should I wrap my REST Response, the controller or the service?
Asked Answered
S

3

5

I'm building a web app in Spring MVC that has a controller layer and a service layer. My responses are wrapped in a custom wrapper that includes the payload and metadata as to the outcome of the invocation (whether or not the payload was successful, includes only partial data, etc.) so that I can return an HTTP 200 and let the metadata describe the outcome of the invocation to the client.

Originally I was wrapping the response in the service that was invoked and having the controller just pass along the response. However, this pattern doesn't work well when I need to call my service from other services, because I have to first unwrap the data, do some work on it, then wrap it again and send it back to the controller. Because of this, I began building the response in the controllers instead.

The problem is that I want to keep my controllers as small as possible so that they are clear and readable as a mapping of urls to services.

Are the controllers the proper place to build the REST response wrapper, or should I leave it to the services?

Steerage answered 21/3, 2014 at 22:20 Comment(0)
L
6

You will obtain better separation of concerns by putting the HTTP-related parts into your controllers. This is how I implement my Spring web services and it works very well for me. From my experience, it becomes very easy to refactor services when these separations are in place.

You mentioned that you like to keep your controllers as small as possible. Controllers tend to be incredibly simple and service code tends to be more complex. Thus, you will probably find that your service code is simpler to maintain at the very minor expense of having more code in the controllers. The complexity is more evenly distributed.

Here are some other ways in which putting HTTP-related code into the controller helps.

  • Exception Handling - You probably don't want to handle errors by putting special flags in your returned objects, but this is how HTTP works. Spring MVC provides a great mechanism for returning HTTP errors when failures by means of the @ExceptionHandler annotation. This is an incredible benefit to this separation.

  • HTTP Headers - Sometimes what is part of a response object is more appropriate as an HTTP Header than part of the response JSON. Your internal clients will have a much easier time operating on a POJO than knowing that they have to extract some information from an HTTP Header.

  • Returning lists - Sometimes an API should simply return a list. But, as a rule I never return a JSON array as the root JSON object from an HTTP service. In the event that you need to return more than a list, refactoring the internal code is much easier than having to make a new URL, or breaking existing clients. You can wrap your list in a JSON object at the controller level.

  • Spring Decoupling - Our services don't have dependencies on the Spring Framework. The one common exception is RestTemplate, which is not part of Spring MVC. Our code could easily be transitioned to another web framework, if we wished. (Not that there wouldn't be other large changes, like our application context).

There is only one case I've run into where I've had to blur these lines a little more. That is when I need to distinguish between 200 (OK) and 201 (Created). The controller code often calls a service method which otherwise would not return anything (it would only throw an exception on failures). I have had to add return values to these to distinguish between an update and a create. It does not make internal client code more complicated since callers can ignore the return value. But having the return object just for the controller seems a little less than ideal.

Lenssen answered 22/3, 2014 at 1:28 Comment(0)
C
1

Are the controllers the proper place to build the REST response wrapper, or should I leave it to the services?

Yes, that is the point of having different layers: to separate out different concerns. A Controller is only one - though most of the time the only - consument of the servicelayer. The Service, has to have no knowledge whatever a consument is doing with its data. Think of Meta-Services consuming and processing the data of several services. If these services would receive wrapped data, they would have to unwrap the data to work on. That is a sign of bad design.

The controller (under Spring @Controller) is the interface to the Web-world. This is the right place to wrap the data in whatever format you want: it goes over the wire, so it is necessarily wrapped in one way or the other. The consumers expect wrapped data.

So you have controllers, which initiate the retrieval and wrap results and services which retrieve data: a clear separation of concerns.

Cadal answered 22/3, 2014 at 0:35 Comment(0)
K
1

Think of the following case: Say some new external consumer of your data needs the exact same data that you are returning in your REST Controller (assuming you are returning JSON) but can only handle XML. What would you rather do? Write a new service layer that would return XML if your current one returns JSON, or just write a new Controller method that will return the XML from the generic service layer?

Kvass answered 22/3, 2014 at 14:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.