统一异常处理
当用spring boot开发后端时,我们常采用前后端分离的开发策略,这时候要求前端与后端需要进行数据交互,传统的一般采用json数据交互。
这时候我们要对spring boot的默认异常处理方式进行修改了,要统一返回数据格式,优雅的数据交互,优雅的开发应用。
首先我们要了解一般springboot的错误发生在什么地方。
一般发生在controller、业务层、filter中(如spring security)、拦截器中、还有就是未知错误了。
下面我分享一下我的处理方式: @RestControllerAdvice+filter+重写BasicErrorController的处理方式。
@RestControllerAdvice
@RestControllerAdvice+@ExceptionHandler(Exception.class)可以处理经过controller的异常错误
@RestControllerAdvice
public class BaseExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(BaseExceptionHandler.class);
/**
* 未知异常
* @param e 异常
* @return 统一结果返回
*/
@ExceptionHandler(Exception.class)
public R exception(Exception e) {
logger.error(e.getMessage(), e);
return R.error().message(e.getMessage());
}
@ExceptionHandler(NoHandlerFoundException.class)
public R noHandlerFoundException(){
return R.error().message("当前页面不存在");
}
}
filter捕获所有异常
当错误没有经过controller时,@RestControllerAdvice是不能捕获的,这时候就需要在filter进行处理。
public class ExceptionHandlerFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain){
try {
filterChain.doFilter(servletRequest, servletResponse);
} catch (Exception e) {
ResponseUtils.out((HttpServletResponse) servletResponse, R.error().message(e.getMessage()));
}
}
}
记得filter一定要注册到springboot才有作用,下面是我喜欢的一种方式
@Bean
FilterRegistrationBean<ExceptionHandlerFilter> exceptionFilterRegistrationBean() {
FilterRegistrationBean<ExceptionHandlerFilter> bean = new FilterRegistrationBean<>();
bean.setFilter(new ExceptionHandlerFilter());
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);//放在最前面
return bean;
}
未知异常处理
当发生的异常不知道在哪时,springboot会进行默认异常处理,跳转到 /error
这时我们要统一数据返回就必须要从这下手了,创建一个新的Controller处理/error,继承BasicErrorController,并覆盖其中的方法
@RestController
public class MyErrorController extends BasicErrorController {
@Autowired
public MyErrorController(ErrorAttributes errorAttributes,
ServerProperties serverProperties,
List<ErrorViewResolver> errorViewResolvers) {
super(errorAttributes, serverProperties.getError(), errorViewResolvers);
}
@Override
public ModelAndView errorHtml(HttpServletRequest request,
HttpServletResponse response) {
HttpStatus status = getStatus(request);
Map<String, Object> model = Collections
.unmodifiableMap(getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.TEXT_HTML)));
// return new ModelAndView("error", model, status);
ResponseUtils.out(response, R.error().code(status.value()).message((String) model.get("error")));
return null;
}
@Override
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
Map<String, Object> body = getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.ALL));
//可以换成项目中自定义的通信json
Map<String, Object> resultBody = new HashMap<>(16);
resultBody.put("success", false);
resultBody.put("code", body.get("status"));
resultBody.put("message", body.get("error"));
Map<String, Object> data = new HashMap<>();
data.put("timestamp", body.get("timestamp"));
data.put("path", body.get("path"));
resultBody.put("data", data);
return new ResponseEntity<>(resultBody, HttpStatus.OK);
}
}
配置文件
spring:
mvc:
throw-exception-if-no-handler-found: true
web:
resources:
add-mappings: false
server:
error:
include-exception: true
servlet:
encoding:
charset: utf-8
统一结果返回的工具类
ResponseUtils
public class ResponseUtils {
public static void out(HttpServletResponse response, R r) {
ObjectMapper mapper = new ObjectMapper();
response.setStatus(HttpStatus.OK.value());
response.setContentType("application/json;charset=UTF-8");
try {
mapper.writeValue(response.getWriter(), r);
} catch (IOException e) {
e.printStackTrace();
}
}
R
@Data
public class R implements Serializable {
private boolean success;
private Integer code;
private String message;
private Map<String,Object> data;
private R(){}
public static R ok(){
R r = new R();
r.setSuccess(true);
r.setCode(200);
r.setMessage("成功");
r.setData(new HashMap<>());
return r;
}
public static R error() {
R r = new R();
r.setSuccess(false);
r.setCode(500);
r.setMessage("失败");
r.setData(new HashMap<>());
return r;
}
public R success(Boolean success){
this.success = success;
return this;
}
public R code(Integer code){
this.code = code;
return this;
}
public R message(String message){
this.message = message;
return this;
}
public R data(String key, Object value){
this.data.put(key,value);
return this;
}
public R data(Map<String,Object> map){
this.setData(map);
return this;
}
}