77

I have a Spring MVC Controller that returns a JSON String and I would like to set the mimetype to application/json. How can I do that?

@RequestMapping(method=RequestMethod.GET, value="foo/bar")
@ResponseBody
public String fooBar(){
    return myService.getJson();
}

The business objects are already available as JSON strings, so using MappingJacksonJsonView is not the solution for me. @ResponseBody is perfect, but how can I set the mimetype?

Sean Patrick Floyd
  • 292,901
  • 67
  • 465
  • 588

7 Answers7

124

Use ResponseEntity instead of ResponseBody. This way you have access to the response headers and you can set the appropiate content type. According to the Spring docs:

The HttpEntity is similar to @RequestBody and @ResponseBody. Besides getting access to the request and response body, HttpEntity (and the response-specific subclass ResponseEntity) also allows access to the request and response headers

The code will look like:

@RequestMapping(method=RequestMethod.GET, value="/fooBar")
public ResponseEntity<String> fooBar2() {
    String json = "jsonResponse";
    HttpHeaders responseHeaders = new HttpHeaders();
    responseHeaders.setContentType(MediaType.APPLICATION_JSON);
    return new ResponseEntity<String>(json, responseHeaders, HttpStatus.CREATED);
}
acdcjunior
  • 132,397
  • 37
  • 331
  • 304
Javier Ferrero
  • 8,741
  • 8
  • 45
  • 50
  • hi, I want to return a serialized object but using your method I have a problem, it doesn't compile because it says: HttpHeaders is abstract can not be instantiated.... can you explain me as well how you woud do this having to return the serialization of an object? now it works fine if not using ResponseEntity – Lince81 Mar 30 '11 at 13:31
  • @Lince81 org.springframework.http.HttpHeaders is not an abstract class (http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/http/HttpHeaders.html). See if your import is correct and your libraries are updated. – Javier Ferrero Mar 30 '11 at 17:15
  • @Lince81 the point of the example is returning an already serialized object as a String while setting a different Content-Type. If you want Spring to serialize an Object (as XML, JSON, etc) use @ResponseBody and configure the appropiate MessageConverters (see link in the answer) – Javier Ferrero Mar 30 '11 at 17:18
  • I needed a way to return a string that was already in JSON format and tell spring not to mess with it. This works great. – SimplGy Feb 21 '12 at 20:01
  • 1
    Just wanted to point out that Spring MVC 3.1 allows you to specify a value for "produces" in the RequestMapping. So you can still use @ResponseBody, but you need @RequestMapping(method=RequestMethod.GET, value="/fooBar", produces="application/json"). – Joe Mar 09 '12 at 16:12
  • 2
    With this approach you can have the same method return different content-types, something that you cannot do with Spring's "produces". Also, this is the least intrusive i.e. you don't have to create/ instantiate your own MessageConverters. – dewtea Aug 11 '15 at 19:28
42

I would consider to refactor the service to return your domain object rather than JSON strings and let Spring handle the serialization (via the MappingJacksonHttpMessageConverter as you write). As of Spring 3.1, the implementation looks quite neat:

@RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE, 
    method = RequestMethod.GET
    value = "/foo/bar")
@ResponseBody
public Bar fooBar(){
    return myService.getBar();
}

Comments:

First, the <mvc:annotation-driven /> or the @EnableWebMvc must be added to your application config.

Next, the produces attribute of the @RequestMapping annotation is used to specify the content type of the response. Consequently, it should be set to MediaType.APPLICATION_JSON_VALUE (or "application/json").

Lastly, Jackson must be added so that any serialization and de-serialization between Java and JSON will be handled automatically by Spring (the Jackson dependency is detected by Spring and the MappingJacksonHttpMessageConverter will be under the hood).

matsev
  • 32,104
  • 16
  • 121
  • 156
  • This answer should be number one, let Spring do the work for you!! – chrismarx Oct 07 '13 at 15:40
  • 8
    According to the javadoc, the purpose of property *produces* is to match the *Accept* header of the request. This explains why the value of *produces* is a list of values. Therefore, *produces* is not a reliable mean to add any response headers. According to the javadoc, it has nothing to do with response headers. – rwitzel Mar 20 '15 at 09:18
7

You may not be able to do it with @ResponseBody, but something like this should work:

package xxx;

import java.io.ByteArrayOutputStream;
import java.io.IOException;

import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class FooBar {
  @RequestMapping(value="foo/bar", method = RequestMethod.GET)
  public void fooBar(HttpServletResponse response) throws IOException {
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    out.write(myService.getJson().getBytes());
    response.setContentType("application/json");
    response.setContentLength(out.size());
    response.getOutputStream().write(out.toByteArray());
    response.getOutputStream().flush();
  }
}
GriffeyDog
  • 8,186
  • 3
  • 22
  • 34
  • would he need to write to the response, or just setting the header would do? – Bozho Dec 17 '10 at 21:16
  • 1
    great answer. If the mime type is dynamic, then the byte array must surely be dynamic as well, so it makes sense to just write the byte array without the magic of @ResponseBody. The only revise would be to write the OutputStream directly, instead of loading it into memory (out.toByteArray). – James Watkins May 22 '14 at 00:22
  • @JamesWatkins You need to load the JSON into memory to be able to calculate the content length. If your JSON object is reasonable small (and they usually should be!) this should be no concern. – Stefan Haberl May 23 '14 at 09:40
  • I think the true nature of the question is: "How do I set the mime type and send a non-HTML response at the same time?" I could have a very large file that I want to stream to the client. Also, Content Length is optional and there are other ways to derive it (from database, etc). – James Watkins May 27 '14 at 19:48
4

I don't think this is possible. There appears to be an open Jira for it:

SPR-6702: Explicitly set response Content-Type in @ResponseBody

dogbane
  • 266,786
  • 75
  • 396
  • 414
  • 6
    It's fixed in Spring 3.1 - use `@RequestMapping(method = RequestMethod.GET, value = "foo/bar", produces = "application/json")`, see [SPR-7353](https://jira.springsource.org/browse/SPR-7353) – Grzegorz Rożniecki Oct 02 '12 at 10:41
3

Register org.springframework.http.converter.json.MappingJacksonHttpMessageConverter as the message converter and return the object directly from the method.

<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
  <property name="webBindingInitializer">
    <bean class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer"/>
  </property>
  <property name="messageConverters">
    <list>
      <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"/>
    </list>
  </property>
</bean>

and the controller:

@RequestMapping(method=RequestMethod.GET, value="foo/bar")
public @ResponseBody Object fooBar(){
    return myService.getActualObject();
}

This requires the dependency org.springframework:spring-webmvc.

OrangeDog
  • 36,653
  • 12
  • 122
  • 207
1

I don't think you can, apart from response.setContentType(..)

Bozho
  • 588,226
  • 146
  • 1,060
  • 1,140
-2

My version of reality. Loading a HTML file and streaming to the browser.

@Controller
@RequestMapping("/")
public class UIController {

    @RequestMapping(value="index", method=RequestMethod.GET, produces = "text/html")
    public @ResponseBody String GetBootupFile() throws IOException  {
        Resource resource = new ClassPathResource("MainPage.html");
        String fileContents = FileUtils.readFileToString(resource.getFile());
        return fileContents;
    }
}
Sean Patrick Floyd
  • 292,901
  • 67
  • 465
  • 588
R Keene
  • 71
  • 2
  • you are aware that Spring MVC can [serve static resources out of the box](http://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-config-static-resources)? – Sean Patrick Floyd Sep 29 '16 at 22:21
  • Not only is that a lot more work than needed, but you are breaking a lot of other things Spring can automatically take care of and manage for you, as well as opening up security issues, and other failure points. – BrianC Aug 10 '17 at 16:02