Web 项目 tiger 之10 错误处理机制 及 定制错误页面

本文导读

默认错误处理机制

浏览器访问

  • Spring Boot 的 web 应用,当浏览器访问发生错误时,默认会如下所示一个错误页面,会有 时间戳、错误类型、状态码、以及错误消息等
  • 显然实际开发中程序员需要自己接手管理错误处理,定制错误页面提升用户体验

APP 访问

  • 如果不是浏览器访问,而是 其它的 APP 等应用 访问时,如果发生错误,则显然是无法给它重定向到错误页面的,而非以错误参数的形式返回,如下所示

区分原理

  • Spring Boot 是如何来区分用户是 浏览器请求还是 APP 等其它类型的请求呢?是根据请求头信息中的 " Accept " 字段判断的。

错误处理机制原理

  • Spring Boot 关于错误处理的自动配置可以参照 “ ErrorMvcAutoConfiguration ” 类
package org.springframework.boot.autoconfigure.web.servlet.error;
.......
@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class })
// Load before the main WebMvcAutoConfiguration so that the error View is available
@AutoConfigureBefore(WebMvcAutoConfiguration.class)
@EnableConfigurationProperties({ ServerProperties.class, ResourceProperties.class })
public class ErrorMvcAutoConfiguration {
........
  • ErrorMvcAutoConfiguration ” 类中给容器添加了以下几个关键的组件

DefaultErrorAttributes

  • 用于提供默认的错误属性,从而可以在页面获取这些信息并显示,如 错误类型、状态码、时间戳、错误信息等
    public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
        LinkedHashMap errorAttributes = new LinkedHashMap();
        errorAttributes.put("timestamp", new Date());
        this.addStatus(errorAttributes, webRequest);
        this.addErrorDetails(errorAttributes, webRequest, includeStackTrace);
        this.addPath(errorAttributes, webRequest);
        return errorAttributes;
    }
  • 页面上可以直接 使用 "$" 获取如下所示的信息:

timestamp:时间戳
status:状态码
error:错误提示
exception:异常对象
message:异常消息
errors:JSR303数据校验的错误都在这里

<!--页面获取值举例-->
<h1>status:[[${status}]]</h1>

<h2>timestamp:[[${timestamp}]]</h2>

<h2>message:<span th:text="${message}"></span></h2>

BasicErrorController

  • 处理默认的 /error 请求
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {

	private final ErrorProperties errorProperties;

    //产生 html 类型的数据;浏览器发送的请求如果错误时,就来到这个方法处理
	@RequestMapping(produces = "text/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 ? modelAndView : new ModelAndView("error", model));
	}


    //产生 json 数据,其它APP等客户端请求错误时,来到这个方法处理
	@RequestMapping
	@ResponseBody
	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);
	}

DefaultErrorViewResolver

  • 默认的错误视图解析器,即发生错误,应用应该去哪里找错误页面
............
public class DefaultErrorViewResolver implements ErrorViewResolver, Ordered {

	private static final Map<Series, String> SERIES_VIEWS;

	static {
		Map<Series, String> views = new EnumMap<>(Series.class);
		views.put(Series.CLIENT_ERROR, "4xx");
		views.put(Series.SERVER_ERROR, "5xx");
		SERIES_VIEWS = Collections.unmodifiableMap(views);
	}
	@Override
	public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status,
			Map<String, Object> model) {
		ModelAndView modelAndView = resolve(String.valueOf(status), model);
		if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
			modelAndView = resolve(SERIES_VIEWS.get(status.series()), model);
		}
		return modelAndView;
	}

	private ModelAndView resolve(String viewName, Map<String, Object> model) {
        // 默认 Spring Boot 会去到 error 目录下寻找,如:error/404.html
		String errorViewName = "error/" + viewName;

        //如果 error 目录位于模板引擎(templates)目录下,则使用模板引擎解析视图
		TemplateAvailabilityProvider provider = this.templateAvailabilityProviders
				.getProvider(errorViewName, this.applicationContext);
		if (provider != null) {
            //模板引擎可用的情况下返回到errorViewName指定的视图地址
			return new ModelAndView(errorViewName, model);
		}
        //模板引擎不可用时,就去静态资源文件夹下找errorViewName对应的页面 如:error/404.html
		return resolveResource(errorViewName, model);
	}
.......
}

定制错误页面

  • 通过上面的错误处理机制原理可知,错误页面默认全部放在 " error " 目录下,错误页面使用 http 错误代码进行命名,如 404.html、500.html 等。
  • 也可以使用 4xx、5xx 作为错误页面的泛指文件名,例如当存在 404.html 与 4xx.html ,访问发生404错误时,优先匹配进入404.html 页面,但是发生 400错误时,则进入了 4xx.html 页面
  • 约定的 error 目录默认可以放在模板引擎目录(templates)下,也可以放在4大静态资源目录下
  • 推荐放在 template 下,因为模板引擎下,可以使用 thymeleaf 模板引擎,静态文件夹下无法使用

templates 下

404. html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head lang="en">
    <meta charset="UTF-8">
    <title>404</title>
</head>
<body>
<!--使用 Thymeleaf 行内表达式赋值-->
<h1>status:[[${status}]]</h1>

<h2>timestamp:[[${timestamp}]]</h2>

<!--th方式赋值-->
<h2>message:<span th:text="${message}"></span></h2>
</body>
</html>

4xx. html

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title>4xx</title>
</head>
<body>

<!--使用 Thymeleaf 行内表达式赋值-->
<h1>status:[[${status}]]</h1>

<h2>timestamp:[[${timestamp}]]</h2>

<!--th方式赋值-->
<h2>message:<span th:text="${message}"></span></h2>
</body>
</html>

运行测试

  • 如下所示,因为后台要求日期是 "yyyy-MM-dd" 格式,所以提交时请求出错,发生400错误,跳转到了 4xx.html页面

  • 如下所示,因为请求地址不存在,发生404错误,跳转到了 404.html页面

静态资源目录 下

  • 页面仍然是上面的两个页面,现在移动到静态资源目录下

  • 如下所示,跳转是正常的,只是页面中无法再使用 thymelaf 模板引擎,只能自己写页面内容,无法获取后台具体的信息值,实际开发中通常不采用,了解即可

猜你喜欢

转载自blog.csdn.net/wangmx1993328/article/details/81486125
今日推荐