I just announced the new Spring Boot 2 material, coming in REST With Spring:

>> CHECK OUT THE COURSE

Table of Contents

1. Overview

This article shows how to set up REST in Spring – the Controller and HTTP response codes, configuration of payload marshalling and content negotiation.

2. Understanding REST in Spring

The Spring framework supports two ways of creating RESTful services:

  • using MVC with ModelAndView
  • using HTTP message converters

The ModelAndView approach is older and much better documented, but also more verbose and configuration heavy. It tries to shoehorn the REST paradigm into the old model, which is not without problems. The Spring team understood this and provided first-class REST support starting with Spring 3.0.

The new approach, based on HttpMessageConverter and annotations, is much more lightweight and easy to implement. Configuration is minimal, and it provides sensible defaults for what you would expect from a RESTful service. It is, however, newer and a bit on the light side concerning documentation – the reference doesn’t go out of its way to make the distinction and the tradeoffs between the two approaches as clear as they should be. Nevertheless, this is the way RESTful services should be built after Spring 3.0.

3. The Java configuration

Spring Java Configuration
@Configuration
@EnableWebMvc
public class WebConfig{
   //
}

The new @EnableWebMvc annotation does some useful things – specifically, in the case of REST, it detects the existence of Jackson and JAXB 2 on the classpath and automatically creates and registers default JSON and XML converters. The functionality of the annotation is equivalent to the XML version:

<mvc:annotation-driven />

This is a shortcut, and though it may be useful in many situations, it’s not perfect. When more complex configuration is needed, remove the annotation and extend WebMvcConfigurationSupport directly.

4. Testing the Spring Context

Starting with Spring 3.1, we get first-class testing support for @Configuration classes:

@RunWith( SpringJUnit4ClassRunner.class )
@ContextConfiguration( 
  classes = { ApplicationConfig.class, PersistenceConfig.class },
  loader = AnnotationConfigContextLoader.class )
public class SpringTest {

   @Test
   public void whenSpringContextIsInstantiated_thenNoExceptions(){
      // When
   }
}

The Java configuration classes are simply specified with the @ContextConfiguration annotation, and the new AnnotationConfigContextLoader loads the bean definitions from the @Configuration classes.

Notice that the WebConfig configuration class was not included in the test because it needs to run in a Servlet context, which is not provided.

5. The Controller

The @Controller is the central artifact in the entire Web Tier of the RESTful API. For the purpose of this post, the controller is modeling a simple REST resource – Foo:

@Controller
@RequestMapping("/foos")
class FooController {

   @Autowired
   private IFooService service;

   @RequestMapping(method = RequestMethod.GET)
   @ResponseBody
   public List<Foo> findAll() {
       return service.findAll();
   }

   @RequestMapping(value = "/{id}", method = RequestMethod.GET)
   @ResponseBody
   public Foo findOne(@PathVariable("id") Long id) {
       return RestPreconditions.checkFound( service.findOne( id ));
   }

   @RequestMapping(method = RequestMethod.POST)
   @ResponseStatus(HttpStatus.CREATED)
   @ResponseBody
   public Long create(@RequestBody Foo resource) {
       Preconditions.checkNotNull(resource);
       return service.create(resource);
   }

   @RequestMapping(value = "/{id}", method = RequestMethod.PUT)
   @ResponseStatus(HttpStatus.OK)
   public void update(@PathVariable( "id" ) Long id, @RequestBody Foo resource) {
       Preconditions.checkNotNull(resource);
       RestPreconditions.checkNotNull(service.getById( resource.getId()));
       service.update(resource);
   }

   @RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
   @ResponseStatus(HttpStatus.OK)
   public void delete(@PathVariable("id") Long id) {
       service.deleteById(id);
   }

}

You may have noticed I’m using a straightforward, Guava style RestPreconditions utility:

public class RestPreconditions {
    public static <T> T checkFound(T resource) {
        if (resource == null) {
            throw new MyResourceNotFoundException();
        }
        return resource;
    }
}

The Controller implementation is non-public – this is because it doesn’t need to be.

Usually, the controller is the last in the chain of dependencies – it receives HTTP requests from the Spring front controller (the DispathcerServlet) and simply delegates them forward to a service layer. If there is no use case where the controller has to be injected or manipulated through a direct reference, then I prefer not to declare it as public.

The request mappings are straightforward – as with any controller, the actual value of the mapping, as well as the HTTP method, are used to determine the target method for the request. @RequestBody will bind the parameters of the method to the body of the HTTP request, whereas @ResponseBody does the same for the response and return type.

They also ensure that the resource will be marshalled and unmarshalled using the correct HTTP converter. Content negotiation will take place to choose which one of the active converters will be used, based mostly on the Accept header, although other HTTP headers may be used to determine the representation as well.

6. Mapping the HTTP Response Codes

The status codes of the HTTP response are one of the most important parts of the REST service, and the subject can quickly become very complicated. Getting these right can be what makes or breaks the service.

6.1. Unmapped Requests

If Spring MVC receives a request which doesn’t have a mapping, it considers the request not to be allowed and returns a 405 METHOD NOT ALLOWED back to the client.

It is also good practice to include the Allow HTTP header when returning a 405 to the client, to specify which operations are allowed. This is the standard behavior of Spring MVC and does not require any additional configuration.

6.2. Valid, Mapped Requests

For any request that does have a mapping, Spring MVC considers the request valid and responds with 200 OK if no other status code is specified otherwise.

It is because of this that controller declares different @ResponseStatus for the create, update and delete actions but not for get, which should indeed return the default 200 OK.

6.3. Client Error

In the case of a client error, custom exceptions are defined and mapped to the appropriate error codes.

Simply throwing these exceptions from any of the layers of the web tier will ensure Spring maps the corresponding status code on the HTTP response.

@ResponseStatus(HttpStatus.BAD_REQUEST)
public class BadRequestException extends RuntimeException {
   //
}
@ResponseStatus(HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
   //
}

These exceptions are part of the REST API and, as such, should only be used in the appropriate layers corresponding to REST; if for instance, a DAO/DAL layer exists, it should not use the exceptions directly.

Note also that these are not checked exceptions but runtime exceptions – in line with Spring practices and idioms.

6.4. Using @ExceptionHandler

Another option to map custom exceptions on specific status codes is to use the @ExceptionHandler annotation in the controller. The problem with that approach is that the annotation only applies to the controller in which it is defined, not to the entire Spring Container, which means that it needs to be declared in each controller individually.

This quickly becomes cumbersome, especially in more complex applications which many controllers. There are a few JIRA issues opened with Spring at this time to handle this and other related limitations: SPR-8124, SPR-7278, SPR-8406.

7. Additional Maven dependencies

In addition to the spring-webmvc dependency required for the standard web application, we’ll need to set up content marshalling and unmarshalling for the REST API:

<dependencies>
   <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>${jackson.version}</version>
   </dependency>
   <dependency>
      <groupId>javax.xml.bind</groupId>
      <artifactId>jaxb-api</artifactId>
      <version>${jaxb-api.version}</version>
      <scope>runtime</scope>
   </dependency>
</dependencies>

<properties>
   <jackson.version>2.4.0</jackson.version>
   <jaxb-api.version>2.2.11</jaxb-api.version>
</properties>

These are the libraries used to convert the representation of the REST resource to either JSON or XML.

8. Conclusion

This tutorial illustrated how to implement and configure a REST Service using Spring 4 and Java based configuration, discussing HTTP response codes, basic Content Negotiation and marshaling.

In the next articles of the series, I will focus on Discoverability of the API, advanced content negotiation and working with additional representations of a Resource.

All the code of this article is available over on Github. This is a Maven-based project, so it should be easy to import and run as it is.

I just announced the new Spring Boot 2 material, coming in REST With Spring:

>> CHECK OUT THE LESSONS

newest oldest most voted
wojtek
Guest
wojtek

What do I have to change to be able to GET all Foo’s using application/xml media type? Now i am getting 406 error. Only JSON works fine

Eugen Paraschiv
Guest

I will look into it. Also, you can always open up an issue in github. Thanks.

Voodoorider
Guest
Voodoorider

Done.

Jack
Guest
Jack

This is maybe the best run though of spring 3.1 (and webservices) i have yet seen. Indeed, the level of detail is great. Keep it up1

Rodrigo Reyes
Guest
Rodrigo Reyes

Is there anyway to map DAO level exceptions to REST status code without using a controller specific @ExceptionHandler? I would like to configure that system wide.

Eugen Paraschiv
Guest

See the existing custom exceptions in the project (in github – link is at the end of the article); these are system wide and they map status codes to exceptions.
Eugen.

Rodrigo Reyes
Guest
Rodrigo Reyes

Yes, I saw them. Problem is, I want to map Spring exceptions (Ex. JpaObjectRetrievalFailureException) to status codes. Anyway to do that?

PS: Excelent articles 🙂 Thanks for the contribution.

Eugen Paraschiv
Guest

No way to map already existing exceptions to HTTP status codes as far as I am aware of. Also, the standard way to achieve what you’re trying to do (and a common pattern in Spring) is exception translation – catch the exceptions you know the underlying code throws and you wrap and re throw them in your own exceptions.
Hope this helps.
Eugen.

Rodrigo Reyes
Guest
Rodrigo Reyes

Thanks, Eugen. I hoped there was some declarative way of handling this system wide since it needs to be done in several places. Thanks anyway.

brian
Guest
brian

Question on the @ResponseStatus – I tried adding a reason to your BadRequestException (see my change below). I wanted to see the reason propagated to the client Response, but I don’t see it there. I set a breakpoint in your FooRESTIntegrationTest.java at the method givenResourceDoesNotExist_whenResourceIsDeleted_then404IsReceived on the assert and it just shows the response.statusLine=”HTTP/1.1 404 Not Found” where I would expect it to be “HTTP/1.2 404 BadRequestExceptionTest”).

@ResponseStatus( value = HttpStatus.BAD_REQUEST, reason=”BadRequestExceptionTest”)
public final class BadRequestException extends RuntimeException{
//
}

brian
Guest
brian

Apologies, I was testing with your older version of the project. I loaded the new version and also can reproduce same issue as follows: I modified your
ConflictException as follows, just the one line to add reason:
@ResponseStatus(value = HttpStatus.CONFLICT, reason = “ConflictExceptionReason”)

And then set breakpoint in your UserLogicRESTIntegrationTest.java at method whenUserIsCreatedWithNewRole_then409IsReceived at the response and don’t see the reason changed. It is “HTTP/1.1 409 Conflict” and would expect “HTTP/1.1 409 ConflictExceptionReason”.

Thanks for any reply if you have the time, Otherwise I understand (it is a small issue, but I’m thinking it may just be a configuration).

brian

Eugen Paraschiv
Guest

Thanks for the detailed feedback. Please go ahead and open up a new issue in github and I will deal with it as soon as possible.
Thanks.
Eugen.

Eugen Paraschiv
Guest

Sorry for the long delay, the issue is now closed.

Thanks.

Eugen.

Knesek
Guest
Knesek

Thanks for great post. For some reason, if methods are marked as final, Spring injection doesn’t seem to work for controllers in my configuration (injected beans are null in controller). Couldn’t figure out why. Just wanted to point that out if someone stumbles on the same problem.

Eugen Paraschiv
Guest

If methods are final, then Spring is no longer able to proxy them – it’s likely that, in your configuration, your controllers are implementing an interface, in which case, the controllers are peroxided via JDK proxies. The quick fix is to architect your controllers to not implement any interface.