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 | +----+---------------------+-----------------------+------------------+-------------------------------------+
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);
}