How to change the content type in exception handler

dma_k picture dma_k · Oct 19, 2012 · Viewed 32.6k times · Source

Suppose I have a controller that serves GET request and returns bean to be serialized to JSON and also provides an exception handler for IllegalArgumentException that can be raised in service:

@RequestMapping(value = "/meta/{itemId}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public MetaInformation getMetaInformation(@PathVariable int itemId) {
    return myService.getMetaInformation(itemId);
}

@ExceptionHandler(IllegalArgumentException.class)
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
@ResponseBody
public String handleIllegalArgumentException(IllegalArgumentException ex) {
    return ExceptionUtils.getStackTrace(ex);
}

Message convertors are:

<mvc:annotation-driven>
    <mvc:message-converters>
        <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" />
        <bean class="org.springframework.http.converter.StringHttpMessageConverter" />
    </mvc:message-converters>
</mvc:annotation-driven>

Now when I request the given URL in browser I see the correct JSON reply. However if exception is raised, the stringified exception is converted into JSON as well, but I would love it to be processed by StringHttpMessageConverter (resulting text/plain mime type). How can I go it?

To make the picture more complete (and complicated), suppose I also have the following handler:

@RequestMapping(value = "/version", method = RequestMethod.GET)
@ResponseBody
public String getApplicationVersion() {
    return "1.0.12";
}

This handler allows the return string to be serialized by both MappingJackson2HttpMessageConverter and StringHttpMessageConverter depending in passed Accept-type by the client. The return types and values should be as following:

+----+---------------------+-----------------------+------------------+-------------------------------------+
| NN | URL                 | Accept-type           | Content-type     | Message converter                   |
|    |                     | request header        | response header  |                                     |
+----+---------------------+-----------------------+------------------+-------------------------------------+
| 1. | /version            | text/html; */*        | text/plain       | StringHttpMessageConverter          |
| 2. | /version            | application/json; */* | application/json | MappingJackson2HttpMessageConverter |
| 3. | /meta/1             | text/html; */*        | application/json | MappingJackson2HttpMessageConverter |
| 4. | /meta/1             | application/json; */* | application/json | MappingJackson2HttpMessageConverter |
| 5. | /meta/0 (exception) | text/html; */*        | text/plain       | StringHttpMessageConverter          |
| 6. | /meta/0 (exception) | application/json; */* | text/plain       | StringHttpMessageConverter          |
+----+---------------------+-----------------------+------------------+-------------------------------------+

Answer

oehmiche picture oehmiche · Oct 19, 2012

I think removing the produces = MediaType.APPLICATION_JSON_VALUE from @RequestMapping of the getMetaInformation will give you the desired result.

The response-type will be negotiated according to the content-type value in the Accept header.


edit

As this does not cover scenario 3,4 here is a solution working with ResponseEntity.class directly:

@ExceptionHandler(Exception.class)
public ResponseEntity<String> handleIllegalArgumentException(Exception ex) {
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.TEXT_PLAIN);
    return new ResponseEntity<String>(ex.getMessage(), headers, HttpStatus.BAD_REQUEST);
}