美文网首页从零开始学习SpringBoot
SpringBoot - Web开发 自定义错误页面处理

SpringBoot - Web开发 自定义错误页面处理

作者: BzCoder | 来源:发表于2018-05-23 19:12 被阅读8次

    在Web开发中,SpringBoot会自动帮你配置了错误页面。并且在不同端请求,会有不同的效果。那么假如我们想自定义错误页面应该如何做配置呢?

    错误请求

    一.错误页面配置原理

    谈到原理,我们就要回到SpringBoot的自动配置类ErrorMvcAutoConfiguration去查看。

    通过代码,我们看到SpringBoot配置了以下几个类:

    1. DefaultErrorAttributes
    2. BasicErrorController
    3. errorPageCustomizer
    4. DefaultErrorViewResolver

    步骤:

    1. 当出现错误时,errorPageCustomizer就会生效,并来到/error请求,由BasicErrorController来处理
    //errorPageCustomizer
    @Value("${error.path:/error}")
        private String path = "/error";
    

    BasicErrorController是一个Controller,其中里面有两种处理方法,一种是HTML形式,一种是JSON格式。其中我们的页面信息可以从getErrorAttributes从获取。DefaultErrorAttributes是ErrorAttributes的实现类。

    @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<>(body, status);
        }
    
    1. 当为HTML模式时,就会构建一个resolveErrorView类,而resolverErrorView继续调用ErrorViewResolver。
    protected ModelAndView resolveErrorView(HttpServletRequest request,
                HttpServletResponse response, HttpStatus status, Map<String, Object> model) {
            for (ErrorViewResolver resolver : this.errorViewResolvers) {
                ModelAndView modelAndView = resolver.resolveErrorView(request, status, model);
                if (modelAndView != null) {
                    return modelAndView;
                }
            }
            return null;
        }
    

    3.在我们没有做自定义配置时,ErrorViewResolver就会指向DefaultErrorViewResolver。

    static {
            //可以用4xx,5xx文件名来统一匹配错误,但是会以精确优先的原则
            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) {
            //将错误代码拼接到error后
            String errorViewName = "error/" + viewName;
            TemplateAvailabilityProvider provider = this.templateAvailabilityProviders
                    .getProvider(errorViewName, this.applicationContext);
            //如果模版引擎可用就让模版引擎进行解析如:Template/error/404
            if (provider != null) {
                return new ModelAndView(errorViewName, model);
            }
            //如果模版引擎不可用,就在静态资源文件夹下找资源文件,error/404
            return resolveResource(errorViewName, model);
        }
    

    4.自定义错误页面

    • 查看DefaultErrorAttributes我们可以获取到的参数。
      • timestamp - The time that the errors were extracted
      • status - The status code
      • error - The error reason
      • exception - The class name of the root exception (if configured)
      • message - The exception message
      • errors - Any {@link ObjectError}s from a {@link BindingResult} exception
      • trace - The exception stack trace
      • path - The URL path when the exception was raised

    以下为示例代码

    <main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
                        <h1>status:[[${status}]]</h1>
                        <h2>timestamp:[[${timestamp}]]</h2>
                    </main>
    

    二.自定义错误页面

    根据上述原理,只要将我们自定义的错误页面按照错误码放入相应路径。优先会取模版引擎的错误界面(template/error/404,template/error/4xx),其次取静态资源文件夹下(static/error/404,template/error/4xx,public/error/404,template/error/4xx),如果前两者都没有,再取SpringBoot的默认页面。

    三.自定义错误返回

    自定义错误页面只针对TEXT_HTML的方式,要自定义类似于SpringBoot自动配置(网页访问返回错误页面,移动端请求返回错误JSON串)的返回我们应该如何做呢?
    流程:
    出现错误之后,先将页面转发到/error,它就会被BasicErrorController处理,而其中数据是由getErrorAttributes得到的。
    所以我们的实现思路是:

    • 编写一个ErrorController的实现类,放在容器中。
    • 页面上能用的数据,或者是json返回能用的数据都是通过errorAttributes.getErrorAttributes得到;重写DefaultErrorAttributes 进行数据处理。

    定义一个异常事件:

    @Controller
    public class TestController {
        @ResponseBody
        @RequestMapping("/hello")
        public  String hello(@RequestParam("user") String user)
        {
            if (user.equals("aaa"))
            {
                throw  new UserNotExistException();
            }
            return "Hello";
        }
    }
    

    自定义一个异常类:

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

    添加一个统一异常处理类:

    /**
     * @author BaoZhou
     * @date 2018/5/22
     */
    @ControllerAdvice
    public class MyExceptionHandler {
    
        @ExceptionHandler(UserNotExistException.class)
        public String handlerException(Exception e, HttpServletRequest request) {
            Map<String, Object> map = new HashMap<>();
            //定义错误码,否则默认为200
            request.setAttribute("javax.servlet.error.status_code",400);
            map.put("code", "user.notexist");
            map.put("message", "用户出错");
            //将自定义map放入Attribute中
            request.setAttribute("ext",map);
            //重定向到error页面
            return "forward:error";
        }
    }
    

    自定义ErrorAttributes:

    //给容器中加入我们自己定义的ErrorAttributes
    @Component
    public class MyErrorAttributes extends DefaultErrorAttributes {
        /**
         * 重写getErrorAttributes方法,将自己需要的错误讯息放入到map中,以便错误时能在map中获取
         * 其中webRequest.getAttribute(para,para2))函数第二项表示从何处取数据
         *  int SCOPE_REQUEST = 0;
         *  int SCOPE_SESSION = 1;
         */
        @Override
        public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
           Map<String,Object> map = super.getErrorAttributes(webRequest, includeStackTrace);
           map.put("user_name","baozhou");
           map.put("ext",webRequest.getAttribute("ext",0));
           return map;
        }
    
    
    访问网页
    POSTMAN请求

    就此大功告成!

    相关文章

      网友评论

        本文标题:SpringBoot - Web开发 自定义错误页面处理

        本文链接:https://www.haomeiwen.com/subject/jwwhjftx.html