Spring MVC supports ResponseStatusException

The ResponseStatusException is now also supported in Spring MVC
through the ResponseStatusExceptionResolver.

Issue: SPR-14895
This commit is contained in:
Rossen Stoyanchev
2016-11-29 15:18:02 -05:00
parent 729551f375
commit 93cfc791a7
2 changed files with 91 additions and 42 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
package org.springframework.web.servlet.mvc.annotation;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -25,6 +26,7 @@ import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver;
@@ -41,6 +43,8 @@ import org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver;
* present on cause exceptions, and as of 4.2.2 this resolver supports
* attribute overrides for {@code @ResponseStatus} in custom composed annotations.
*
* <p>As of 5.0 this resolver also supports {@link ResponseStatusException}.
*
* @author Arjen Poutsma
* @author Rossen Stoyanchev
* @author Sam Brannen
@@ -62,43 +66,80 @@ public class ResponseStatusExceptionResolver extends AbstractHandlerExceptionRes
protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) {
ResponseStatus responseStatus = AnnotatedElementUtils.findMergedAnnotation(ex.getClass(), ResponseStatus.class);
if (responseStatus != null) {
try {
return resolveResponseStatus(responseStatus, request, response, handler, ex);
try {
if (ex instanceof ResponseStatusException) {
return resolveResponseStatusException((ResponseStatusException) ex, request, response, handler);
}
catch (Exception resolveEx) {
logger.warn("Handling of @ResponseStatus resulted in Exception", resolveEx);
ResponseStatus status = AnnotatedElementUtils.findMergedAnnotation(ex.getClass(), ResponseStatus.class);
if (status != null) {
return resolveResponseStatus(status, request, response, handler, ex);
}
if (ex.getCause() instanceof Exception) {
ex = (Exception) ex.getCause();
return doResolveException(request, response, handler, ex);
}
}
else if (ex.getCause() instanceof Exception) {
ex = (Exception) ex.getCause();
return doResolveException(request, response, handler, ex);
catch (Exception resolveEx) {
logger.warn("Handling of @ResponseStatus resulted in Exception", resolveEx);
}
return null;
}
/**
* Template method that handles {@link ResponseStatus @ResponseStatus} annotation.
* <p>The default implementation sends a response error using
* {@link HttpServletResponse#sendError(int)} or
* {@link HttpServletResponse#sendError(int, String)} if the annotation has a
* {@linkplain ResponseStatus#reason() reason} and then returns an empty ModelAndView.
* @param responseStatus the annotation
* Template method that handles the {@link ResponseStatus @ResponseStatus} annotation.
* <p>The default implementation delegates to {@link #applyStatusAndReason}
* with the status code and reason from the annotation.
* @param responseStatus the {@code @ResponseStatus} annotation
* @param request current HTTP request
* @param response current HTTP response
* @param handler the executed handler, or {@code null} if none chosen at the
* time of the exception (for example, if multipart resolution failed)
* @param ex the exception that got thrown during handler execution or the
* exception that has the ResponseStatus annotation if found on the cause.
* @return a corresponding ModelAndView to forward to, or {@code null}
* for default processing
* time of the exception, e.g. if multipart resolution failed
* @param ex the exception
* @return an empty ModelAndView, i.e. exception resolved
*/
protected ModelAndView resolveResponseStatus(ResponseStatus responseStatus, HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex) throws Exception {
protected ModelAndView resolveResponseStatus(ResponseStatus responseStatus,
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
int statusCode = responseStatus.code().value();
String reason = responseStatus.reason();
return applyStatusAndReason(statusCode, reason, response);
}
/**
* Template method that handles an {@link ResponseStatusException}.
* <p>The default implementation delegates to {@link #applyStatusAndReason}
* with the status code and reason from the exception.
* @param ex the exception
* @param request current HTTP request
* @param response current HTTP response
* @param handler the executed handler, or {@code null} if none chosen at the
* time of the exception, e.g. if multipart resolution failed
* @return an empty ModelAndView, i.e. exception resolved
* @since 5.0
*/
protected ModelAndView resolveResponseStatusException(ResponseStatusException ex,
HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
int statusCode = ex.getStatus().value();
String reason = ex.getReason();
applyStatusAndReason(statusCode, reason, response);
return new ModelAndView();
}
/**
* Apply the resolved status code and reason to the response.
* <p>The default implementation sends a response error using
* {@link HttpServletResponse#sendError(int)} or
* {@link HttpServletResponse#sendError(int, String)} if there is a reason
* and then returns an empty ModelAndView.
* @since 5.0
*/
protected ModelAndView applyStatusAndReason(int statusCode, String reason, HttpServletResponse response)
throws IOException {
if (this.messageSource != null) {
reason = this.messageSource.getMessage(reason, null, reason, LocaleContextHolder.getLocale());
}