Spring Boot自定义Controller错误处理页

734 阅读2分钟

一切的起源来自与我使用 knife4j 时,一直被它这里默认有个 basic-error-controller 所困扰,一开始我不以为然,而后来我使用 openapi 自动生成前端接口时,因为文档中这个 basic-error-controller 导致代码看起来很奇怪。所以我经过一番搜索,找到了合适的解决方案

因为 knife4j 无法指定扫描的 controller 目录,以至于我一直无法处理掉这个 controller。 经过搜索发现,它是来自于 springboot 包下的一个 controller

罪魁祸首就是他们

spring boot 默认错误处理是这样的

  1. 浏览器,默认返回一个错误页面

  2. 如果是通过接口请求,它是这样的

它的原理大概是这样的

@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {
    
    @RequestMapping(produces = "text/html")//产生html类型的数据;浏览器发送的请求来到这个方法处理
	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 ? new ModelAndView("error", model) : modelAndView);
	}

	@RequestMapping
	@ResponseBody    //产生json数据,其他客户端来到这个方法处理;
	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<Map<String, Object>>(body, status);
	}

我们可以通过继承 BasicErrorController 重写 errorHtmlerror 方法修改返回的数据或者页面

又由于继承的 controller 写到了我们自己的包下,于是我们可以轻易控制在 knife4j 中的显示

例如像我这样,使用 thymeleaf 生成页面,让错误页面看起来也很不错

/**
 * 覆盖SpringBoot 默认错误处理器
 */
@Controller
@ApiIgnore
public class ErrorController extends BasicErrorController {
  public ErrorController(ErrorAttributes errorAttributes,
                         ServerProperties serverProperties,
                         List<ErrorViewResolver> errorViewResolvers) {
    super(errorAttributes, serverProperties.getError(), errorViewResolvers);
  }

  /**
   * 返回的html/text页面
   *
   * @param request
   * @param response
   * @return
   */
  @Override
  public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
    HttpStatus status = getStatus(request);
    // 获取 Spring Boot 默认提供的错误信息,然后添加一个自定义的错误信息
    Map<String, Object> model = getErrorAttributes(request,
        getErrorAttributeOptions(request, MediaType.TEXT_HTML));
    return new ModelAndView("errorPage", model, status);
  }

  /**
   * 返回的json数据
   *
   * @param request
   * @return
   */
  @Override
  public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
    HttpStatus status = getStatus(request);
    // 获取 Spring Boot 默认提供的错误信息,然后添加一个自定义的错误信息
    Map<String, Object> body = getErrorAttributes(request,
        getErrorAttributeOptions(request, MediaType.TEXT_HTML));
    return new ResponseEntity<>(R.error(ErrorCode.SYSTEM_ERROR).toMap(), status);
  }
}

templates/errorPage.html 中写入页面内容

<!doctype html>
<html lang="zh">
<head>
  <meta charset="UTF-8">
  <meta name="viewport"
        content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>系统错误</title>
  <style>
      样式...
  </style>
</head>
<body>
<div style="display: flex;flex-direction: column;justify-content: center;align-items: center;height: 100vh;">
  <svg width="254" height="294">
  可以放个图片
  </svg>
  <table>
  还可以将错误信息用表格的形式列出
    <tr>
      <td>timestamp</td>
      <td th:text="${timestamp}"></td>
    </tr>
    <tr>
      <td>status</td>
      <td th:text="${status}"></td>
    </tr>
    <tr>
      <td>error</td>
      <td th:text="${error}"></td>
    </tr>
    <tr>
      <td>message</td>
      <td th:text="${message}"></td>
    </tr>
    <tr>
      <td>path</td>
      <td th:text="${path}"></td>
    </tr>
  </table>
</div>
</body>
</html>

错误页面像这样