之所以写这篇文章,是因为在项目中处理全局异常是发现自定义Intercepter中的异常没发通过 @ControllerAdvice
捕获。
项目中使用 @ControllerAdvice
处理了controller中的异常,但发现在自定义拦截器( extends HandlerInterceptorAdapter )中抛出的异常没有被拦截,跳转到了/error下,所以想要重写/error请求,过程比较曲折,现将测试通过的代码进行记录。
最终结果如下:
package com.xxx.core.controller; import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.web.ErrorProperties; import org.springframework.boot.autoconfigure.web.ErrorProperties.IncludeStacktrace; import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.boot.autoconfigure.web.servlet.error.AbstractErrorController; import org.springframework.boot.web.servlet.error.ErrorAttributes; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.servlet.ModelAndView; import lombok.extern.slf4j.Slf4j; @Controller @RequestMapping("${server.error.path:${error.path:/error}}") @Slf4j public class CustomerExceptionController extends AbstractErrorController { private final ErrorProperties errorProperties; @Autowired public CustomerExceptionController(ErrorAttributes errorAttributes, ServerProperties serverProperties) { super(errorAttributes); this.errorProperties = serverProperties.getError(); } @Override public String getErrorPath() { return errorProperties.getPath(); } @RequestMapping(produces = "text/html") public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) { ModelAndView modelAndView = new ModelAndView("error"); Map<String, Object> errorMap = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL)); if (errorMap != null) { /* timestamp status error message path */ modelAndView.addObject("msg", errorMap.get("error")); modelAndView.addObject("statusCode", errorMap.get("status")); logHandler(errorMap); } return modelAndView; } @RequestMapping @ResponseBody public ResponseEntity<JsonResult<Map<String, Object>>> error(HttpServletRequest request) { Map<String, Object> errorMap = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.APPLICATION_JSON)); logHandler(errorMap); Map<String, Object> data = new HashMap<>(); data.putAll(errorMap); data.remove("trace"); // HttpStatus status = getStatus(request); return new ResponseEntity<>(JsonResult.failure("", data), HttpStatus.OK); } private void logHandler(Map<String, Object> errorMap) { log.error("url:{},status{},time:{},errorMsg:{}", errorMap.get("path"), errorMap.get("status"), errorMap.get("timestamp"), errorMap.get("message")); } protected boolean isIncludeStackTrace(HttpServletRequest request, MediaType produces) { IncludeStacktrace include = getErrorProperties().getIncludeStacktrace(); if (include == IncludeStacktrace.ALWAYS) { return true; } if (include == IncludeStacktrace.ON_TRACE_PARAM) { return getTraceParameter(request); } return false; } private ErrorProperties getErrorProperties() { return this.errorProperties; } }
本来想直接继承 BasicErrorController,重写error方法 完事,结果发现继承后启动报错,提示没有ErrorProperties。
后来找到一篇文章,构造方法中注入的是ServerProperties serverProperties(和本例类似),通过serverProperties获取ErrorProperties,修改后提示没有无参的构造方法。
心好累,不想努力了,就直接继承了AbstractErrorController,抄了一些BasicErrorController的内容成了上面的代码。完美解决了纠结的问题。
注意事项
拦截器中要排除 /error路径
@Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(authorizeIntercepter).addPathPatterns("/**").excludePathPatterns("/login", "/login/**", "/static/**", "/webjars/**", "/swagger-ui.html/**", "/swagger-resources/**","/v2/**","/error"); }