SpringBoot04: 错误处理机制

1、 默认处理机制

​ 根据 Http 请求头(Request Headers )中,Accept中信息决定返回 html 页面还是 json 数据。

  • 浏览器请求 text/html – 返回html页面
  • 在这里插入图片描述
    默认返回页面(html白页)
    在这里插入图片描述
  • 其他客户端(postman)请求 */* – 返回json数据
    在这里插入图片描述
    默认返回的json信息
    在这里插入图片描述

2、默认处理原理

  • 主要参见ErrorMvcAutoConfiguration自动配置类
// 条件注入组件: DefaultErrorAttributes
@Bean
@ConditionalOnMissingBean(
	value = {ErrorAttributes.class},
	search = SearchStrategy.CURRENT
)
public DefaultErrorAttributes errorAttributes() {
	return new DefaultErrorAttributes(this.serverProperties.getError().isIncludeException());
}

// 条件注入组件: BasicErrorController
@Bean
@ConditionalOnMissingBean(
    value = {ErrorController.class},
     search = SearchStrategy.CURRENT
)
public BasicErrorController basicErrorController(ErrorAttributes errorAttributes) {
	return new BasicErrorController(errorAttributes, 
		this.serverProperties.getError(), this.errorViewResolvers);
}

// ErrorPageCustomizer(产生错误,发出什么处理请求)
@Bean
public ErrorMvcAutoConfiguration.ErrorPageCustomizer errorPageCustomizer() {
	return new ErrorMvcAutoConfiguration.ErrorPageCustomizer(this.serverProperties,
                this.dispatcherServletPath);
}

// 默认处理页
static class StaticView {...}
  • ErrorPageCustomizer
private static class ErrorPageCustomizer implements ErrorPageRegistrar, Ordered {
    ......
    public void registerErrorPages(ErrorPageRegistry errorPageRegistry) {
        // 这里默认的请求 /error
        ErrorPage errorPage = new ErrorPage(this.dispatcherServletPath
                 .getRelativePath(this.properties.getError().getPath()));
        errorPageRegistry.addErrorPages(new ErrorPage[]{errorPage});
    }
}
  • BasicErrorController:处理默认/error请求

    当产生错误时,会去请求 /error

@Controller
@RequestMapping({"${server.error.path:${error.path:/error}}"})
public class BasicErrorController extends AbstractErrorController {
    ......
    // 产生html类型的数据。浏览器发送的请求来到这个方法处理
    @RequestMapping(
        produces = {"text/html"}
    )
    public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
        HttpStatus status = this.getStatus(request);
        Map<String, Object> model = Collections.unmodifiableMap(this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.TEXT_HTML)));
        response.setStatus(status.value());
        // 去哪个页面作为错误页面;包含页面地址和页面内容  
        ModelAndView modelAndView = this.resolveErrorView(request, response, status, model);
        return modelAndView != null ? modelAndView : new ModelAndView("error", model);
    }

    // 产生json数据,其他客户端来到这个方法处理
    @RequestMapping
    public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
        Map<String, Object> body = this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.ALL));
        HttpStatus status = this.getStatus(request);
        return new ResponseEntity(body, status);
    }
}
  • DefaultErrorViewResolver:默认错误视图解析
public class DefaultErrorViewResolver implements ErrorViewResolver, Ordered {
	...
    // 视图解析
    public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
        ModelAndView modelAndView = this.resolve(String.valueOf(status.value()), model);
        if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
            modelAndView = this.resolve((String)SERIES_VIEWS.get(status.series()), model);
        }
        return modelAndView;
    }

    private ModelAndView resolve(String viewName, Map<String, Object> model) {
        // 默认寻找 error/xxx页面,如 error/404
        String errorViewName = "error/" + viewName;
        // 模板引擎可以解析这个页面地址就用模板引擎解析
        TemplateAvailabilityProvider provider = this.templateAvailabilityProviders
                             .getProvider(errorViewName, this.applicationContext);
        // 模板引擎可用返回指定的视图模型,否则在资源文件夹下寻找页面
        return provider != null ? new ModelAndView(errorViewName, model) 
                                : this.resolveResource(errorViewName, model);
    }
}
  • DefaultErrorAttributes: 提供的默认错误信息

    帮助我们在页面展示出错信息。

public class DefaultErrorAttributes 
    implements ErrorAttributes, HandlerExceptionResolver, Ordered {
 	......
    public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
        Map<String, Object> errorAttributes = new LinkedHashMap();
        errorAttributes.put("timestamp", new Date());   // 错误产生的时间
        this.addStatus(errorAttributes, webRequest);    // 状态码等
        this.addErrorDetails(errorAttributes, webRequest, includeStackTrace);  // 出错信息
        this.addPath(errorAttributes, webRequest);
        return errorAttributes;
    }
    ......
}

3、自定义错误页面

​ 在模板文件夹templates下,新建error文件夹,里面放置错误页面,匹配时由精确到模糊。

  • 4xx.html 模糊,4开头类型错误的处理页面
  • 5xx.html 模糊,5开头类型错误的处理页面
  • 404.html 精确,404类型,错误的处理页面

4、异常跳转错误页面

​ 在@ControllerAdvice注解下的类,里面的方法用@ExceptionHandler注解修饰的方法,会将对应的异常交给对应的方法处理。

注意:

  1. @ExceptionHandler注解参数可以传入数组,处理多种异常
  2. 不能使用两个@ExceptionHandler处理同一种异常
@ControllerAdvice
public class CustomizeExceptionHandler {
    // 返回渲染的视图
    @ExceptionHandler(CustomizeException.class)  // 处理自定义CustomizeException异常
    ModelAndView handle(HttpServletRequest request, Throwable e, Model model) {
        model.addAttribute("message", e.getMessage());
        return new ModelAndView("/error");
    }
    
    // 返回json数据
    @ResponseBody
    @ExceptionHandler(UserNotExistException.class)  // 处理自定义UserNotExistException异常
    ModelAndView handle(HttpServletRequest request, Throwable e, Model model) {
        Map<String, String> map = new HashMap<>();
        map.put("code", "not exist");
        map.put("msg", e.getMessage());
        return map;
    }
}

5、 定制自适应错误页面

​ 出现错误以后,会来到/error请求,会被BasicErrorController处理,响应出去可以获取的数据是由
getErrorAttributes得到的(是AbstractErrorController(ErrorController)规定的方法)

​ 页面上能用的数据,或者是json返回能用的数据都是通过getErrorAttributes得到;
容器中默认使用DefaultErrorAttributes.getErrorAttributes()得到,我们可以继承DefaultErrorAttributes来自定义返回的数据。

// 自定义异常
public class UserNotExistException extends RuntimeException {
    public UserNotExistException() {
        super("用户找不到");
    }
}

// 请求处理出现异常
@ResponseBody
@RequestMapping("/aaa")
public String aaa(@RequestParam("user") String user) {
    if ("aaa".equals(user)) {
        throw new UserNotExistException();  //抛出自定义异常
    }
    return "/";
}

// 异常被捕获
@ControllerAdvice
public class MyExceptionHandler {
	@ExceptionHandler(UserNotExistException.class)  // 处理自定义UserNotExistException异常
    public String handleUserNotExistException(Exception e, HttpServletRequest request) {
        Map<String, String> map = new HashMap<>();
        // 重要: 传入我们自己的错误状态码
        request.setAttribute("javax.servlet.error.status_code", 500);
        map.put("code", "not exist");     // 一些该异常的错误信息
        map.put("msg", e.getMessage());
        // 把数据放入request域
        request.setAttribute("ext", map);
        return "forward:/error";
    }
}

// 容器中添加自定义的ErrorAttributes,不再使用默认的DefaultErrorAttributes
// 获得不同客户端自适应效果
@Component
public class MyErrorAttributes extends DefaultErrorAttributes {
    // 返回的map就是页面和json能获取的所有字段
    @Override
    public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
        Map<String, Object> map = super.getErrorAttributes(webRequest, includeStackTrace);
        map.put("company", "moc");  // 自定义通用的数据
        // 加入异常处理器携带的数据
        Map<String, Object> ext = (Map<String, Object>) webRequest.getAttribute("ext", 0);
        map.put("ext", ext);

        return map;
    }
}

// 页面获取数据(thymeleaf)
<body>
<h1>status:[[${status}]]</h1>
<h1>timestamp:[[${timestamp}]]</h1>
<h1>trace:[[${trace}]]</h1>
<h1>message:[[${message}]]</h1>
<h1>company:[[${company}]]</h1>
<h1>ext:[[${ext.code}]]</h1>
<h1>ext:[[${ext.msg}]]</h1>
</body>
  • map数据
    在这里插入图片描述
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值