Polish standard Spring MVC exception handling

Rename ExceptionHandlerSupport to ResponseEntityExceptionHandler and
emphasize the contrast to DefaultHandlerExceptionResovler -- i.e.
one returns a ResponseEntity and relies on message converters while
the other returns a ModelAndView and relies on view resolution.

Issue: SPR-9290
This commit is contained in:
Rossen Stoyanchev 2012-08-27 14:06:06 -04:00
parent 0a6b1167a7
commit da05b094f5
7 changed files with 157 additions and 129 deletions

View File

@ -17,8 +17,13 @@
package org.springframework.web;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import javax.servlet.ServletException;
import org.springframework.http.HttpMethod;
/**
* Exception thrown when a request handler does not support a
* specific request method.
@ -95,4 +100,15 @@ public class HttpRequestMethodNotSupportedException extends ServletException {
return this.supportedMethods;
}
/**
* Return the actually supported HTTP methods, if known, as {@link HttpMethod} instances.
*/
public Set<HttpMethod> getSupportedHttpMethods() {
Set<HttpMethod> supportedMethods = new HashSet<HttpMethod>();
for (String value : this.supportedMethods) {
supportedMethods.add(HttpMethod.valueOf(value));
}
return supportedMethods;
}
}

View File

@ -32,7 +32,7 @@ import org.springframework.stereotype.Component;
*
* <p>It is typically used to define {@link ExceptionHandler @ExceptionHandler},
* {@link InitBinder @InitBinder}, and {@link ModelAttribute @ModelAttribute}
* methods that apply across controller class hierarchies.
* methods that apply to all {@link RequestMapping @RequestMapping} methods.
*
* @author Rossen Stoyanchev
* @since 3.2

View File

@ -15,7 +15,6 @@
*/
package org.springframework.web.servlet.mvc.method.annotation;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@ -28,6 +27,7 @@ import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.util.CollectionUtils;
@ -40,30 +40,34 @@ import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.multipart.support.MissingServletRequestPartException;
import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException;
/**
* A convenient base for classes with {@link ExceptionHandler} methods providing
* infrastructure to handle standard Spring MVC exceptions. The functionality is
* equivalent to that of the {@link org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver DefaultHandlerExceptionResolver}
* except it can be customized to write error content to the body of the response. If
* there is no need to write error content, use {@code DefaultHandlerExceptionResolver}
* instead.
* A convenient base class for {@link ControllerAdvice @ControllerAdvice} classes
* that wish to provide centralized exception handling across all
* {@code @RequestMapping} methods through {@code @ExceptionHandler} methods.
*
* <p>It is expected the sub-classes will be annotated with
* {@link ControllerAdvice @ControllerAdvice} and that
* {@link ExceptionHandlerExceptionResolver} is configured to ensure this class
* applies to exceptions from any {@link RequestMapping @RequestMapping} method.
* <p>This base class provides an {@code @ExceptionHandler} for handling standard
* Spring MVC exceptions that returns a {@code ResponseEntity} to be written with
* {@link HttpMessageConverter message converters}. This is in contrast to
* {@link org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
* DefaultHandlerExceptionResolver} which returns a {@code ModelAndView} instead.
*
* <p>If there is no need to write error content to the response body, or if using
* view resolution, e.g. {@code ContentNegotiatingViewResolver}, then use
* {@code DefaultHandlerExceptionResolver} instead.
*
* <p>Note that in order for an {@code @ControllerAdvice} sub-class to be
* detected, {@link ExceptionHandlerExceptionResolver} must be configured.
*
* @author Rossen Stoyanchev
* @since 3.2
*
* @see org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
*/
public abstract class ExceptionHandlerSupport {
public abstract class ResponseEntityExceptionHandler {
protected final Log logger = LogFactory.getLog(getClass());
@ -104,294 +108,290 @@ public abstract class ExceptionHandlerSupport {
HttpHeaders headers = new HttpHeaders();
HttpStatus status;
Object body;
if (ex instanceof NoSuchRequestHandlingMethodException) {
status = HttpStatus.NOT_FOUND;
body = handleNoSuchRequestHandlingMethod((NoSuchRequestHandlingMethodException) ex, headers, status, request);
HttpStatus status = HttpStatus.NOT_FOUND;
return handleNoSuchRequestHandlingMethod((NoSuchRequestHandlingMethodException) ex, headers, status, request);
}
else if (ex instanceof HttpRequestMethodNotSupportedException) {
status = HttpStatus.METHOD_NOT_ALLOWED;
body = handleHttpRequestMethodNotSupported((HttpRequestMethodNotSupportedException) ex, headers, status, request);
HttpStatus status = HttpStatus.METHOD_NOT_ALLOWED;
return handleHttpRequestMethodNotSupported((HttpRequestMethodNotSupportedException) ex, headers, status, request);
}
else if (ex instanceof HttpMediaTypeNotSupportedException) {
status = HttpStatus.UNSUPPORTED_MEDIA_TYPE;
body = handleHttpMediaTypeNotSupported((HttpMediaTypeNotSupportedException) ex, headers, status, request);
HttpStatus status = HttpStatus.UNSUPPORTED_MEDIA_TYPE;
return handleHttpMediaTypeNotSupported((HttpMediaTypeNotSupportedException) ex, headers, status, request);
}
else if (ex instanceof HttpMediaTypeNotAcceptableException) {
status = HttpStatus.NOT_ACCEPTABLE;
body = handleHttpMediaTypeNotAcceptable((HttpMediaTypeNotAcceptableException) ex, headers, status, request);
HttpStatus status = HttpStatus.NOT_ACCEPTABLE;
return handleHttpMediaTypeNotAcceptable((HttpMediaTypeNotAcceptableException) ex, headers, status, request);
}
else if (ex instanceof MissingServletRequestParameterException) {
status = HttpStatus.BAD_REQUEST;
body = handleMissingServletRequestParameter((MissingServletRequestParameterException) ex, headers, status, request);
HttpStatus status = HttpStatus.BAD_REQUEST;
return handleMissingServletRequestParameter((MissingServletRequestParameterException) ex, headers, status, request);
}
else if (ex instanceof ServletRequestBindingException) {
status = HttpStatus.BAD_REQUEST;
body = handleServletRequestBindingException((ServletRequestBindingException) ex, headers, status, request);
HttpStatus status = HttpStatus.BAD_REQUEST;
return handleServletRequestBindingException((ServletRequestBindingException) ex, headers, status, request);
}
else if (ex instanceof ConversionNotSupportedException) {
status = HttpStatus.INTERNAL_SERVER_ERROR;
body = handleConversionNotSupported((ConversionNotSupportedException) ex, headers, status, request);
HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
return handleConversionNotSupported((ConversionNotSupportedException) ex, headers, status, request);
}
else if (ex instanceof TypeMismatchException) {
status = HttpStatus.BAD_REQUEST;
body = handleTypeMismatch((TypeMismatchException) ex, headers, status, request);
HttpStatus status = HttpStatus.BAD_REQUEST;
return handleTypeMismatch((TypeMismatchException) ex, headers, status, request);
}
else if (ex instanceof HttpMessageNotReadableException) {
status = HttpStatus.BAD_REQUEST;
body = handleHttpMessageNotReadable((HttpMessageNotReadableException) ex, headers, status, request);
HttpStatus status = HttpStatus.BAD_REQUEST;
return handleHttpMessageNotReadable((HttpMessageNotReadableException) ex, headers, status, request);
}
else if (ex instanceof HttpMessageNotWritableException) {
status = HttpStatus.INTERNAL_SERVER_ERROR;
body = handleHttpMessageNotWritable((HttpMessageNotWritableException) ex, headers, status, request);
HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
return handleHttpMessageNotWritable((HttpMessageNotWritableException) ex, headers, status, request);
}
else if (ex instanceof MethodArgumentNotValidException) {
status = HttpStatus.BAD_REQUEST;
body = handleMethodArgumentNotValid((MethodArgumentNotValidException) ex, headers, status, request);
HttpStatus status = HttpStatus.BAD_REQUEST;
return handleMethodArgumentNotValid((MethodArgumentNotValidException) ex, headers, status, request);
}
else if (ex instanceof MissingServletRequestPartException) {
status = HttpStatus.BAD_REQUEST;
body = handleMissingServletRequestPart((MissingServletRequestPartException) ex, headers, status, request);
HttpStatus status = HttpStatus.BAD_REQUEST;
return handleMissingServletRequestPart((MissingServletRequestPartException) ex, headers, status, request);
}
else if (ex instanceof BindException) {
status = HttpStatus.BAD_REQUEST;
body = handleBindException((BindException) ex, headers, status, request);
HttpStatus status = HttpStatus.BAD_REQUEST;
return handleBindException((BindException) ex, headers, status, request);
}
else {
logger.warn("Unknown exception type: " + ex.getClass().getName());
status = HttpStatus.INTERNAL_SERVER_ERROR;
body = handleExceptionInternal(ex, headers, status, request);
HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
return handleExceptionInternal(ex, null, headers, status, request);
}
return new ResponseEntity<Object>(body, headers, status);
}
/**
* A single place to customize the response body of all Exception types.
* This method returns {@code null} by default.
* @param ex the exception
* @param body the body to use for the response
* @param headers the headers to be written to the response
* @param status the selected response status
* @param request the current request
*/
protected Object handleExceptionInternal(Exception ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
return null;
protected ResponseEntity<Object> handleExceptionInternal(Exception ex, Object body,
HttpHeaders headers, HttpStatus status, WebRequest request) {
return new ResponseEntity<Object>(body, headers, status);
}
/**
* Customize the response for NoSuchRequestHandlingMethodException.
* This method logs a warning and delegates to
* {@link #handleExceptionInternal(Exception, HttpHeaders, HttpStatus, WebRequest)}.
* {@link #handleExceptionInternal(Exception, Object, HttpHeaders, HttpStatus, WebRequest)}.
* @param ex the exception
* @param headers the headers to be written to the response
* @param status the selected response status
* @param request the current request
* @return an Object or {@code null}
* @return a {@code ResponseEntity} instance
*/
protected Object handleNoSuchRequestHandlingMethod(NoSuchRequestHandlingMethodException ex,
protected ResponseEntity<Object> handleNoSuchRequestHandlingMethod(NoSuchRequestHandlingMethodException ex,
HttpHeaders headers, HttpStatus status, WebRequest request) {
pageNotFoundLogger.warn(ex.getMessage());
return handleExceptionInternal(ex, headers, status, request);
return handleExceptionInternal(ex, null, headers, status, request);
}
/**
* Customize the response for HttpRequestMethodNotSupportedException.
* This method logs a warning, sets the "Allow" header, and delegates to
* {@link #handleExceptionInternal(Exception, HttpHeaders, HttpStatus, WebRequest)}.
* {@link #handleExceptionInternal(Exception, Object, HttpHeaders, HttpStatus, WebRequest)}.
* @param ex the exception
* @param headers the headers to be written to the response
* @param status the selected response status
* @param request the current request
* @return an Object or {@code null}
* @return a {@code ResponseEntity} instance
*/
protected Object handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException ex,
protected ResponseEntity<Object> handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException ex,
HttpHeaders headers, HttpStatus status, WebRequest request) {
pageNotFoundLogger.warn(ex.getMessage());
Set<HttpMethod> mediaTypes = new HashSet<HttpMethod>();
for (String value : ex.getSupportedMethods()) {
mediaTypes.add(HttpMethod.valueOf(value));
}
if (!mediaTypes.isEmpty()) {
headers.setAllow(mediaTypes);
Set<HttpMethod> supportedMethods = ex.getSupportedHttpMethods();
if (!supportedMethods.isEmpty()) {
headers.setAllow(supportedMethods);
}
return handleExceptionInternal(ex, headers, status, request);
return handleExceptionInternal(ex, null, headers, status, request);
}
/**
* Customize the response for HttpMediaTypeNotSupportedException.
* This method sets the "Accept" header and delegates to
* {@link #handleExceptionInternal(Exception, HttpHeaders, HttpStatus, WebRequest)}.
* {@link #handleExceptionInternal(Exception, Object, HttpHeaders, HttpStatus, WebRequest)}.
* @param ex the exception
* @param headers the headers to be written to the response
* @param status the selected response status
* @param request the current request
* @return an Object or {@code null}
* @return a {@code ResponseEntity} instance
*/
protected Object handleHttpMediaTypeNotSupported(HttpMediaTypeNotSupportedException ex,
protected ResponseEntity<Object> handleHttpMediaTypeNotSupported(HttpMediaTypeNotSupportedException ex,
HttpHeaders headers, HttpStatus status, WebRequest request) {
List<MediaType> mediaTypes = ex.getSupportedMediaTypes();
if (!CollectionUtils.isEmpty(mediaTypes)) {
headers.setAccept(mediaTypes);
}
return handleExceptionInternal(ex, headers, status, request);
return handleExceptionInternal(ex, null, headers, status, request);
}
/**
* Customize the response for HttpMediaTypeNotAcceptableException.
* This method delegates to {@link #handleExceptionInternal(Exception, HttpHeaders, HttpStatus, WebRequest)}.
* This method delegates to {@link #handleExceptionInternal(Exception, Object, HttpHeaders, HttpStatus, WebRequest)}.
* @param ex the exception
* @param headers the headers to be written to the response
* @param status the selected response status
* @param request the current request
* @return an Object or {@code null}
* @return a {@code ResponseEntity} instance
*/
protected Object handleHttpMediaTypeNotAcceptable(HttpMediaTypeNotAcceptableException ex,
protected ResponseEntity<Object> handleHttpMediaTypeNotAcceptable(HttpMediaTypeNotAcceptableException ex,
HttpHeaders headers, HttpStatus status, WebRequest request) {
return handleExceptionInternal(ex, headers, status, request);
return handleExceptionInternal(ex, null, headers, status, request);
}
/**
* Customize the response for MissingServletRequestParameterException.
* This method delegates to {@link #handleExceptionInternal(Exception, HttpHeaders, HttpStatus, WebRequest)}.
* This method delegates to {@link #handleExceptionInternal(Exception, Object, HttpHeaders, HttpStatus, WebRequest)}.
* @param ex the exception
* @param headers the headers to be written to the response
* @param status the selected response status
* @param request the current request
* @return an Object or {@code null}
* @return a {@code ResponseEntity} instance
*/
protected Object handleMissingServletRequestParameter(MissingServletRequestParameterException ex,
protected ResponseEntity<Object> handleMissingServletRequestParameter(MissingServletRequestParameterException ex,
HttpHeaders headers, HttpStatus status, WebRequest request) {
return handleExceptionInternal(ex, headers, status, request);
return handleExceptionInternal(ex, null, headers, status, request);
}
/**
* Customize the response for ServletRequestBindingException.
* This method delegates to {@link #handleExceptionInternal(Exception, HttpHeaders, HttpStatus, WebRequest)}.
* This method delegates to {@link #handleExceptionInternal(Exception, Object, HttpHeaders, HttpStatus, WebRequest)}.
* @param ex the exception
* @param headers the headers to be written to the response
* @param status the selected response status
* @param request the current request
* @return an Object or {@code null}
* @return a {@code ResponseEntity} instance
*/
protected Object handleServletRequestBindingException(ServletRequestBindingException ex,
protected ResponseEntity<Object> handleServletRequestBindingException(ServletRequestBindingException ex,
HttpHeaders headers, HttpStatus status, WebRequest request) {
return handleExceptionInternal(ex, headers, status, request);
return handleExceptionInternal(ex, null, headers, status, request);
}
/**
* Customize the response for ConversionNotSupportedException.
* This method delegates to {@link #handleExceptionInternal(Exception, HttpHeaders, HttpStatus, WebRequest)}.
* This method delegates to {@link #handleExceptionInternal(Exception, Object, HttpHeaders, HttpStatus, WebRequest)}.
* @param ex the exception
* @param headers the headers to be written to the response
* @param status the selected response status
* @param request the current request
* @return an Object or {@code null}
* @return a {@code ResponseEntity} instance
*/
protected Object handleConversionNotSupported(ConversionNotSupportedException ex,
protected ResponseEntity<Object> handleConversionNotSupported(ConversionNotSupportedException ex,
HttpHeaders headers, HttpStatus status, WebRequest request) {
return handleExceptionInternal(ex, headers, status, request);
return handleExceptionInternal(ex, null, headers, status, request);
}
/**
* Customize the response for TypeMismatchException.
* This method delegates to {@link #handleExceptionInternal(Exception, HttpHeaders, HttpStatus, WebRequest)}.
* This method delegates to {@link #handleExceptionInternal(Exception, Object, HttpHeaders, HttpStatus, WebRequest)}.
* @param ex the exception
* @param headers the headers to be written to the response
* @param status the selected response status
* @param request the current request
* @return an Object or {@code null}
* @return a {@code ResponseEntity} instance
*/
protected Object handleTypeMismatch(TypeMismatchException ex, HttpHeaders headers,
protected ResponseEntity<Object> handleTypeMismatch(TypeMismatchException ex, HttpHeaders headers,
HttpStatus status, WebRequest request) {
return handleExceptionInternal(ex, headers, status, request);
return handleExceptionInternal(ex, null, headers, status, request);
}
/**
* Customize the response for HttpMessageNotReadableException.
* This method delegates to {@link #handleExceptionInternal(Exception, HttpHeaders, HttpStatus, WebRequest)}.
* This method delegates to {@link #handleExceptionInternal(Exception, Object, HttpHeaders, HttpStatus, WebRequest)}.
* @param ex the exception
* @param headers the headers to be written to the response
* @param status the selected response status
* @param request the current request
* @return an Object or {@code null}
* @return a {@code ResponseEntity} instance
*/
protected Object handleHttpMessageNotReadable(HttpMessageNotReadableException ex,
protected ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException ex,
HttpHeaders headers, HttpStatus status, WebRequest request) {
return handleExceptionInternal(ex, headers, status, request);
return handleExceptionInternal(ex, null, headers, status, request);
}
/**
* Customize the response for HttpMessageNotWritableException.
* This method delegates to {@link #handleExceptionInternal(Exception, HttpHeaders, HttpStatus, WebRequest)}.
* This method delegates to {@link #handleExceptionInternal(Exception, Object, HttpHeaders, HttpStatus, WebRequest)}.
* @param ex the exception
* @param headers the headers to be written to the response
* @param status the selected response status
* @param request the current request
* @return an Object or {@code null}
* @return a {@code ResponseEntity} instance
*/
protected Object handleHttpMessageNotWritable(HttpMessageNotWritableException ex,
protected ResponseEntity<Object> handleHttpMessageNotWritable(HttpMessageNotWritableException ex,
HttpHeaders headers, HttpStatus status, WebRequest request) {
return handleExceptionInternal(ex, headers, status, request);
return handleExceptionInternal(ex, null, headers, status, request);
}
/**
* Customize the response for MethodArgumentNotValidException.
* This method delegates to {@link #handleExceptionInternal(Exception, HttpHeaders, HttpStatus, WebRequest)}.
* This method delegates to {@link #handleExceptionInternal(Exception, Object, HttpHeaders, HttpStatus, WebRequest)}.
* @param ex the exception
* @param headers the headers to be written to the response
* @param status the selected response status
* @param request the current request
* @return an Object or {@code null}
* @return a {@code ResponseEntity} instance
*/
protected Object handleMethodArgumentNotValid(MethodArgumentNotValidException ex,
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex,
HttpHeaders headers, HttpStatus status, WebRequest request) {
return handleExceptionInternal(ex, headers, status, request);
return handleExceptionInternal(ex, null, headers, status, request);
}
/**
* Customize the response for MissingServletRequestPartException.
* This method delegates to {@link #handleExceptionInternal(Exception, HttpHeaders, HttpStatus, WebRequest)}.
* This method delegates to {@link #handleExceptionInternal(Exception, Object, HttpHeaders, HttpStatus, WebRequest)}.
* @param ex the exception
* @param headers the headers to be written to the response
* @param status the selected response status
* @param request the current request
* @return an Object or {@code null}
* @return a {@code ResponseEntity} instance
*/
protected Object handleMissingServletRequestPart(MissingServletRequestPartException ex,
protected ResponseEntity<Object> handleMissingServletRequestPart(MissingServletRequestPartException ex,
HttpHeaders headers, HttpStatus status, WebRequest request) {
return handleExceptionInternal(ex, headers, status, request);
return handleExceptionInternal(ex, null, headers, status, request);
}
/**
* Customize the response for BindException.
* This method delegates to {@link #handleExceptionInternal(Exception, HttpHeaders, HttpStatus, WebRequest)}.
* This method delegates to {@link #handleExceptionInternal(Exception, Object, HttpHeaders, HttpStatus, WebRequest)}.
* @param ex the exception
* @param headers the headers to be written to the response
* @param status the selected response status
* @param request the current request
* @return an Object or {@code null}
* @return a {@code ResponseEntity} instance
*/
protected Object handleBindException(BindException ex, HttpHeaders headers,
protected ResponseEntity<Object> handleBindException(BindException ex, HttpHeaders headers,
HttpStatus status, WebRequest request) {
return handleExceptionInternal(ex, headers, status, request);
return handleExceptionInternal(ex, null, headers, status, request);
}
}

View File

@ -60,7 +60,7 @@ import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMeth
* @author Rossen Stoyanchev
* @since 3.0
*
* @see org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerSupport
* @see org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler
* @see #handleNoSuchRequestHandlingMethod
* @see #handleHttpRequestMethodNotSupported
* @see #handleHttpMediaTypeNotSupported

View File

@ -18,7 +18,6 @@ package org.springframework.web.servlet.mvc.method.annotation;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.EnumSet;
@ -56,13 +55,13 @@ import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMeth
import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver;
/**
* Test fixture for {@link ExceptionHandlerSupport}.
* Test fixture for {@link ResponseEntityExceptionHandler}.
*
* @author Rossen Stoyanchev
*/
public class ExceptionHandlerSupportTests {
public class ResponseEntityExceptionHandlerTests {
private ExceptionHandlerSupport exceptionHandlerSupport;
private ResponseEntityExceptionHandler exceptionHandlerSupport;
private DefaultHandlerExceptionResolver defaultExceptionResolver;
@ -86,7 +85,7 @@ public class ExceptionHandlerSupportTests {
@Test
public void supportsAllDefaultHandlerExceptionResolverExceptionTypes() throws Exception {
Method annotMethod = ExceptionHandlerSupport.class.getMethod("handleException", Exception.class, WebRequest.class);
Method annotMethod = ResponseEntityExceptionHandler.class.getMethod("handleException", Exception.class, WebRequest.class);
ExceptionHandler annot = annotMethod.getAnnotation(ExceptionHandler.class);
List<Class<? extends Throwable>> supportedTypes = Arrays.asList(annot.value());
@ -217,14 +216,14 @@ public class ExceptionHandlerSupportTests {
}
@ControllerAdvice
private static class ApplicationExceptionHandler extends ExceptionHandlerSupport {
private static class ApplicationExceptionHandler extends ResponseEntityExceptionHandler {
@Override
protected Object handleServletRequestBindingException(ServletRequestBindingException ex,
protected ResponseEntity<Object> handleServletRequestBindingException(ServletRequestBindingException ex,
HttpHeaders headers, HttpStatus status, WebRequest request) {
headers.set("someHeader", "someHeaderValue");
return "error content";
return handleExceptionInternal(ex, "error content", headers, status, request);
}

View File

@ -3843,15 +3843,23 @@ public class SimpleController {
transparently by setting the status of the response. However, it stops short
of writing any error content to the body of the response while your
application may need to add developer-friendly content to every error
response for example when providing a REST API.</para>
response for example when providing a REST API. You can prepare a
<classname>ModelAndView</classname> and
render error content through view resolution -- i.e. by configuring a
<classname>ContentNeogitatingViewResolver</classname>,
<classname>MappingJacksonJsonView</classname>, and so on. However, you
may prefer to use <interfacename>@ExceptionHandler</interfacename>
methods instead.</para>
<para>To achieve this extend <classname>ExceptionHandlerSupport</classname>,
a convenient base class with an <interfacename>@ExceptionHandler</interfacename>
method that handles standard Spring MVC exceptions just as the
<classname>DefaultHandlerExceptionResolver</classname> does but also
allowing you to prepare error content for the body of the response.
See the Javadoc of <classname>ExceptionHandlerSupport</classname>
for more details.</para>
<para>If you prefer to write error content via
<interfacename>@ExceptionHandler</interfacename> methods you can extend
<classname>ResponseEntityExceptionHandler</classname> instead.
This is a convenient base for <interfacename>@ControllerAdvice</interfacename>
classes providing an <interfacename>@ExceptionHandler</interfacename>
method to handle standard Spring MVC exceptions and return
<classname>ResponseEntity</classname>. That allows you to customize the response
and write error content with message converters. See the Javadoc of
<classname>ResponseEntityExceptionHandler</classname> for more details.</para>
</section>
<section id="mvc-ann-annotated-exceptions">

View File

@ -66,7 +66,7 @@
</section>
<section id="new-in-3.2-webmvc-controller-advice">
<title><interfacename>@ControllerAdvice</interfacename> annotation</title>
<title>New <interfacename>@ControllerAdvice</interfacename> annotation</title>
<para>Classes annotated with <interfacename>@ControllerAdvice</interfacename>
can contain <interfacename>@ExceptionHandler</interfacename>,
@ -81,13 +81,18 @@
</section>
<section id="new-in-3.2-webmvc-exception-handler-support">
<title><classname>ExceptionHandlerSupport</classname> class</title>
<title>New <classname>ResponseEntityExceptionHandler</classname> class</title>
<para>A convenient base class with an
<interfacename>@ExceptionHandler</interfacename>
method that handles standard Spring MVC exceptions, just as the
<classname>DefaultHandlerExceptionResolver</classname> does, but also
allowing you to prepare error content for the body of the response.</para>
method that handles standard Spring MVC exceptions and returns a
<classname>ResponseEntity</classname> that allowing customizing and writing
the response with HTTP message converters. This servers as an alternative
to the <classname>DefaultHandlerExceptionResolver</classname>, which does
the same but returns a <classname>ModelAndView</classname> instead.</para>
<para>See the revised <xref linkend="mvc-exceptionhandlers"/>
including information on customizing the default Servlet container error page.</para>
</section>