Build a REST API with Spring 4 and Java Config

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 2 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 a bit on the light side concerning documentation; what’s , the reference doesn’t go out of it’s 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 build after Spring 3.0.

3. The Java configuration

Spring Java Configuration

public class WebConfig{

The new @EnableWebMvc annotation does a number of useful things – specifically, in the case of REST, it detect 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{

   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:

@RequestMapping( value = "/foos" )
class FooController{

   IFooService service;

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

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

   @RequestMapping( method = RequestMethod.POST )
   @ResponseStatus( HttpStatus.CREATED )
   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 very simple, guava style RestPreconditions utility:

public class RestPreconditions {
    public static <T> T checkFound(final 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 delegate 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 complex. 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, in order 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 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( value = HttpStatus.BAD_REQUEST )
public class BadRequestException extends RuntimeException{
@ResponseStatus( value = 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 exist, 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

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:



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.

The implementation of this Spring REST API Tutorial can be downloaded as a working sample project.

This is an Eclipse based project, so it should be easy to import and run as it is.

I usually post about REST APIs and HTTP on Google+ - you can follow me there:

Get the eBooks and Learn to Build a Simple App
Build a Simple but Working App with Spring

, , ,

  • Josh Long

    Nice post! I love the attention to details (for example, mentioning @ExceptionHandler and the relevant JIRAs). Keep up the great work and I definitely look forward to seeing your future posts and adventures with Spring (and Spring MVC).

  • 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

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

      • Voodoorider


  • 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

    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

      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.

      • 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

          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.

          • 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

    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 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

      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 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).


      • Eugen

        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.

        • baeldung

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



  • 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.

    • baeldung

      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.

  • Thiago Henrique

    wow nice good, thank!

  • Rakesh Ranjan

    Trying to call Rest API from Application to create entity – getting error “HTTP Status 405 – Request method ‘GET’ not supported”. could anyone help me out – how to resolve this ?

    • Eugen Paraschiv

      There are a few possible reasons this could happen – but more detail is needed to figure out what’s happening – the URL mappings for one, and the way you’re sending the request. Are you working against the REST API from my github project, or maybe one of the new tutorials or did you set up a new one? If you are working from the github project – you can raise an issue on github (with a bit more detail) and I’ll take a look.

  • Binh Thanh Nguyen

    Thanks, nice post

  • karthik

    Hi to all ,

    I am new to Spring Boot ,i was face this problem of itteration but it’s resolved by using Jackson annotaions of @JsonBackReference and @JsonManagedReference and @JsonIdentityInfo after added these annotaions and bean configuration is everything fine .. but after i could not able to post the data as Json to a controller .Please suggest answer i am facing issue like

    1)Requested URL is http://localhost:8080/custMast/state (POST) that time 405 method is coming and JSON is
    “custmastCountry” : {
    “id” : 1,
    “name” : “INDIA”,
    “customerAddresses” : [ ]
    “name” : “MahaRashtra”,
    “createdOn” : null,
    “updatedOn” : null,
    “”custmastDistricts” : [ ],
    “customerAddresses” : [ ]

    It’s giving this following error like

    Caused by: java.lang.IllegalArgumentException: Multiple back-reference properties with name ‘defaultReference’

    • Eugen Paraschiv

      If you have multiple @JacksonBackReference – provide a reference name to each. Hope this helps. Cheers,

      • karthik

        Here is no option to give reference name to the annotation and can you please check the Configuration of my code is it might be problem i think so
        Here is My Configuration file for JSON in Spring BOOT

        public MappingJackson2HttpMessageConverter jackson2Converter() {
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        return converter;

        public ObjectMapper objectMapper() {
        Object objectMapper = new ObjectMapper();
        // ((ObjectMapper) objectMapper).registerModule(new Hibernate4Module());
        ((ObjectMapper) objectMapper)
        // to allow serialization of “empty” POJOs (no properties to serialize)
        // (without this setting, an exception is thrown in those cases)
        ((ObjectMapper) objectMapper)
        // to write java.util.Date, Calendar as number (timestamp):
        ((ObjectMapper) objectMapper)

        // DeserializationFeature for changing how JSON is read as POJOs:

        // to prevent exception when encountering unknown property:
        ((ObjectMapper) objectMapper)
        // to allow coercion of JSON empty String (“”) to null Object value:
        ((ObjectMapper) objectMapper)

        return (ObjectMapper) objectMapper;

        • Eugen Paraschiv

          Hey Karthik – please use pastebin for long code samples. Cheers,

          • karthik

            Here is the link for Paste bin please refer the code in the link about Spring Boot Jackson configuration

          • Eugen Paraschiv

            Looks like you pasted your Spring config, but the issue is due to your use of annotations on the DTOs. Also – in order to properly diagnose a specific exception like this one, my recommendation is to do a quick project replicating the error on github. Otherwise, there’s really no way I can diagnose the issue with certainty.

          • karthik

            Hi Eugen,

            Here is the link for a sample App “ ” please refer the issue and Configuration file .

          • Eugen Paraschiv

            Thanks for the sample – I did some digging into your code and you seem to have the annotations in the DTOs reversed – the many side – the collection (Set) should have the @JsonManagedReference and the one side should have the @JsonBackReference. After changing these, I was able to correctly consume the API. Hope it helps. Cheers,

          • karthik

            Thanks Eugen,But here i am facing a small issue while fetching a State Object it’s corresponding Country Object is not coming

          • Eugen Paraschiv

            My suggestion is – if you’re running into Jackson specific issues – no point in replicating these over REST and go through all of these layers. Instead – write a small Jackson test where you’re simply using an object mapper to serialize a State with a Country – it’s going to be much easier to reason about the code on such a test. Also – let’s move this on github – please open an issue on that sample project and we can follow up there – since it’s getting a bit to long for blog comments. Cheers,

  • Alejandro Imass

    Hi Eugen, excellent article and one of the few solid references on Spring REST (post 3.x) where you can tell the author knows what he’s talking about. Did you ever post the advanced content negotiation ones?

    • Eugen Paraschiv

      Hey Alejandro – no, I didn’t end up writing on content negotiation – mainly because it’s a subject few people are interested in. I usually tackle stuff in the order I see interest from readers (limited time, article TODO list a mile long) – so I can bump this one if you’d like some more in-depth article on the subject. The plan was to do a custom media type.
      And if you’d like to read a bit more on content negotatiation, I touched on the subject here as well as for testing here. Hope this helps. Cheers,

      • Alejandro Imass

        No, just curious because I stumbled on your article because I was looking for a solution in order to tweak the way that Spring 4 negotiates content with the container. I wound up debugging the actual Spring 4.0.5 to figure out what was going on and Spring uses this order to decided what is the response content type:

        (1) path extension (.com, .png, etc.)
        (2) via a query parameter (“format” is default name for this param)
        (3) from the Accept header.

        I would have assumed that Accept header should take precedence, after all it’s the client’s User Agent’s requirement, but no. So for example if you have a resource that is referenced by Email (e.g. /account/ the container’s Media Type definition will kick in and override the Accept header. So if you develop on say Jetty, everything will work well but when you deeply on Tomcat, surprise: you get 406 with no other hint as to what is going on (this is another topic of discussion is that Jackson swallows everything including exceptions!).

        Anyway, the reason your code won’t work in Tomcat is because this container comes with a bunch of pre-defined MIME and the Email URL will trigger this one:


        The is NOTHING in the logs that will tell you what is going on, just this:

        org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation

        I actually had to download and debug Spring 4.0.5 to determine that the chosen media type was “application/x-msdownload” I mean out of all things “application/x-msdownload”!!! I think that at the very least ContentNegotiationManager should be a little more verbose in Debug mode and tell you what is going on and not just return 406 whiteout specifying exactly why.

        Anyway, Servlet code should be so sensitive to the container or the container’s configuration like MIME, so after some more research I was able to configure/tweak the ContentNegotiationManager to be a little bit more sensible for REST services. So here is my config in case someone else runs into this:


        • Eugen Paraschiv

          The difference between Tomcat and Jetty is good to know. I usually stay away from adding media type semantics to the URI of the resource, which is probably why I was lucky enough to not run into this particular bug. However, Jackson issues – especially during content negotiation – are plenty – and debugging Spring itself is really the only valid way of dealing with them. It’s also a very good exercise to learn the platform.
          There are set places where you can easily catch the exception (for example in the DispatcherServlet) – and then you can do a second pass with the debug set to break on that particular exception – that’s a quick way to get right to the root of issues like this.
          But – ultimately – it’s a useful process – thanks for sharing a good default here for people that may not have the option to only cleanly use the Accept header.

          • Alejandro Imass

            “I usually stay away from adding media type semantics to the URI of the resource,”

            I did not design the API (just re-wrote it in Spring) but I agree it’s an awful design. It should have been at the very least a query parameter to avoid media type confusion. Many people say they are “RESTful” just because the use minimal HTTP semantics such as GET and POST without even understanding the mere basics of the representational state transfer architectural style. I plan to re-design this API completely in the future and get rid of crap like this but in the mean time I have to replicate the old API until we can re-write the client code.

            Thanks again for the wonderful article which I helped me kick-start the Spring migration of this service.

  • Rai Singh

    Hi! Love your articles and your tutorials are top notch.


    From your spring-security-rest-full github project is the following abstract class and interface it implements your style of pushing custom persistence-related methods up to the service layer versus creating custom behaviors globally across repositories at the persistence layer as is shown here? If so, do you prefer this method for any particular reason?
    Hope my question makes sense.


    • Eugen Paraschiv

      Hey Rai – my view on this is that it’s pretty much the same thing – since at the end, you have a single definition application to your entire API – so I don’t have any strong preference towards one or the other, as long as you only have a single definition and so no duplication. Cheers,