SpringMVC异常处理句柄这些细节,你知道吗?
回顾
@Controller和@ControllerAdvice类可以使用@ExceptionHandler注解标注方法来处理控制器方法的异常,如下例所示:
局部异常处理
@Controller
public class SimpleController {
// ...
// 只能处理当前SimpleController中发生的异常
@ExceptionHandler
public ResponseEntity<String> handle(Exception ex) {
// ...
}
}
全局异常处理
@RestControllerAdvice
public class TestControllerAdvice {
// 全局异常处理
@ExceptionHandler
public Object handle(Exception e) {
return "我是全局异常: " + e.getMessage() ;
}
}
指定能处理的异常类型
@ExceptionHandler({FileSystemException.class, RemoteException.class})
public ResponseEntity<String> handle(Exception ex) {
// ...
}
在@ExceptionHandler中指明能够处理的异常类。
以上是回顾了SpringMVC异常处理的基本使用,通过@ExceptionHandler注解标注方法,在上面的方法中都接受的是异常类,那这方法可以接收什么样的参数及返回值呢?
异常句柄参数
@ExceptionHandler方法支持以下参数:
方法参数 |
描述 |
Exception type |
用于访问引发的异常。 |
HandlerMethod |
用于访问引发异常的控制器方法。 |
WebRequest, NativeWebRequest |
对请求参数、请求和会话属性的通用访问,而不直接使用 Servlet API。 |
javax.servlet.ServletRequest, javax.servlet.ServletResponse |
选择任何特定的请求或响应类型(例如,ServletRequest或HttpServletRequest或Spring的MultipartRequest或MultipartHttpServletRequest)。 |
javax.servlet.http.HttpSession |
强制会话的存在。因此,这样的参数永远不会为空。注意,会话访问不是线程安全的。如果允许多个请求并发访问一个会话,可以考虑将RequestMappingHandlerAdapter实例的synchronizeOnSession标志设置为true。 |
java.security.Principal |
当前已验证的用户-如果已知,可能是特定的Principal实现类。 |
HttpMethod |
请求的HTTP方法。 |
java.util.Locale |
当前请求区域设置,由可用的最特定的LocaleResolver确定——实际上是配置的LocaleResolver或LocaleContextResolver。 |
java.util.TimeZone, java.time.ZoneId |
与当前请求关联的时区,由LocaleContextResolver确定。 |
java.io.OutputStream, java.io.Writer |
用于访问由Servlet API公开的原始响应体。 |
java.util.Map, org.springframework.ui.Model, org.springframework.ui.ModelMap |
用于访问错误响应的模型。总是空的。 |
RedirectAttributes |
指定在重定向情况下使用的属性-(将被追加到查询字符串中)和临时存储的flash属性,直到重定向后的请求。请参见重定向属性和Flash属性。 |
@SessionAttribute |
对于任何会话属性的访问,与作为类级@SessionAttributes声明的结果存储在会话中的模型属性相反。更多细节请参见@SessionAttribute。 |
@RequestAttribute |
用于访问请求属性。详见@RequestAttribute。 |
异常句柄返回值
@ExceptionHandler方法支持以下返回值:
返回值 |
描述 |
@ResponseBody |
返回值通过HttpMessageConverter实例转换并写入响应。看到@ResponseBody。 |
HttpEntity<B>, ResponseEntity<B> |
返回值指定通过HttpMessageConverter实例转换完整响应(包括HTTP报头和正文)并将其写入响应。看到ResponseEntity。 |
String |
要用ViewResolver实现解析的视图名称,并与隐式模型一起使用——通过命令对象和@ModelAttribute方法确定。处理程序方法还可以通过声明model参数(前面描述过)以编程方式丰富模型。 |
View |
一个用于呈现隐式模型的View实例——通过命令对象和@ModelAttribute方法确定。处理程序方法还可以通过声明model参数(前面描述过)以编程方式丰富模型。 |
java.util.Map, org.springframework.ui.Model |
属性被添加到隐式模型中,视图名通过RequestToViewNameTranslator隐式确定。 |
@ModelAttribute |
添加到模型中的属性,通过RequestToViewNameTranslator隐式确定视图名。 注意@ModelAttribute是可选的。请参阅该表末尾的“任何其他返回值”。 |
ModelAndView object |
要使用的视图和模型属性以及(可选的)响应状态。 |
void |
具有void返回类型(或空返回值)的方法,如果它还具有ServletResponse、OutputStream参数或@ResponseStatus注释,则认为它已经完全处理了响应。如果控制器已经做了一个正的ETag或lastModified时间戳检查(详情请参阅Controllers),同样也是正确的。 如果以上都不为真,void返回类型也可以表示REST控制器的“无响应体”或HTML控制器的默认视图名称选择。 |
Any other return value |
如果返回值与上述任何一个都不匹配,并且不是简单类型(由BeanUtils#isSimpleProperty决定),默认情况下,它将被视为要添加到模型中的模型属性。如果是简单类型,则仍未解决。 |
REST API异常
REST服务的一个常见需求是在响应体中包含错误详细信息。Spring框架不会自动执行此操作,因为响应体中的错误细节表示是特定于应用程序的。但是,@RestController可以使用带有ResponseEntity返回值的@ExceptionHandler方法来设置响应的状态和主体。这样的方法也可以在@ControllerAdvice类中声明,以便全局应用它们。
在响应体中使用错误细节实现全局异常处理的应用程序应该考虑扩展ResponseEntityExceptionHandler,它为Spring MVC引发的异常提供处理,并提供自定义响应体的钩子。要使用它,可以创建ResponseEntityExceptionHandler的子类,用@ControllerAdvice注释它,重写必要的方法,并将其声明为Spring bean,如下:
@RestControllerAdvice
static class CustomResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
@Override
protected ResponseEntity<Object> handleExceptionInternal(Exception ex, Object body, HttpHeaders headers,HttpStatus status, WebRequest request) {
System.out.println(">>>>>>>>>>> - body - " + body) ;
System.out.println(">>>>>>>>>>> - headers - " + headers) ;
System.out.println(">>>>>>>>>>> - status - " + status) ;
return new ResponseEntity<>(ex.getMessage(), headers, status) ;
}
}
ResponseEntityExceptionHandler类中内置了很多类型的异常处理
public abstract class ResponseEntityExceptionHandler {
@ExceptionHandler({
HttpRequestMethodNotSupportedException.class,
HttpMediaTypeNotSupportedException.class,
HttpMediaTypeNotAcceptableException.class,
MissingPathVariableException.class,
MissingServletRequestParameterException.class,
ServletRequestBindingException.class,
ConversionNotSupportedException.class,
TypeMismatchException.class,
HttpMessageNotReadableException.class,
HttpMessageNotWritableException.class,
MethodArgumentNotValidException.class,
MissingServletRequestPartException.class,
BindException.class,
NoHandlerFoundException.class,
AsyncRequestTimeoutException.class
})
@Nullable
public final ResponseEntity<Object> handleException(Exception ex, WebRequest request) throws Exception {
HttpHeaders headers = new HttpHeaders();
if (ex instanceof HttpRequestMethodNotSupportedException) {
HttpStatus status = HttpStatus.METHOD_NOT_ALLOWED;
return handleHttpRequestMethodNotSupported((HttpRequestMethodNotSupportedException) ex, headers, status, request);
}
else if (ex instanceof HttpMediaTypeNotSupportedException) {
HttpStatus status = HttpStatus.UNSUPPORTED_MEDIA_TYPE;
return handleHttpMediaTypeNotSupported((HttpMediaTypeNotSupportedException) ex, headers, status, request);
}
// ...
else {
throw ex;
}
}
}