Skip to content

Commit ed44262

Browse files
committed
ResponseEntityExceptionHandler rethrows unknown exception (for further processing in DispatcherServlet's HandlerExceptionResolver chain)
Issue: SPR-16743 (cherry picked from commit 7b894fe)
1 parent a0d37ac commit ed44262

File tree

6 files changed

+185
-105
lines changed

6 files changed

+185
-105
lines changed

spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerExceptionResolver.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -129,8 +129,8 @@ public void setPreventResponseCaching(boolean preventResponseCaching) {
129129
*/
130130
@Override
131131
@Nullable
132-
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response,
133-
@Nullable Object handler, Exception ex) {
132+
public ModelAndView resolveException(
133+
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
134134

135135
if (shouldApplyTo(request, handler)) {
136136
if (this.logger.isDebugEnabled()) {

spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandler.java

Lines changed: 55 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@
7070
* using view resolution (e.g., via {@code ContentNegotiatingViewResolver}),
7171
* then {@code DefaultHandlerExceptionResolver} is good enough.
7272
*
73-
* <p>Note that in order for an {@code @ControllerAdvice} sub-class to be
73+
* <p>Note that in order for an {@code @ControllerAdvice} subclass to be
7474
* detected, {@link ExceptionHandlerExceptionResolver} must be configured.
7575
*
7676
* @author Rossen Stoyanchev
@@ -121,8 +121,9 @@ public abstract class ResponseEntityExceptionHandler {
121121
AsyncRequestTimeoutException.class
122122
})
123123
@Nullable
124-
public final ResponseEntity<Object> handleException(Exception ex, WebRequest request) {
124+
public final ResponseEntity<Object> handleException(Exception ex, WebRequest request) throws Exception {
125125
HttpHeaders headers = new HttpHeaders();
126+
126127
if (ex instanceof HttpRequestMethodNotSupportedException) {
127128
HttpStatus status = HttpStatus.METHOD_NOT_ALLOWED;
128129
return handleHttpRequestMethodNotSupported((HttpRequestMethodNotSupportedException) ex, headers, status, request);
@@ -181,38 +182,17 @@ else if (ex instanceof NoHandlerFoundException) {
181182
}
182183
else if (ex instanceof AsyncRequestTimeoutException) {
183184
HttpStatus status = HttpStatus.SERVICE_UNAVAILABLE;
184-
return handleAsyncRequestTimeoutException(
185-
(AsyncRequestTimeoutException) ex, headers, status, request);
185+
return handleAsyncRequestTimeoutException((AsyncRequestTimeoutException) ex, headers, status, request);
186186
}
187187
else {
188-
if (logger.isWarnEnabled()) {
189-
logger.warn("Unknown exception type: " + ex.getClass().getName());
190-
}
191-
HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
192-
return handleExceptionInternal(ex, null, headers, status, request);
188+
// Unknown exception, typically a wrapper with a common MVC exception as cause
189+
// (since @ExceptionHandler type declarations also match first-level causes):
190+
// We only deal with top-level MVC exceptions here, so let's rethrow the given
191+
// exception for further processing through the HandlerExceptionResolver chain.
192+
throw ex;
193193
}
194194
}
195195

196-
/**
197-
* A single place to customize the response body of all Exception types.
198-
* <p>The default implementation sets the {@link WebUtils#ERROR_EXCEPTION_ATTRIBUTE}
199-
* request attribute and creates a {@link ResponseEntity} from the given
200-
* body, headers, and status.
201-
* @param ex the exception
202-
* @param body the body for the response
203-
* @param headers the headers for the response
204-
* @param status the response status
205-
* @param request the current request
206-
*/
207-
protected ResponseEntity<Object> handleExceptionInternal(Exception ex, @Nullable Object body,
208-
HttpHeaders headers, HttpStatus status, WebRequest request) {
209-
210-
if (HttpStatus.INTERNAL_SERVER_ERROR.equals(status)) {
211-
request.setAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE, ex, WebRequest.SCOPE_REQUEST);
212-
}
213-
return new ResponseEntity<>(body, headers, status);
214-
}
215-
216196
/**
217197
* Customize the response for HttpRequestMethodNotSupportedException.
218198
* <p>This method logs a warning, sets the "Allow" header, and delegates to
@@ -223,8 +203,8 @@ protected ResponseEntity<Object> handleExceptionInternal(Exception ex, @Nullable
223203
* @param request the current request
224204
* @return a {@code ResponseEntity} instance
225205
*/
226-
protected ResponseEntity<Object> handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException ex,
227-
HttpHeaders headers, HttpStatus status, WebRequest request) {
206+
protected ResponseEntity<Object> handleHttpRequestMethodNotSupported(
207+
HttpRequestMethodNotSupportedException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
228208

229209
pageNotFoundLogger.warn(ex.getMessage());
230210

@@ -245,8 +225,8 @@ protected ResponseEntity<Object> handleHttpRequestMethodNotSupported(HttpRequest
245225
* @param request the current request
246226
* @return a {@code ResponseEntity} instance
247227
*/
248-
protected ResponseEntity<Object> handleHttpMediaTypeNotSupported(HttpMediaTypeNotSupportedException ex,
249-
HttpHeaders headers, HttpStatus status, WebRequest request) {
228+
protected ResponseEntity<Object> handleHttpMediaTypeNotSupported(
229+
HttpMediaTypeNotSupportedException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
250230

251231
List<MediaType> mediaTypes = ex.getSupportedMediaTypes();
252232
if (!CollectionUtils.isEmpty(mediaTypes)) {
@@ -265,8 +245,8 @@ protected ResponseEntity<Object> handleHttpMediaTypeNotSupported(HttpMediaTypeNo
265245
* @param request the current request
266246
* @return a {@code ResponseEntity} instance
267247
*/
268-
protected ResponseEntity<Object> handleHttpMediaTypeNotAcceptable(HttpMediaTypeNotAcceptableException ex,
269-
HttpHeaders headers, HttpStatus status, WebRequest request) {
248+
protected ResponseEntity<Object> handleHttpMediaTypeNotAcceptable(
249+
HttpMediaTypeNotAcceptableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
270250

271251
return handleExceptionInternal(ex, null, headers, status, request);
272252
}
@@ -281,8 +261,8 @@ protected ResponseEntity<Object> handleHttpMediaTypeNotAcceptable(HttpMediaTypeN
281261
* @return a {@code ResponseEntity} instance
282262
* @since 4.2
283263
*/
284-
protected ResponseEntity<Object> handleMissingPathVariable(MissingPathVariableException ex,
285-
HttpHeaders headers, HttpStatus status, WebRequest request) {
264+
protected ResponseEntity<Object> handleMissingPathVariable(
265+
MissingPathVariableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
286266

287267
return handleExceptionInternal(ex, null, headers, status, request);
288268
}
@@ -296,8 +276,8 @@ protected ResponseEntity<Object> handleMissingPathVariable(MissingPathVariableEx
296276
* @param request the current request
297277
* @return a {@code ResponseEntity} instance
298278
*/
299-
protected ResponseEntity<Object> handleMissingServletRequestParameter(MissingServletRequestParameterException ex,
300-
HttpHeaders headers, HttpStatus status, WebRequest request) {
279+
protected ResponseEntity<Object> handleMissingServletRequestParameter(
280+
MissingServletRequestParameterException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
301281

302282
return handleExceptionInternal(ex, null, headers, status, request);
303283
}
@@ -311,8 +291,8 @@ protected ResponseEntity<Object> handleMissingServletRequestParameter(MissingSer
311291
* @param request the current request
312292
* @return a {@code ResponseEntity} instance
313293
*/
314-
protected ResponseEntity<Object> handleServletRequestBindingException(ServletRequestBindingException ex,
315-
HttpHeaders headers, HttpStatus status, WebRequest request) {
294+
protected ResponseEntity<Object> handleServletRequestBindingException(
295+
ServletRequestBindingException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
316296

317297
return handleExceptionInternal(ex, null, headers, status, request);
318298
}
@@ -326,8 +306,8 @@ protected ResponseEntity<Object> handleServletRequestBindingException(ServletReq
326306
* @param request the current request
327307
* @return a {@code ResponseEntity} instance
328308
*/
329-
protected ResponseEntity<Object> handleConversionNotSupported(ConversionNotSupportedException ex,
330-
HttpHeaders headers, HttpStatus status, WebRequest request) {
309+
protected ResponseEntity<Object> handleConversionNotSupported(
310+
ConversionNotSupportedException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
331311

332312
return handleExceptionInternal(ex, null, headers, status, request);
333313
}
@@ -341,8 +321,8 @@ protected ResponseEntity<Object> handleConversionNotSupported(ConversionNotSuppo
341321
* @param request the current request
342322
* @return a {@code ResponseEntity} instance
343323
*/
344-
protected ResponseEntity<Object> handleTypeMismatch(TypeMismatchException ex, HttpHeaders headers,
345-
HttpStatus status, WebRequest request) {
324+
protected ResponseEntity<Object> handleTypeMismatch(
325+
TypeMismatchException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
346326

347327
return handleExceptionInternal(ex, null, headers, status, request);
348328
}
@@ -356,8 +336,8 @@ protected ResponseEntity<Object> handleTypeMismatch(TypeMismatchException ex, Ht
356336
* @param request the current request
357337
* @return a {@code ResponseEntity} instance
358338
*/
359-
protected ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException ex,
360-
HttpHeaders headers, HttpStatus status, WebRequest request) {
339+
protected ResponseEntity<Object> handleHttpMessageNotReadable(
340+
HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
361341

362342
return handleExceptionInternal(ex, null, headers, status, request);
363343
}
@@ -371,8 +351,8 @@ protected ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotRead
371351
* @param request the current request
372352
* @return a {@code ResponseEntity} instance
373353
*/
374-
protected ResponseEntity<Object> handleHttpMessageNotWritable(HttpMessageNotWritableException ex,
375-
HttpHeaders headers, HttpStatus status, WebRequest request) {
354+
protected ResponseEntity<Object> handleHttpMessageNotWritable(
355+
HttpMessageNotWritableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
376356

377357
return handleExceptionInternal(ex, null, headers, status, request);
378358
}
@@ -386,8 +366,8 @@ protected ResponseEntity<Object> handleHttpMessageNotWritable(HttpMessageNotWrit
386366
* @param request the current request
387367
* @return a {@code ResponseEntity} instance
388368
*/
389-
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex,
390-
HttpHeaders headers, HttpStatus status, WebRequest request) {
369+
protected ResponseEntity<Object> handleMethodArgumentNotValid(
370+
MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
391371

392372
return handleExceptionInternal(ex, null, headers, status, request);
393373
}
@@ -401,8 +381,8 @@ protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotV
401381
* @param request the current request
402382
* @return a {@code ResponseEntity} instance
403383
*/
404-
protected ResponseEntity<Object> handleMissingServletRequestPart(MissingServletRequestPartException ex,
405-
HttpHeaders headers, HttpStatus status, WebRequest request) {
384+
protected ResponseEntity<Object> handleMissingServletRequestPart(
385+
MissingServletRequestPartException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
406386

407387
return handleExceptionInternal(ex, null, headers, status, request);
408388
}
@@ -416,8 +396,8 @@ protected ResponseEntity<Object> handleMissingServletRequestPart(MissingServletR
416396
* @param request the current request
417397
* @return a {@code ResponseEntity} instance
418398
*/
419-
protected ResponseEntity<Object> handleBindException(BindException ex, HttpHeaders headers,
420-
HttpStatus status, WebRequest request) {
399+
protected ResponseEntity<Object> handleBindException(
400+
BindException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
421401

422402
return handleExceptionInternal(ex, null, headers, status, request);
423403
}
@@ -467,4 +447,24 @@ protected ResponseEntity<Object> handleAsyncRequestTimeoutException(
467447
return handleExceptionInternal(ex, null, headers, status, webRequest);
468448
}
469449

450+
/**
451+
* A single place to customize the response body of all Exception types.
452+
* <p>The default implementation sets the {@link WebUtils#ERROR_EXCEPTION_ATTRIBUTE}
453+
* request attribute and creates a {@link ResponseEntity} from the given
454+
* body, headers, and status.
455+
* @param ex the exception
456+
* @param body the body for the response
457+
* @param headers the headers for the response
458+
* @param status the response status
459+
* @param request the current request
460+
*/
461+
protected ResponseEntity<Object> handleExceptionInternal(
462+
Exception ex, @Nullable Object body, HttpHeaders headers, HttpStatus status, WebRequest request) {
463+
464+
if (HttpStatus.INTERNAL_SERVER_ERROR.equals(status)) {
465+
request.setAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE, ex, WebRequest.SCOPE_REQUEST);
466+
}
467+
return new ResponseEntity<>(body, headers, status);
468+
}
469+
470470
}

spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/support/DefaultHandlerExceptionResolver.java

Lines changed: 30 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,9 @@
5353
import org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver;
5454

5555
/**
56-
* Default implementation of the {@link org.springframework.web.servlet.HandlerExceptionResolver
57-
* HandlerExceptionResolver} interface that resolves standard Spring exceptions and translates
58-
* them to corresponding HTTP status codes.
56+
* The default implementation of the {@link org.springframework.web.servlet.HandlerExceptionResolver}
57+
* interface, resolving standard Spring MVC exceptions and translating them to corresponding
58+
* HTTP status codes.
5959
*
6060
* <p>This exception resolver is enabled by default in the common Spring
6161
* {@link org.springframework.web.servlet.DispatcherServlet}.
@@ -169,54 +169,59 @@ protected ModelAndView doResolveException(HttpServletRequest request, HttpServle
169169

170170
try {
171171
if (ex instanceof HttpRequestMethodNotSupportedException) {
172-
return handleHttpRequestMethodNotSupported((HttpRequestMethodNotSupportedException) ex, request,
173-
response, handler);
172+
return handleHttpRequestMethodNotSupported(
173+
(HttpRequestMethodNotSupportedException) ex, request, response, handler);
174174
}
175175
else if (ex instanceof HttpMediaTypeNotSupportedException) {
176-
return handleHttpMediaTypeNotSupported((HttpMediaTypeNotSupportedException) ex, request, response,
177-
handler);
176+
return handleHttpMediaTypeNotSupported(
177+
(HttpMediaTypeNotSupportedException) ex, request, response, handler);
178178
}
179179
else if (ex instanceof HttpMediaTypeNotAcceptableException) {
180-
return handleHttpMediaTypeNotAcceptable((HttpMediaTypeNotAcceptableException) ex, request, response,
181-
handler);
180+
return handleHttpMediaTypeNotAcceptable(
181+
(HttpMediaTypeNotAcceptableException) ex, request, response, handler);
182182
}
183183
else if (ex instanceof MissingPathVariableException) {
184-
return handleMissingPathVariable((MissingPathVariableException) ex, request,
185-
response, handler);
184+
return handleMissingPathVariable(
185+
(MissingPathVariableException) ex, request, response, handler);
186186
}
187187
else if (ex instanceof MissingServletRequestParameterException) {
188-
return handleMissingServletRequestParameter((MissingServletRequestParameterException) ex, request,
189-
response, handler);
188+
return handleMissingServletRequestParameter(
189+
(MissingServletRequestParameterException) ex, request, response, handler);
190190
}
191191
else if (ex instanceof ServletRequestBindingException) {
192-
return handleServletRequestBindingException((ServletRequestBindingException) ex, request, response,
193-
handler);
192+
return handleServletRequestBindingException(
193+
(ServletRequestBindingException) ex, request, response, handler);
194194
}
195195
else if (ex instanceof ConversionNotSupportedException) {
196-
return handleConversionNotSupported((ConversionNotSupportedException) ex, request, response, handler);
196+
return handleConversionNotSupported(
197+
(ConversionNotSupportedException) ex, request, response, handler);
197198
}
198199
else if (ex instanceof TypeMismatchException) {
199-
return handleTypeMismatch((TypeMismatchException) ex, request, response, handler);
200+
return handleTypeMismatch(
201+
(TypeMismatchException) ex, request, response, handler);
200202
}
201203
else if (ex instanceof HttpMessageNotReadableException) {
202-
return handleHttpMessageNotReadable((HttpMessageNotReadableException) ex, request, response, handler);
204+
return handleHttpMessageNotReadable(
205+
(HttpMessageNotReadableException) ex, request, response, handler);
203206
}
204207
else if (ex instanceof HttpMessageNotWritableException) {
205-
return handleHttpMessageNotWritable((HttpMessageNotWritableException) ex, request, response, handler);
208+
return handleHttpMessageNotWritable(
209+
(HttpMessageNotWritableException) ex, request, response, handler);
206210
}
207211
else if (ex instanceof MethodArgumentNotValidException) {
208-
return handleMethodArgumentNotValidException((MethodArgumentNotValidException) ex, request, response,
209-
handler);
212+
return handleMethodArgumentNotValidException(
213+
(MethodArgumentNotValidException) ex, request, response, handler);
210214
}
211215
else if (ex instanceof MissingServletRequestPartException) {
212-
return handleMissingServletRequestPartException((MissingServletRequestPartException) ex, request,
213-
response, handler);
216+
return handleMissingServletRequestPartException(
217+
(MissingServletRequestPartException) ex, request, response, handler);
214218
}
215219
else if (ex instanceof BindException) {
216220
return handleBindException((BindException) ex, request, response, handler);
217221
}
218222
else if (ex instanceof NoHandlerFoundException) {
219-
return handleNoHandlerFoundException((NoHandlerFoundException) ex, request, response, handler);
223+
return handleNoHandlerFoundException(
224+
(NoHandlerFoundException) ex, request, response, handler);
220225
}
221226
else if (ex instanceof AsyncRequestTimeoutException) {
222227
return handleAsyncRequestTimeoutException(
@@ -225,7 +230,7 @@ else if (ex instanceof AsyncRequestTimeoutException) {
225230
}
226231
catch (Exception handlerException) {
227232
if (logger.isWarnEnabled()) {
228-
logger.warn("Handling of [" + ex.getClass().getName() + "] resulted in Exception", handlerException);
233+
logger.warn("Handling of [" + ex.getClass().getName() + "] resulted in exception", handlerException);
229234
}
230235
}
231236
return null;
@@ -550,7 +555,6 @@ else if (logger.isDebugEnabled()) {
550555
protected void sendServerError(Exception ex, HttpServletRequest request, HttpServletResponse response)
551556
throws IOException {
552557

553-
554558
request.setAttribute("javax.servlet.error.exception", ex);
555559
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
556560
}

0 commit comments

Comments
 (0)