前言
温馨提示:本文使用的spring boot版本为2.1.8.RELEASE。
全局异常处理大家应该都接触过,也不是什么难事,网上一搜一大堆,但是写的对不对只能自己测试了,运气好的话找了一个能用的,运气不好的可能会烦到你怀疑人生。
我就是那个运气不好的人,也是因为碰到了一些问题,所以才会有这篇文章吧。
优势
全局异常处理主要的好处:
- 统一接口返回格式。比如说请求方法错误,本来是get但是用成了post,这种错误都不会经过自己写的代码,无法控制返回的数据格式。
- 方便记录日志。跟上面一样的道理,有的错误框架直接返回了,可能都没有日志记录。
- 减少冗余代码。统一处理总比每一个接口自己处理要好吧。
做法
一般的方案都是基于@ControllerAdvice
和@ExceptionHandler
做的。
-
@ControllerAdvice
相当于controller的切面,主要用于@ExceptionHandler
,@InitBinder
和@ModelAttribute
,使注解标注的方法对每一个controller都起作用。默认对所有controller都起作用,当然也可以通过@ControllerAdvice
注解中的一些属性选定符合条件的controller。 -
@ExceptionHandler
用于异常处理的注解,可以通过value指定处理哪种类型的异常还可以与@ResponseStatus
搭配使用,处理特定的http错误。标记的方法入参与返回值都有很大的灵活性,具体可以看注释也可以后边的深度探究。
其实有上面两个已经够了,还可以继承ResponseEntityExceptionHandler
读一下这个类的注释就知道它是干啥的了
大概意思就是这个类是为了方便统一异常处理的基类,但是要注意返回的是ResponseEntity
,如果不需要往响应体中写内容或者返回一个视图,可以使用DefaultHandlerExceptionResolver
。
可以看一下这个类的实现
/**
* Provides handling for standard Spring MVC exceptions.
* @param ex the target exception
* @param request the current request
*/
@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 if (ex instanceof HttpMediaTypeNotAcceptableException) {
HttpStatus status = HttpStatus.NOT_ACCEPTABLE;
return handleHttpMediaTypeNotAcceptable((HttpMediaTypeNotAcceptableException) ex, headers, status, request);
}
else if (ex instanceof MissingPathVariableException) {
HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
return handleMissingPathVariable((MissingPathVariableException) ex, headers, status, request);
}
else if (ex instanceof MissingServletRequestParameterException) {
HttpStatus status = HttpStatus.BAD_REQUEST;
return handleMissingServletRequestParameter((MissingServletRequestParameterException) ex, headers, status, request);
}
else if (ex instanceof ServletRequestBindingException) {
HttpStatus status = HttpStatus.BAD_REQUEST;
return handleServletRequestBindingException((ServletRequestBindingException) ex, headers, status, request);
}
else if (ex instanceof ConversionNotSupportedException) {
HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
return handleConversionNotSupported((ConversionNotSupportedException) ex, headers, status, request);
}
else if (ex instanceof TypeMismatchException) {
HttpStatus status = HttpStatus.BAD_REQUEST;
return handleTypeMismatch((TypeMismatchException) ex, headers, status, request);
}
else if (ex instanceof HttpMessageNotReadableException) {
HttpStatus status = HttpStatus.BAD_REQUEST;
return handleHttpMessageNotReadable((HttpMessageNotReadableException) ex, headers, status, request);
}
else if (ex instanceof HttpMessageNotWritableException) {
HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
return handleHttpMessageNotWritable((HttpMessageNotWritableException) ex, headers, status, request);
}
else if (ex instanceof MethodArgumentNotValidException) {
HttpStatus status = HttpStatus.BAD_REQUEST;
return handleMethodArgumentNotValid((MethodArgumentNotValidException) ex, headers, status, request);
}
else if (ex instanceof MissingServletRequestPartException) {
HttpStatus status = HttpStatus.BAD_REQUEST;
return handleMissingServletRequestPart((MissingServletRequestPartException) ex, headers, status, request);
}
else if (ex instanceof BindException) {
HttpStatus status = HttpStatus.BAD_REQUEST;
return handleBindException((BindException) ex, headers, status, request);
}
else if (ex instanceof NoHandlerFoundException) {
HttpStatus status = HttpStatus.NOT_FOUND;
return handleNoHandlerFoundException((NoHandlerFoundException) ex, headers, status, request);
}
else if (ex instanceof AsyncRequestTimeoutException) {
HttpStatus status = HttpStatus.SERVICE_UNAVAILABLE;
return handleAsyncRequestTimeoutException((AsyncRequestTimeoutException) ex, headers, status, request);
}
else {
// Unknown exception, typically a wrapper with a common MVC exception as cause
// (since @ExceptionHandler type declarations also match first-level causes):
// We only deal with top-level MVC exceptions here, so let's rethrow the given
// exception for further processing through the HandlerExceptionResolver chain.
throw ex;
}
}
对于每一种异常,可以重写相应的方法。同时每个异常的具体处理方法最后又调用了同一个方法
/**
* A single place to customize the response body of all exception types.
* <p>The default implementation sets the {@link WebUtils#ERROR_EXCEPTION_ATTRIBUTE}
* request attribute and creates a {@link ResponseEntity} from the given
* body, headers, and status.
* @param ex the exception
* @param body the body for the response
* @param headers the headers for the response
* @param status the response status
* @param request the current request
*/
protected ResponseEntity<Object> handleExceptionInternal(
Exception ex, @Nullable Object body, HttpHeaders headers, HttpStatus status, WebRequest request) {
if (HttpStatus.INTERNAL_SERVER_ERROR.equals(status)) {
request.setAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE, ex, WebRequest.SCOPE_REQUEST);
}
return new ResponseEntity<>(body, headers, status);
}
通用的处理可以在这个方法中实现,返回结果中可以自定义body、header、以及http status。比如对于一些异常,可能并不希望http status code为500,而是返回200,用body里的code再去判断是成功还是失败,用这种方法就非常容易实现。
使用这种方案,我们自己写的类就应该类似这种
package com.zworks.aircraft.web.advice;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
@ControllerAdvice
public class AirCraftExceptionHandler extends ResponseEntityExceptionHandler {
}
实战
可以去Spring Initializr去初始化工程
依赖只需要web就可以了,为了方便可以添加lombok
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.zworks</groupId>
<artifactId>aircraft</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>aircraft</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
定义一个通用返回结果
package com.zworks.aircraft.model;
import lombok.Data;
@Data
public class RespMsg<T> {
private int code = 200;
private String msg;
private T data;
public RespMsg() {
}
public RespMsg(int code, String msg) {
this.code = code;
this.msg = msg;
}
public RespMsg(T data) {
this.data = data;
}
public static <T> RespMsg<T> success() {
return new RespMsg();
}
public static <T> RespMsg<T> success(T data) {
return new RespMsg(data);
}
public static RespMsg failed(int code, String msg) {
return new RespMsg(code, msg);
}
}
定义用户实体类,在字段上加上校验,方便演示错误的请求
package com.zworks.aircraft.model;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
@Data
public class User {
@NotNull(message = "id不能为空")
private Long id;
@NotBlank(message = "名称不能为空")
private String name;
}
定义一个Controller,什么都不用干,只需要验证参数的合法性,然后返回正确即可。
package com.zworks.aircraft.web.controller;
import com.zworks.aircraft.model.RespMsg;
import com.zworks.aircraft.model.User;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
@RestController
public class UserController {
@PostMapping(value = "user")
public RespMsg user(@Valid @RequestBody User user) {
return RespMsg.success();
}
}
如果参数正确,返回如下
{
"code": 200,
"msg": null,
"data": null
}
但如果参数错误,比如不传入name,则会返回
{
"timestamp": "2019-09-07T14:04:54.440+0000",
"status": 400,
"error": "Bad Request",
"errors": [
{
"codes": [
"NotBlank.user.name",
"NotBlank.name",
"NotBlank.java.lang.String",
"NotBlank"
],
"arguments": [
{
"codes": [
"user.name",
"name"
],
"arguments": null,
"defaultMessage": "name",
"code": "name"
}
],
"defaultMessage": "名称不能为空",
"objectName": "user",
"field": "name",
"rejectedValue": null,
"bindingFailure": false,
"code": "NotBlank"
}
],
"message": "Validation failed for object='user'. Error count: 1",
"path": "/user"
}
这肯定不是我们想要的结果。
按照刚才的方案,实现一个全局异常处理。
package com.zworks.aircraft.web.advice;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
@ControllerAdvice
public class AirCraftExceptionHandler extends ResponseEntityExceptionHandler {
}
这个时候其实已经生效了,只是默认的什么数据都没有返回,可以重写handleExceptionInternal
方法
package com.zworks.aircraft.web.advice;
import com.zworks.aircraft.model.RespMsg;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.lang.Nullable;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import org.springframework.web.util.WebUtils;
@ControllerAdvice
public class AirCraftExceptionHandler extends ResponseEntityExceptionHandler {
protected ResponseEntity<Object> handleExceptionInternal(
Exception ex, @Nullable Object body, HttpHeaders headers, HttpStatus status, WebRequest request) {
if (HttpStatus.INTERNAL_SERVER_ERROR.equals(status)) {
request.setAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE, ex, WebRequest.SCOPE_REQUEST);
}
logger.error(ex.getMessage(), ex);//打印异常信息
RespMsg respMsg = RespMsg.failed(status.value(), ex.getMessage());//使用http状态码作为返回体的code,同时把异常信息返回
return new ResponseEntity<>(respMsg, headers, status);
}
}
这时返回的信息如下
{
"code": 400,
"msg": "Validation failed for argument [0] in public com.zworks.aircraft.model.RespMsg com.zworks.aircraft.web.controller.UserController.user(com.zworks.aircraft.model.User): [Field error in object 'user' on field 'name': rejected value [null]; codes [NotBlank.user.name,NotBlank.name,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [user.name,name]; arguments []; default message [name]]; default message [不能为空]] ",
"data": null
}
虽然已经是我们想要的格式了,但是返回的内容不太已读。
刚才打印了日志,可以看到报错为MethodArgumentNotValidException
,可以通过重写handleMethodArgumentNotValid
方法改变异常信息
package com.zworks.aircraft.web.advice;
import com.zworks.aircraft.model.RespMsg;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.lang.Nullable;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import org.springframework.web.util.WebUtils;
@ControllerAdvice
public class AirCraftExceptionHandler extends ResponseEntityExceptionHandler {
protected ResponseEntity<Object> handleExceptionInternal(
Exception ex, @Nullable Object body, HttpHeaders headers, HttpStatus status, WebRequest request) {
if (HttpStatus.INTERNAL_SERVER_ERROR.equals(status)) {
request.setAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE, ex, WebRequest.SCOPE_REQUEST);
}
logger.error(ex.getMessage(), ex);//打印异常信息
RespMsg respMsg = RespMsg.failed(status.value(), ex.getMessage());//使用http状态码作为返回体的code,同时把异常信息返回
return new ResponseEntity<>(respMsg, headers, status);
}
protected ResponseEntity<Object> handleMethodArgumentNotValid(
MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
StringBuffer sb = new StringBuffer();
ex.getBindingResult().getAllErrors().forEach(error -> {
sb.append(error.getDefaultMessage()).append(";");
});
RespMsg respMsg = RespMsg.failed(status.value(), sb.toString());
return new ResponseEntity<>(respMsg, headers, HttpStatus.OK);//这里可以根据具体情况改变状态码
}
}
返回如下
{
"code": 400,
"msg": "名称不能为空;",
"data": null
}
同时需要注意的是,由于我修改了返回码,这次请求返回的是http status code是200而不是之前的400。
拓展
默认异常处理
在最开始没有自定义全局异常处理的时候,也返回了错误信息,那这个是谁处理的呢。
默认spring boot 会提供一个/error映射处理所有的异常。而且会根据请求的不同返回一个页面或是json。
在ErrorMvcAutoConfiguration
类中可以看到
@Bean
@ConditionalOnMissingBean(value = ErrorAttributes.class, search = SearchStrategy.CURRENT)
public DefaultErrorAttributes errorAttributes() {
return new DefaultErrorAttributes(this.serverProperties.getError().isIncludeException());
}
@Bean
@ConditionalOnMissingBean(value = ErrorController.class, search = SearchStrategy.CURRENT)
public BasicErrorController basicErrorController(ErrorAttributes errorAttributes) {
return new BasicErrorController(errorAttributes, this.serverProperties.getError(), this.errorViewResolvers);
}
都可以通过自己定义相应的类进行定制化。
之前由于被网上文章误导,使用前面那种方式没成功,为了记录异常,我还写了个切面。
package com.zworks.aircraft.config;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
@Aspect
@Component
@Slf4j
public class ErrorControllerAspect {
@Before("execution(public * org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.*(..))")
public void before(JoinPoint joinPoint) {
Object arg = joinPoint.getArgs()[0];
if (arg instanceof HttpServletRequest) {
HttpServletRequest request = (HttpServletRequest) arg;
log.error((String) request.getAttribute("javax.servlet.error.message"), (Throwable) request.getAttribute("javax.servlet.error.exception"));
}
}
}
返回类型
我被坑主要是因为看了一篇文章,Validation in Spring Boot,我不能说一定是文章的问题,至少我按他的没做对。
异常处理是这么写的
package com.zworks.aircraft.web.advice;
import org.springframework.http.HttpStatus;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import java.util.HashMap;
import java.util.Map;
@ControllerAdvice
public class AirCraftExceptionHandler {
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MethodArgumentNotValidException.class)
public Map<String, String> handleValidationExceptions(MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getAllErrors().forEach((error) -> {
String fieldName = ((FieldError) error).getField();
String errorMessage = error.getDefaultMessage();
errors.put(fieldName, errorMessage);
});
return errors;
}
}
注意这里返回的是Map类型
返回和没加是一样的
{
"timestamp": "2019-09-07T14:30:21.688+0000",
"status": 500,
"error": "Internal Server Error",
"errors": [
{
"codes": [
"NotBlank.user.name",
"NotBlank.name",
"NotBlank.java.lang.String",
"NotBlank"
],
"arguments": [
{
"codes": [
"user.name",
"name"
],
"arguments": null,
"defaultMessage": "name",
"code": "name"
}
],
"defaultMessage": "名称不能为空",
"objectName": "user",
"field": "name",
"rejectedValue": null,
"bindingFailure": false,
"code": "NotBlank"
}
],
"message": "Validation failed for object='user'. Error count: 1",
"path": "/user"
}
然后我在那个方法上打了断点,发现代码居然执行了
然后我就跟代码,也对比了两种实现的执行情况,发现在HandlerMethodReturnValueHandlerComposite
的selectHandler
方法中会根据返回的类型决定使用那个handler进行处理
@Nullable
private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
boolean isAsyncValue = isAsyncReturnValue(value, returnType);
for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
continue;
}
if (handler.supportsReturnType(returnType)) {
return handler;
}
}
return null;
}
现在的handler是MapMethodProcessor
而如果我们将返回值格式改一下
package com.zworks.aircraft.web.advice;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import java.util.HashMap;
import java.util.Map;
@ControllerAdvice
public class AirCraftExceptionHandler {
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity handleValidationExceptions(MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getAllErrors().forEach((error) -> {
String fieldName = ((FieldError) error).getField();
String errorMessage = error.getDefaultMessage();
errors.put(fieldName, errorMessage);
});
return new ResponseEntity<>(errors, new HttpHeaders(), HttpStatus.OK);//这里可以根据具体情况改变状态码
}
}
会发现handler变成了HttpEntityMethodProcessor
返回值也没问题了
{
"name": "名称不能为空"
}
请求参数
大部分人从网上查到了信息,都不会考虑请求参数该传那些,顺序有没有影响吧,受前面返回类型的影响,我也看了下请求参数相关的。
可以在自己的方法中打个断点,然后不断找上层调用。
在InvocableHandlerMethod
的invokeForRequest
方法中可以看到
@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
return doInvoke(args);
}
会获取参数,然后调用我们的方法
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
MethodParameter[] parameters = getMethodParameters();
if (ObjectUtils.isEmpty(parameters)) {
return EMPTY_ARGS;
}
Object[] args = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
args[i] = findProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
try {
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
}
catch (Exception ex) {
// Leave stack trace for later, exception may actually be resolved and handled...
if (logger.isDebugEnabled()) {
String exMsg = ex.getMessage();
if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
logger.debug(formatArgumentError(parameter, exMsg));
}
}
throw ex;
}
}
return args;
}
在findProvidedArgument
中会根据类型找参数
@Nullable
protected static Object findProvidedArgument(MethodParameter parameter, @Nullable Object... providedArgs) {
if (!ObjectUtils.isEmpty(providedArgs)) {
for (Object providedArg : providedArgs) {
if (parameter.getParameterType().isInstance(providedArg)) {
return providedArg;
}
}
}
return null;
}
providedArgs
中有两个类型,一个是org.springframework.web.bind.MethodArgumentNotValidException
类型,也就是异常,另一个是org.springframework.web.method.HandlerMethod
。所以如果单写一个异常是没问题的。
如果通过providedArgs
没取到,会从resolvers
里去取。
只要这些resolvers
能支持的参数,都可以取到。
比如看到了上面有ServletRequestMethodArgumentResolver
类,可以看到支持HttpSession
@Override
public boolean supportsParameter(MethodParameter parameter) {
Class<?> paramType = parameter.getParameterType();
return (WebRequest.class.isAssignableFrom(paramType) ||
ServletRequest.class.isAssignableFrom(paramType) ||
MultipartRequest.class.isAssignableFrom(paramType) ||
HttpSession.class.isAssignableFrom(paramType) ||
(pushBuilder != null && pushBuilder.isAssignableFrom(paramType)) ||
Principal.class.isAssignableFrom(paramType) ||
InputStream.class.isAssignableFrom(paramType) ||
Reader.class.isAssignableFrom(paramType) ||
HttpMethod.class == paramType ||
Locale.class == paramType ||
TimeZone.class == paramType ||
ZoneId.class == paramType);
}
我们测试下,在参数里添加一个HttpSession
package com.zworks.aircraft.web.advice;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.context.request.WebRequest;
import javax.servlet.http.HttpSession;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@ControllerAdvice
public class AirCraftExceptionHandler {
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity handleValidationExceptions(WebRequest request, MethodArgumentNotValidException ex, HttpSession httpSession) {
log.info("sessionId:{}", httpSession.getId());
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getAllErrors().forEach((error) -> {
String fieldName = ((FieldError) error).getField();
String errorMessage = error.getDefaultMessage();
errors.put(fieldName, errorMessage);
});
return new ResponseEntity<>(errors, new HttpHeaders(), HttpStatus.OK);//这里可以根据具体情况改变状态码
}
}
可以看到已经输出了sessionId
2019-09-07 23:15:43.257 INFO 25692 --- [nio-8080-exec-1] c.z.a.w.advice.AirCraftExceptionHandler : sessionId:B9C6E5BA4156BBBC5931FFE9B259E5ED
请求参数的处理方式和返回类型的处理方式是否很相似呢
后记
全局异常处理我也处理过很多次了,大部分也都是网上找一篇,虽然每次不会花很多时间,但是做了这么多遍也只是有个大概印象,并没有很深入的去探究过。整理一下,知识才会变成自己的。
看到了这里一定是真爱了,关注微信公众号【憨憨的春天】第一时间获取更新
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。