一、继承BasicErrorController
spring boot默认有处理异常的跳转页面,成为“Whilelabel Error Page”,spring boot的异常处理全都由ErrorMvcAutoConfiguration类来调度的,这个类中定义了一个BasicErrorController类,这个类中有两个方法:
1、处理页面请求(procedure=“text/html”)
@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
HttpStatus status = getStatus(request);
Map<String, Object> model = Collections
.unmodifiableMap(getErrorAttributes(request, isIncludeStackTrace(request, MediaType.TEXT_HTML)));
response.setStatus(status.value());
ModelAndView modelAndView = resolveErrorView(request, response, status, model);
return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
}
2、处理费页面请求(procedure=“application/json;charset=UTF-8”)
@RequestMapping
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL));
HttpStatus status = getStatus(request);
return new ResponseEntity<>(body, status);
}
我们只要继承BasicErrorController类,重写这两个方法就可以。下面是我们自己的代码
/**
* @Classname ErrorController
* @Description 自定义异常处理的Controller
* @Date 2019/7/31 20:00
* @Author by weikai
*/
@Controller
public class ErrorController extends BasicErrorController {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
MyErrorAttributes myErrorAttributes;
public ErrorController(ServerProperties serverProperties) {
super(new DefaultErrorAttributes(), serverProperties.getError());
}
/**
* 覆盖默认的Json响应
*/
@Override
@RequestMapping(produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
@ResponseBody
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
Map<String, Object> body = getErrorAttributes(request,
isIncludeStackTrace(request, MediaType.ALL));
HttpStatus status = getStatus(request);
//输出自定义的Json格式
Map<String, Object> map = new HashMap<String, Object>();
map.put("status", false);
map.put("msg", body.get("message"));
return new ResponseEntity<Map<String, Object>>(map, status);
}
/**
* 覆盖默认的HTML响应
*/
@Override
@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
//请求的状态
HttpStatus status = getStatus(request);
response.setStatus(getStatus(request).value());
WebRequest webRequest = new ServletWebRequest(request);
Map<String, Object> model = myErrorAttributes.getErrorAttributes(webRequest,
isIncludeStackTrace(request, MediaType.TEXT_HTML));
//设置流的字符集
OutputStream outputStream = null;
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:sss");
String realPageContent ;
response.setHeader("content-type", "text/html;charset=utf-8");
try {
outputStream = response.getOutputStream();
String pageContent = ErrorPathConstant.PAGE_ERROR;
realPageContent = String.format(pageContent,model.get("timestamp"),model.get("status"),model.get("message"));
outputStream.write(realPageContent.getBytes("UTF-8"));
} catch (Exception e) {
logger.error("获取响应的的流失败",e);
}finally {
IOUtils.closeQuietly(outputStream);
}
return null;
}
}
在返回到错误页面的方法中,我们用了response.getOutputStream()获得流,然后在write()到页面的形式。
我们还需要重写DefaultErrorAttributes类的getErrorAttributes,这里是对model里的信息做的处理。先看一下源码:
@Override
public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
Map<String, Object> errorAttributes = new LinkedHashMap<>();
errorAttributes.put("timestamp", new Date());
addStatus(errorAttributes, webRequest);
addErrorDetails(errorAttributes, webRequest, includeStackTrace);
addPath(errorAttributes, webRequest);
return errorAttributes;
}
我们的MyErrorAttributes重写了代码如下:
/**
* @Classname MyErrorAttributes
* @Description 自定义errorAttributes
* @Date 2019/8/1 12:11
* @Author by weikai
*/
@Configuration
@ConditionalOnProperty(name = "bean.valid.normal",havingValue = "true")
public class MyErrorAttributes extends DefaultErrorAttributes {
@Override
public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
Map<String, Object> errorAttributes = super.getErrorAttributes(webRequest,includeStackTrace);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
errorAttributes.put("timestamp", simpleDateFormat.format(new Date()));
return errorAttributes;
}
}
//这里就修改了timestamp的格式
这里说明一下,error这个方法被触发是在请求为ajax请求或者通过jmeter等工具触发的非页面请求
看一下效果,用页面请求得到的错误页面

用工具调用得到的json格式的错误信息

二、写通知
用@ControllerAvice("kai.personal.wei")这个注解扫描基础包,@ExceptionHandler(Exception.class)表示要捕捉的异常类型,这样就可以有好的处理程序中抛出的异常了。代码如下:
/**
* @Classname AcmeControllerAdvice
* @Description 处理错误的方法类
* @Date 2019/7/30 16:29
* @Author by weikai
*/
@ControllerAdvice("kai.personal.wei")
@ConditionalOnProperty(name = "bean.valid.normal",havingValue = "true")
public class AcmeControllerAdvice extends ResponseEntityExceptionHandler {
@ExceptionHandler(Exception.class)
@ResponseBody
ResponseEntity<?> handleControllerException(HttpServletRequest request, Throwable throwable) {
HttpStatus status = getStatus(request);
return new ResponseEntity<>(new ResponseDto(status.value(), throwable.getMessage()), status);
}
private HttpStatus getStatus(HttpServletRequest request) {
Integer statusCode = (Integer) request.getAttribute("javax.servlet.customerror.status_code");
if (statusCode == null) {
return HttpStatus.INTERNAL_SERVER_ERROR;
}
return HttpStatus.valueOf(statusCode);
}
}
当我的请求报错之后,会进入到我写的这个通知里,并把异常友好的打印出来。

需要注意的是,reqeust.getAtrribute("javax.servlet.error.status_code")是404,500等状态码的key,
org.springframework.boot.web.servlet.error.DefaultErrorAttributes.ERROR为错误信息的key。
三、在tomcat中配置ErrorPages,修改错误信息的映射
写一个配置类,配置各种错误的映射路径,如下
/**
* @Classname WebServerAutoConfiguration
* @Description 这个是tomcat的错误页面的配置类
* @Date 2019/8/1 10:58
* @Author by weikai
*/
@Configuration
public class WebServerAutoConfiguration {
@Bean
public ConfigurableServletWebServerFactory webServerFactory() {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
// ErrorPage errorPage400 = new ErrorPage(HttpStatus.BAD_REQUEST, "/customerror/400.html");
ErrorPage errorPage400 = new ErrorPage(HttpStatus.BAD_REQUEST, "/customerror/400");
// ErrorPage errorPage401 = new ErrorPage(HttpStatus.UNAUTHORIZED, "/customerror/401.html");
ErrorPage errorPage401 = new ErrorPage(HttpStatus.UNAUTHORIZED, "/customerror/401");
// ErrorPage errorPage403 = new ErrorPage(HttpStatus.FORBIDDEN, "/customerror/403.html");
ErrorPage errorPage403 = new ErrorPage(HttpStatus.FORBIDDEN, "/customerror/403");
// ErrorPage errorPage404 = new ErrorPage(HttpStatus.NOT_FOUND, "/customerror/404.html");
ErrorPage errorPage404 = new ErrorPage(HttpStatus.NOT_FOUND, "/customerror/404");
// ErrorPage errorPage415 = new ErrorPage(HttpStatus.UNSUPPORTED_MEDIA_TYPE, "/customerror/415.html");
ErrorPage errorPage415 = new ErrorPage(HttpStatus.UNSUPPORTED_MEDIA_TYPE, "/customerror/415");
// ErrorPage errorPage500 = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/customerror/500.html");
ErrorPage errorPage500 = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/customerror/500");
factory.addErrorPages(errorPage400, errorPage401, errorPage403, errorPage404, errorPage415, errorPage500);
return factory;
}
}
这个类是把各个错误状态码维护到TomcatServletWebServerFactory中,每个状态码都有各自的吊装路径,可以是一个controller,也可以是一个html。这里用的是controller,看一下controller的代码
/**
* @Classname HttpErrorStatusController
* @Description 配置error映射
* @Date 2019/8/1 14:28
* @Author by weikai
*/
@Controller
@RequestMapping("customerror")
public class HttpErrorStatusController {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@RequestMapping("404")
public void get404(HttpServletRequest request, HttpServletResponse response) {
logger.info("==========这个是404的错误处理器");
OutputStream outputStream = null;
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:sss");
String realPageContent ;
//设置response的字符集
response.setHeader("content-type", "text/html;charset=utf-8");
try {
outputStream = response.getOutputStream();
String pageContent = ErrorPathConstant.PAGE_404;
String dateStr = simpleDateFormat.format(new Date());
realPageContent = String.format(pageContent,dateStr);
if (response.getContentType() == null) {
response.setContentType(MediaType.TEXT_HTML_VALUE);
}
outputStream.write(realPageContent.getBytes("UTF-8"));
} catch (Exception e) {
logger.error("获取响应的的流失败",e);
}finally {
IOUtils.closeQuietly(outputStream);
}
}
@RequestMapping("500")
public void get500(HttpServletRequest request, HttpServletResponse response) {
logger.info("==========这个是500的错误处理器");
//设置流的字符集
OutputStream outputStream = null;
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:sss");
String realPageContent ;
response.setHeader("content-type", "text/html;charset=utf-8");
try {
outputStream = response.getOutputStream();
String pageContent = ErrorPathConstant.PAGE_404;
String dateStr = simpleDateFormat.format(new Date());
realPageContent = String.format(pageContent,dateStr);
if (response.getContentType() == null) {
response.setContentType(MediaType.TEXT_HTML_VALUE);
}
outputStream.write(realPageContent.getBytes("UTF-8"));
} catch (Exception e) {
logger.error("获取响应的的流失败",e);
}finally {
IOUtils.closeQuietly(outputStream);
}
}
}
请求的程序中出现了500的错,则

这些主要都是我的笔记,有什么不懂得地方可以留言,大家一起探讨一下。