框架技术---SpringBoot(五)WEB开发《四》WEB开发错误处理

SpringBoot错误处理机制

1.SpringBoot默认的错误处理机制

默认效果:

  • 返回一个默认的错误页面;

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ANlDBuFg-1575170934856)(F:\noteAtom\img\404.png)]

2.原理

参考:ErrorMvcAutoConfiguration;错误处理的自动配置。

给容器中添加了如下组件:

1.DefaultErrorAttributes:

​ 是默认响应错误页面上的数据参数,可以通过继承该方法添加自己想添加的参数

2.BasicErrorController:处理默认的/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) {
        HttpStatus status = this.getStatus(request);
        if (status == HttpStatus.NO_CONTENT) {
            return new ResponseEntity(status);
        } else {
            Map<String, Object> body = this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.ALL));
            return new ResponseEntity(body, status);
        }
    }

3.ErrorPageCustomizer:定制错误的规则

@Value("${error.path:/error}")
private String path = "/error";系统出现错误以后来到error请求进行处理(类似web.xml注册的错误页面规则);

4.DefaultErrorViewResolver:通过模板引擎加载默认的错误页面

 //方法1 解析错误页面
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;
    }


// 放法2 上面方法1要调用该方法
    private ModelAndView resolve(String viewName, Map<String, Object> model) {
        //springboot默认可以去找到的一个页面?errer/404(状态码)
        String errorViewName = "error/" + viewName;
        
        //模板引擎可以解析这个页面地址就用这个
        TemplateAvailabilityProvider provider = this.templateAvailabilityProviders.getProvider(errorViewName, this.applicationContext);
        
        //模板可用调用ModelAndview这方法返回这个页面;不可用则调用resolveResource方法
        return provider != null ? new ModelAndView(errorViewName, model) : this.resolveResource(errorViewName, model);
    }


//方法3 方法2中默认的模板引擎不可用,要调用该方法
private ModelAndView resolveResource(String viewName, Map<String, Object> model) {
    //  要在静态资源文件夹下找errorViewName对应的页面
    String[] var3 = this.resourceProperties.getStaticLocations();
        int var4 = var3.length;

        for(int var5 = 0; var5 < var4; ++var5) {
            String location = var3[var5];

            try {
                Resource resource = this.applicationContext.getResource(location);
                resource = resource.createRelative(viewName + ".html");
                if (resource.exists()) {
                    return new ModelAndView(new DefaultErrorViewResolver.HtmlResourceView(resource), model);
                }
            } catch (Exception var8) {
                ;
            }
        }

        return null;
    }

步骤:

一旦系统出现4xx或者是5xx之类的错误;

1.ErrorPageCustomizer就会生效(定制错误的相应规则);

2.就会来到/error请求;就会被BasicErrorController处理;

3.然后通过DefaultErrorViewResolver寻找响应的页面。

​ 1)、响应页面:去哪个页面是由DefaultErrorViewResolver解析得到的:

protected ModelAndView resolveErrorView(HttpServletRequest request, HttpServletResponse response, HttpStatus status, Map<String, Object> model) {
        Iterator var5 = this.errorViewResolvers.iterator();

        ModelAndView modelAndView;
        do {
            if (!var5.hasNext()) {
                return null;
            }

            ErrorViewResolver resolver = (ErrorViewResolver)var5.next();
            modelAndView = resolver.resolveErrorView(request, status, model);
        } while(modelAndView == null);

        return modelAndView;
    }
3.如何订制错误响应:
1)、 如何定制错误页面(系统查找404页面的顺序)

​ )、有模板引擎的情况下;error/状态码(将错误页面命名为:状态码.html放在模板引擎文件夹里面的error文件夹下)发生此状态码的错误就来来到 对应的页面;

​ 我们可以使用4xx和5xx作为错误页面的文件名来匹配这种类型的所有错误,精确优先(优先寻找精确的状态码.html)

​ 页面可以获取的信息:

​ timestamp:时间戳

​ status:状态码

​ error:错误提示

	 exception:异常对象

​ message:异常消息

​ errors:JSR303数据校验的错误

​ 2)、没有模板引擎的情况下:(模板引擎找不到这个错误页面),静态资源文件下找;

​ 3)、以上都没有错误页面,就是默认来到Springboot默认的空白错误提示页面。

2)、如何定制错误的json 数据 (推荐使用第三种)

​ 1)、自定义异常处理&返回定制的json数据;

@ControllerAdvice
public class MyExceptionHandler{
 // 这种情况的缺点不是自适应的;浏览器和客户端返回的都是json
    @ResponseBody
    @ExceptionHandler(UserNotExistException.class)
    public Map<String, Object> handleException(Exception e){
        Map<String,Object> map = new HashMap<>();
        map.put("code","userNotExist");
        map.put("message",e.getMessage());
        return map;

    }

​ 2)、定制可以自适应的异常处理

// 将自适应交给springboot默认的去执行
 @ExceptionHandler(UserNotExistException.class)
    public String handleException(Exception e, HttpServletRequest request){
        Map<String,Object> map = new HashMap<>();
        // 传入我门自己的错误状态码
        request.setAttribute("javax.servlet.error.status_code",500);
        map.put("code","userNotExist");
        map.put("message",e.getMessage());
        return "forward:/error";

    }

​ 3)、将我们的定制数据携带出去

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

​ 实现方法:(推荐使用第二种ErrorAttributes)

  1. 完全来编写一个ErrorController的实现类(或者是编写AbstractErrorController的子类),放在容器中。

  2. 页面上返回到的数据,或者json返回的。都是通过errorAttributes.getErrorAttributes得到的;容器中DefaultErrorAttributes.getErrorAttributes();默认进行处理,我们只需要自己写一个

    @Bean
    // 条件判断是否有ErrorAttributes这个类,没有才调用DefaultErrorAttributes
        @ConditionalOnMissingBean(
            value = {ErrorAttributes.class},
            search = SearchStrategy.CURRENT
        )
        public DefaultErrorAttributes errorAttributes() {
            return new DefaultErrorAttributes(this.serverProperties.getError().isIncludeException());
        }
    

    因此:我们可以自己实现一个ErrorAttributes类让其继承DefaultErrorAttributes类。

/**
 * 容器中加入自定义的ErrorAttributes
 */
@Component
public class ErrorAttributes extends DefaultErrorAttributes {

    @Override
    public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
        Map<String, Object> errorAttributes = super.getErrorAttributes(webRequest, includeStackTrace);
        errorAttributes.put("conpany","tit");
        return errorAttributes;
    }
}

客户端响应:

在这里插入图片描述

总结:

在开发过程中自定义一套错误处理:

1.使用模板引擎方式,添加自己的错误页面。

<main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-4"><div style="position: absolute; inset: 0px; overflow: hidden; pointer-events: none; visibility: hidden; z-index: -1;" class="chartjs-size-monitor"><div class="chartjs-size-monitor-expand" style="position:absolute;left:0;top:0;right:0;bottom:0;overflow:hidden;pointer-events:none;visibility:hidden;z-index:-1;"><div style="position:absolute;width:1000000px;height:1000000px;left:0;top:0"></div></div><div class="chartjs-size-monitor-shrink" style="position:absolute;left:0;top:0;right:0;bottom:0;overflow:hidden;pointer-events:none;visibility:hidden;z-index:-1;"><div style="position:absolute;width:200%;height:200%;left:0; top:0"></div></div></div>
        使用的是springboot默认的参数
        <h1>status:[[${status}]]</h1>
        <h2>timestamp:[[${timestamp}]]</h2>
        <h3>exception:[[${exception}]]</h3>
        使用的是MyExceptionHandler中自定义的参数
        <h3>message:[[${message}]]</h3>
        使用的是自己定制的ErrorAttributes的参数
        <h2>conpany:[[${conpany}]]</h2>
		使用的是MyExceptionHandler中自定义的参数
        ext:[[${ext.message}]]

    </main>

2.定制自己的异常错误类

public class UserNotExistException extends RuntimeException {

    public UserNotExistException() {
        super("用户不存在");
    }
}

3.定制可以自适应的异常处理器

// 将自适应交给springboot默认的去执行(捕获自定义的异常类,不用让其出现在控制台)
@ControllerAdvice
public class MyExceptionHandler{
    @ExceptionHandler(UserNotExistException.class)
    public String handleException(Exception e, HttpServletRequest request){
        Map<String,Object> map = new HashMap<>();
        // 传入我门自己的错误状态码
        request.setAttribute("javax.servlet.error.status_code",500);
        map.put("code","userNotExist");
        map.put("message",e.getMessage());
        request.setAttribute("ext",map);
        //重定向到springboot默认的异常解析器地址
        return "forward:/error";

    }


}

4.定制一个ErrorAttributes类让其继承DefaultErrorAttributes类改变需要返回的内容。

/**
 * 容器中加入自定义的ErrorAttributes
 */
@Component
public class ErrorAttributes extends DefaultErrorAttributes {

//    返回值的map就是页面和json能获取的所有字段
    @Override
    public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
        Map<String, Object> errorAttributes = super.getErrorAttributes(webRequest, includeStackTrace);
        errorAttributes.put("conpany","tit");
        // 将reqest域中的自己定制的异常处理器添加的数据也添加进来,0代表request域
        Map<String,Object> ext = (Map<String, Object>) webRequest.getAttribute("ext", 0);
        errorAttributes.put("ext",ext);
        return errorAttributes;
    }
}

最终结果:响应是自适应的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值