Support Content Negotiation with @ExceptionHandler
Prior to this commit, `@ExceptionHandler` annotated controller methods
could be mapped using the exception type declaration as an annotation
attribute, or as a method parameter.
While such methods support a wide variety of method arguments and return
types, it was not possible to declare the same exception type on
different methods (in the same controller/controller advice).
This commit adds a new `produces` attribute on `@ExceptionHandler`; with
that, applications can vary the HTTP response depending on the exception
type and the requested content-type by the client:
```
@ExceptionHandler(produces = "application/json")
public ResponseEntity<ErrorMessage> handleJson(IllegalArgumentException exc) {
return ResponseEntity.badRequest().body(new ErrorMessage(exc.getMessage(), 42));
}
@ExceptionHandler(produces = "text/html")
public String handle(IllegalArgumentException exc, Model model) {
model.addAttribute("error", new ErrorMessage(exc.getMessage(), 42));
return "errorView";
}
```
This commit implements support in both Spring MVC and Spring WebFlux.
Closes gh-31936
This commit is contained in:
@@ -71,7 +71,7 @@ import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Test fixture with {@link ExceptionHandlerExceptionResolver}.
|
||||
* Tests for {@link ExceptionHandlerExceptionResolver}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Arjen Poutsma
|
||||
@@ -419,6 +419,41 @@ class ExceptionHandlerExceptionResolverTests {
|
||||
assertThat(mav.isEmpty()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveExceptionJsonMediaType() throws UnsupportedEncodingException, NoSuchMethodException {
|
||||
IllegalArgumentException ex = new IllegalArgumentException();
|
||||
HandlerMethod handlerMethod = new HandlerMethod(new MediaTypeController(), "handle");
|
||||
this.resolver.afterPropertiesSet();
|
||||
this.request.addHeader("Accept", "application/json");
|
||||
ModelAndView mav = this.resolver.resolveException(this.request, this.response, handlerMethod, ex);
|
||||
|
||||
assertExceptionHandledAsBody(mav, "jsonBody");
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveExceptionHtmlMediaType() throws NoSuchMethodException {
|
||||
IllegalArgumentException ex = new IllegalArgumentException();
|
||||
HandlerMethod handlerMethod = new HandlerMethod(new MediaTypeController(), "handle");
|
||||
this.resolver.afterPropertiesSet();
|
||||
this.request.addHeader("Accept", "text/html");
|
||||
ModelAndView mav = this.resolver.resolveException(this.request, this.response, handlerMethod, ex);
|
||||
|
||||
assertThat(mav).isNotNull();
|
||||
assertThat(mav.getViewName()).isEqualTo("htmlView");
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveExceptionDefaultMediaType() throws NoSuchMethodException {
|
||||
IllegalArgumentException ex = new IllegalArgumentException();
|
||||
HandlerMethod handlerMethod = new HandlerMethod(new MediaTypeController(), "handle");
|
||||
this.resolver.afterPropertiesSet();
|
||||
this.request.addHeader("Accept", "*/*");
|
||||
ModelAndView mav = this.resolver.resolveException(this.request, this.response, handlerMethod, ex);
|
||||
|
||||
assertThat(mav).isNotNull();
|
||||
assertThat(mav.getViewName()).isEqualTo("htmlView");
|
||||
}
|
||||
|
||||
|
||||
private void assertMethodProcessorCount(int resolverCount, int handlerCount) {
|
||||
assertThat(this.resolver.getArgumentResolvers().getResolvers()).hasSize(resolverCount);
|
||||
@@ -650,4 +685,21 @@ class ExceptionHandlerExceptionResolverTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Controller
|
||||
static class MediaTypeController {
|
||||
|
||||
public void handle() {}
|
||||
|
||||
@ExceptionHandler(exception = IllegalArgumentException.class, produces = "application/json")
|
||||
@ResponseBody
|
||||
public String handleExceptionJson() {
|
||||
return "jsonBody";
|
||||
}
|
||||
|
||||
@ExceptionHandler(exception = IllegalArgumentException.class, produces = {"text/html", "*/*"})
|
||||
public String handleExceptionHtml() {
|
||||
return "htmlView";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user