zoukankan      html  css  js  c++  java
  • spring-boot-全局异常

    Spring Boot默认的异常处理机制

    默认情况下,Spring Boot为两种情况提供了不同的响应方式。

    一种是浏览器客户端请求一个不存在的页面或服务端处理发生异常时,一般情况下浏览器默认发送的请求头中Accept: text/html,所以Spring Boot默认会响应一个html文档内容,称作“Whitelabel Error Page”。

    另一种是使用Postman等调试工具发送请求一个不存在的url或服务端处理发生异常时,Spring Boot会返回类似如下的Json格式字符串信息

    {
        "timestamp": "2018-05-12T06:11:45.209+0000",
        "status": 404,
        "error": "Not Found",
        "message": "No message available",
        "path": "/index.html"
    }
    

    Spring Boot 默认提供了程序出错的结果映射路径/error。这个/error请求会在BasicErrorController中处理,其内部是通过判断请求头中的Accept的内容是否为text/html来区分请求是来自客户端浏览器(浏览器通常默认自动发送请求头内容Accept:text/html)还是客户端接口的调用,以此来决定返回页面视图还是 JSON 消息内容

    如何自定义错误页面

    • 在/resources/templates下面创建error.html就可以覆盖默认的Whitelabel Error Page的错误页面

    • 访问不存在的页面时,结果如下:

    • 根据不同的状态码返回不同的视图页面,也就是对应的404,500等页面

      • 静态的:如果只是静态HTML页面,不带错误信息的,在resources/public/下面创建error目录,在error目录下面创建对应的状态码html即可

      • 如果是动态模板页面,可以带上错误信息,在resources/templates/下面创建error目录

    • 模拟下500错误
    @Controller 
    public class BaseErrorController extends  AbstractController{ 
    private Logger logger = LoggerFactory.getLogger(this.getClass()); 
    
        @RequestMapping(value="/ex") 
        @ResponseBody 
        public String error(){ 
            int i=5/0; 
            return "ex"; 
        } 
    }
    

    如果同时存在静态页面500.html和动态模板的500.html,则后者覆盖前者。即templates/error/这个的优先级比resources/public/error高。
    error.html会覆盖默认的 whitelabel Error Page 错误提示, 静态错误页面优先级别比error.html高

    根据不同的状态码,自动转发到对应的错误的页面上

    @Configuration
    public class ContainerConfig {
        @Bean
        public EmbeddedServletContainerCustomizer containerCustomizer() {
            return new EmbeddedServletContainerCustomizer() {
                @Override
                public void customize(ConfigurableEmbeddedServletContainer container) {
                    //如果发生500错误,就会将请求转发到/error/500这个映射来
                    container.addErrorPages(new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error/500"));
                    //如果发生404错误,就会将请求转发到/error/404这个映射来
                    container.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/error/404"));
                }
            };
        }
    }
    
    
    @Controller
    public class MyBasicErrorController extends BasicErrorController {
    
        public MyBasicErrorController() {
            super(new DefaultErrorAttributes(), new ErrorProperties());
        }
    
        /**
         * 定义500的ModelAndView
         * @param request
         * @param response
         * @return
         */
        @RequestMapping(produces = "text/html",value = "/500")
        public ModelAndView errorHtml500(HttpServletRequest request,
                                         HttpServletResponse response) {
            response.setStatus(getStatus(request).value());
            Map<String, Object> model = getErrorAttributes(request,
                    isIncludeStackTrace(request, MediaType.TEXT_HTML));
            model.put("msg","自定义错误信息");
            return new ModelAndView("error/500", model);
        }
    
    
        /**
         * 定义500的错误JSON信息
         * @param request
         * @return
         */
        @RequestMapping(value = "/500")
        @ResponseBody
        public ResponseEntity<Map<String, Object>> error500(HttpServletRequest request) {
            Map<String, Object> body = getErrorAttributes(request,
                    isIncludeStackTrace(request, MediaType.TEXT_HTML));
            HttpStatus status = getStatus(request);
            return new ResponseEntity<Map<String, Object>>(body, status);
        }
    
    
        /**
         * 定义404的ModelAndView
         * @param request
         * @param response
         * @return
         */
        @RequestMapping(produces = "text/html",value = "/404")
        public ModelAndView errorHtml400(HttpServletRequest request,
                                         HttpServletResponse response) {
            response.setStatus(getStatus(request).value());
            Map<String, Object> model = getErrorAttributes(request,
                    isIncludeStackTrace(request, MediaType.TEXT_HTML));
            model.put("msg","自定义错误信息");
            return new ModelAndView("error/404", model);
        }
    }
    
    

    BasicErrorController默认对应的@RequestMapping是/error,固我们方法里面对应的@RequestMapping(produces = "text/html",value = "/500")实际上完整的映射请求是/error/500,这就跟上面 customize 方法自定义的映射路径对上了

    通过@ControllerAdvice注解来处理异常

    Spring Boot提供的ErrorController是一种全局性的容错机制。此外,你还可以用@ControllerAdvice注解和@ExceptionHandler注解实现对指定异常的特殊处理。

    这里介绍两种情况:

    • 局部异常处理 @Controller + @ExceptionHandler
    • 全局异常处理 @ControllerAdvice + @ExceptionHandle
    @RestControllerAdvice
    public class BusinessExceptionHandler {
    	private Logger logger = LoggerFactory.getLogger(getClass());
    
    
    
    	/**
    	 * 应用到所有@RequestMapping注解方法,在其执行之前初始化数据绑定器
    	 * @param binder
    	 */
    	@InitBinder
    	public void initBinder(WebDataBinder binder) {
    		System.out.println("请求有参数才进来");
    	}
    
    	/**
    	 * 把值绑定到Model中,使全局@RequestMapping可以获取到该值
    	 * @param model
    	 */
    	@ModelAttribute
    	public void addAttributes(Model model) {
    		model.addAttribute("author", "嘟嘟MD");
    	}
    
    	@ExceptionHandler(Exception.class)
    	public Object handleException(Exception e,HttpServletRequest req){
    		AjaxObject r = new AjaxObject();
    		//业务异常
    		if(e instanceof BusinessException){
    			r.put("code", ((BusinessException) e).getCode());
    			r.put("msg", ((BusinessException) e).getMsg());
    		}else{//系统异常
    			r.put("code","500");
    			r.put("msg","未知异常,请联系管理员");
    		}
    
    		//使用HttpServletRequest中的header检测请求是否为ajax, 如果是ajax则返回json, 如果为非ajax则返回view(即ModelAndView)
    		String contentTypeHeader = req.getHeader("Content-Type");
    		String acceptHeader = req.getHeader("Accept");
    		String xRequestedWith = req.getHeader("X-Requested-With");
    		if ((contentTypeHeader != null && contentTypeHeader.contains("application/json"))
    				|| (acceptHeader != null && acceptHeader.contains("application/json"))
    				|| "XMLHttpRequest".equalsIgnoreCase(xRequestedWith)) {
    			return r;
    		} else {
    			ModelAndView modelAndView = new ModelAndView();
    			modelAndView.addObject("msg", e.getMessage());
    			modelAndView.addObject("url", req.getRequestURL());
    			modelAndView.addObject("stackTrace", e.getStackTrace());
    			modelAndView.setViewName("error");
    			return modelAndView;
    		}
    	}
    }
    

    全局异常类我用的是@RestControllerAdvice,而不是@ControllerAdvice,因为这里返回的主要是json格式,这样可以少写一个@ResponseBody。

    参考:

    原文: http://tengj.top/2018/05/16/springboot13/  作者: 嘟嘟MD

  • 相关阅读:
    字符串 CSV解析 表格 逗号分隔值 通讯录 电话簿 MD
    Context Application 使用总结 MD
    RxJava RxPermissions 动态权限 简介 原理 案例 MD
    Luban 鲁班 图片压缩 MD
    FileProvider N 7.0 升级 安装APK 选择文件 拍照 临时权限 MD
    组件化 得到 DDComponent JIMU 模块 插件 MD
    gradlew 命令行 build 调试 构建错误 Manifest merger failed MD
    protobuf Protocol Buffers 简介 案例 MD
    ORM数据库框架 SQLite 常用数据库框架比较 MD
    [工具配置]requirejs 多页面,多入口js文件打包总结
  • 原文地址:https://www.cnblogs.com/zhangjianbin/p/10096024.html
Copyright © 2011-2022 走看看