Jackson Top

Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:

>> CHECK OUT THE COURSE

1. Introduction

The content type indicates how to interpret the data present in the request/response. Whenever a controller receives a web request, it consumes or produces some media types. In this request-response model, several media types can be consumed/produced, and JSON is one of them.

In this quick tutorial, we're going to look at different ways to set the content type in Spring MVC.

2. @RequestMapping in Spring

Simply put, @RequestMapping is an important annotation that maps web requests to a Spring controller. It has various attributes including HTTP method, request parameters, headers, and media types.

Generally, media types fall into two categories: consumable and producible. Other than these two, we can also define a custom media type in Spring. The main purpose is, to restrict the primary mapping, to a list of media types for our request handler.

2.1. Consumable Media Type

With the consumes attribute, we specify the media type that the controller will accept from a client. We can provide a list of media types too. Let's define a simple endpoint:

@RequestMapping(value = "/greetings", method = RequestMethod.POST, consumes="application/json")
public void addGreeting(@RequestBody ContentType type, Model model) {
    // code here
}

If a client specifies a media type that is unable to consume by resource, the system will generate an HTTP “415 Unsupported Media Type” error.

2.2. Producible Media Type

As opposed to the consumes attribute, produces specifies the media type a resource can produce and send back to the client. Without a doubt, we can use a list of options. If a resource is unable to produce the requested resource, the system will generate an HTTP “406 Not Acceptable” error.

Let's start with a simple example – an API exposing a JSON string.

Here's our endpoint:

@RequestMapping(
  value = "/greetings-with-response-body", 
  method = RequestMethod.GET, 
  produces="application/json"
) 
@ResponseBody
public String getGreetingWhileReturnTypeIsString() { 
    return "{\"test\": \"Hello using @ResponseBody\"}";
}

Let's test this using CURL:

curl http://localhost:8080/greetings-with-response-body

The above command produces the response:

{ "test": "Hello using @ResponseBody" }

Based on the content type present in the header, @ResponseBody only binds a method return value to the web response body.

3. Content-Type Not Being Set Properly

When a method has a return type String, and no JSON Mapper present at classpath. In this case, the return value is handled by StringHttpMessageConverter class which sets the content type to “text/plain”. This often leads to an issue where the controller is unable to produce the expected content type.

Let's look at different approaches to solve this issue.

3.1. Using @ResponseBody with JSON Mapper

The Jackson ObjectMapper class parses a JSON from a string, stream, or file. If Jackson is on the classpath, any controller in Spring applications renders the JSON response by default.

To include Jackson on the classpath, we need to add the following dependency in pom.xml:

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.12.4</version>
</dependency>

Let's add a unit test to verify from the response:

@Test
public void givenReturnTypeIsString_whenJacksonOnClasspath_thenDefaultContentTypeIsJSON() 
  throws Exception {
    
    // Given
    String expectedMimeType = "application/json";
    
    // Then
    String actualMimeType = this.mockMvc.perform(MockMvcRequestBuilders.get("/greetings-with-response-body", 1))
      .andReturn().getResponse().getContentType();

    Assert.assertEquals(expectedMimeType, actualMimeType);
}

3.2. Using ResponseEntiy

In contrast to @ResponseBody, the ResponseEntity is a generic type that represents the entire HTTP response. As a result, we can control anything that goes into it: status code, header, and the body.

Let's define a new endpoint:

@RequestMapping(
  value = "/greetings-with-response-entity",
  method = RequestMethod.GET, 
  produces = "application/json"
)
public ResponseEntity<String> getGreetingWithResponseEntity() {
    final HttpHeaders httpHeaders= new HttpHeaders();
    httpHeaders.setContentType(MediaType.APPLICATION_JSON);
    return new ResponseEntity<String>("{\"test\": \"Hello with ResponseEntity\"}", httpHeaders, HttpStatus.OK);
}

In the developer console of our browser, we can see the following response:

{"test": "Hello with ResponseEntity"}

With ResponseEntity, we should have the annotation-driven tag in our dispatcher servlet:

<mvc:annotation-driven />

Simply put, the above tag gives greater control over the inner working of Spring MVC.

Let's verify the response's content type with a test case:

@Test
public void givenReturnTypeIsResponseEntity_thenDefaultContentTypeIsJSON() throws Exception {
    
    // Given
    String expectedMimeType = "application/json";
    
    // Then
    String actualMimeType = this.mockMvc.perform(MockMvcRequestBuilders.get("/greetings-with-response-entity", 1))
      .andReturn().getResponse().getContentType();

    Assert.assertEquals(expectedMimeType, actualMimeType);
}

3.3. Using Map<String, Object> Return Type

Last but not the least, we can also set the content type by changing the return type from String to Map. This Map return type will need marshaling and returns JSON object.

Here's our new endpoint:

@RequestMapping(
  value = "/greetings-with-map-return-type", 
  method = RequestMethod.GET, 
  produces = "application/json"
)
@ResponseBody
public Map<String, Object> getGreetingWhileReturnTypeIsMap() {
    HashMap<String, Object> map = new HashMap<String, Object>();
    map.put("test", "Hello from map");
    return map;
}

Let's see this in action:

curl http://localhost:8080/greetings-with-map-return-type

The curl command returns a JSON response:

{ "test": "Hello from map" }

4. Conclusion

This article explains how to set content type in Spring MVC, first adding Json mapper in the classpath, then using ResponseEntity, and finally changing the return type from String to Map.

As always, all the code snippets can be found over on GitHub.

Jackson bottom

Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:

>> CHECK OUT THE COURSE
Generic footer banner
Comments are closed on this article!