spring-boot全局异常处理及统一返回体
1:统一异常实体
import org.apache.tomcat.util.http.fileupload.FileUploadBase;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.multipart.MultipartException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
@RestControllerAdvice
public class ControllerExceptionAdvice {
private static final Logger logger = LoggerFactory.getLogger(ControllerExceptionAdvice.class);
public ControllerExceptionAdvice() {
logger.info("ControllerExceptionAdvice init ");
}
@ExceptionHandler(Throwable.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public RespData handleOtherException(HttpServletRequest request, Throwable e) {
String path = request.getRequestURI();
logger.error("系统发生未知异常,url:{}", path, e);
return RespData.fail(HttpStatus.INTERNAL_SERVER_ERROR.value(),e.getMessage());
}
@ExceptionHandler(MultipartException.class)
public ResponseEntity<RespData> handleException(HttpServletRequest request, MultipartException e) {
String path = request.getRequestURI();
HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
RespData respData = null;
Throwable sizeCause = causeBy(e, FileUploadBase.SizeLimitExceededException.class);
if (sizeCause != null) {
logger.warn("文件上传大小超过限制,url:{}", path, e);
status = HttpStatus.PAYLOAD_TOO_LARGE;
respData = RespData.fail(HttpStatus.PAYLOAD_TOO_LARGE);
return new ResponseEntity(respData, status);
}
Throwable fileSizeCause = causeBy(e, FileUploadBase.FileSizeLimitExceededException.class);
if (fileSizeCause != null) {
logger.warn("文件上传大小超过限制,url:{}", path, e);
status = HttpStatus.PAYLOAD_TOO_LARGE;
respData = RespData.fail(HttpStatus.PAYLOAD_TOO_LARGE);
} else {
respData = RespData.fail(HttpStatus.INTERNAL_SERVER_ERROR);
logger.error("文件上传error,url:{}", path, e);
}
return new ResponseEntity(respData, status);
}
private static Throwable causeBy(Throwable throwable, Class cause) {
if (throwable.getCause() == null && !throwable.getClass().equals(cause)) {
return null;
}
if (throwable.getClass().equals(cause)) {
return throwable;
}
return causeBy(throwable.getCause(), cause);
}
@ExceptionHandler(FrontException.class)
public RespData handleFrontException(HttpServletRequest request, Throwable e) {
String path = request.getRequestURI();
FrontException exception = (FrontException) e;
logger.warn("错误的请求,url={},exception={}", path, e.toString(), e);
if (exception.getCode() != null) {
return RespData.fail(exception.getCode(), exception.getMsg());
}
return RespData.fail(-1, exception.getMsg());
}
@ExceptionHandler(ServletException.class)
public RespData handleServletException(HttpServletRequest request, ServletException e) {
String path = request.getRequestURI();
if (e instanceof MissingServletRequestParameterException) {
MissingServletRequestParameterException exception = (MissingServletRequestParameterException) e;
if (logger.isInfoEnabled()) {
logger.info("{}缺少参数{}", path, exception.getParameterName(), e);
}
return RespData.fail(400, "缺少参数:" + exception.getParameterName());
}
logger.warn("bad request,url:{}", path, e);
return RespData.fail(400, "缺少参数");
}
@ExceptionHandler({HttpMessageNotReadableException.class, BindException.class, MethodArgumentTypeMismatchException.class})
@ResponseStatus(HttpStatus.BAD_REQUEST)
public RespData handleHttpMessageNotReadableException(HttpServletRequest request, Throwable e) {
String path = request.getRequestURI();
logger.warn("参数错误,url:{}", path, e);
return RespData.fail(400, "参数异常");
}
@ExceptionHandler({IllegalArgumentException.class})
@ResponseStatus(HttpStatus.BAD_REQUEST)
public RespData handleIllegalArgumentException(HttpServletRequest request, Throwable e) {
String path = request.getRequestURI();
logger.warn("参数错误,url={},exception={}", path, e.toString(), e);
return RespData.fail(FrontExceptionCodeEnum.API_REQUEST_PARAM_ERROR, e.getMessage());
}
}
2: 实体返回类型的处理
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.Order;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter;
import org.springframework.http.converter.json.MappingJacksonValue;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.lang.Nullable;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
@ControllerAdvice(basePackages = "com.jdt.uj.marketing.front.web.controllers")
@Order(1)
public class MappingJacksonRespDataResponseBodyAdvice implements ResponseBodyAdvice<Object> {
public MappingJacksonRespDataResponseBodyAdvice() {
System.out.println("MappingJacksonRespDataResponseBodyAdvice init");
}
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return AbstractJackson2HttpMessageConverter.class.isAssignableFrom(converterType);
}
@Override
@Nullable
public Object beforeBodyWrite(@Nullable Object body, MethodParameter returnType,
MediaType contentType, Class<? extends HttpMessageConverter<?>> converterType,
ServerHttpRequest request, ServerHttpResponse response) {
MappingJacksonValue container = getOrCreateContainer(body);
beforeBodyWriteInternal(container, contentType, returnType, request, response);
return container;
}
private MappingJacksonValue getOrCreateContainer(Object body) {
return (body instanceof MappingJacksonValue ? (MappingJacksonValue) body : new MappingJacksonValue(body));
}
private void beforeBodyWriteInternal(MappingJacksonValue bodyContainer, MediaType contentType, MethodParameter returnType, ServerHttpRequest request, ServerHttpResponse response) {
Object returnValue = bodyContainer.getValue();
if (returnValue instanceof RespData) {
return;
}
RespData respData = RespData.success(returnValue);
bodyContainer.setValue(respData);
}
}
3: 字符串返回类型的处理
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jdt.uj.marketing.front.common.model.RespData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.http.HttpProperties;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
@ControllerAdvice(basePackages = "com.jdt.uj.marketing.front.web.controllers")
@Order(1)
public class StringRespDataResponseBodyAdvice implements ResponseBodyAdvice {
@Autowired
private HttpProperties encodingProperties;
ObjectMapper objectMapper = new ObjectMapper();
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
return StringHttpMessageConverter.class.isAssignableFrom(converterType);
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
MediaType jsonType = new MediaType(MediaType.APPLICATION_JSON, encodingProperties.getEncoding().getCharset());
HttpHeaders headers = response.getHeaders();
headers.setContentType(jsonType);
try {
return objectMapper.writeValueAsString(RespData.success(body));
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return body;
}
}