乐趣区

关于springboot:SpringBoot优雅的全局异常处理

前言

在日常我的项目开发中,异样是常见的,然而如何更高效的解决好异样信息,让咱们能疾速定位到 BUG,是很重要的,不仅可能进步咱们的开发效率,还能让你代码看上去更难受,SpringBoot 的我的项目曾经有肯定的异样解决了,然而对于咱们开发者而言可能就不太适合了,因而咱们须要对这些异样进行对立的捕捉并解决。

SpringBoot 默认的错误处理机制

返回谬误页面

默认返回 Whitelabel Error Page页面的款式太枯燥,用户体验不好。

如 果 我 们 需 要 将 所 有 的 异 常 同 一 跳 转 到 自 定 义 的 错 误 页 面,需 要 再 src/main/resources/templates 目录下创立 error.html 页面。

留神:名称必须叫 error

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
        <!--SpringBoot 默认存储异样信息的 key 为 exception-->
    <span th:text="${exception}" />
</body>
</html>

返回 json 格局 api

Json 格局的后果字符串不对立,与前端人员约定对立格局不统一

源码剖析

SpringBoot 在页面 产生异样的时候会主动把申请转到 /error,SpringBoot 内置了一个 BasicErrorController 对异样进行对立的解决,当然也能够自定义这个门路

@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.getErrorAttributeOptions(request, MediaType.TEXT_HTML)));
        response.setStatus(status.value());
        ModelAndView modelAndView = this.resolveErrorView(request, response, status, model);
        return modelAndView != null ? modelAndView : new ModelAndView("error", model);
    }

    @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.getErrorAttributeOptions(request, MediaType.ALL));
            return new ResponseEntity(body, status);
        }
    }

咱们能够看到刚好对照两个办法一个返回谬误页面,一个返回谬误字符,默认谬误门路是 /error 如果有自定义就用自定义的

server.error.path=/custom/error

自定义错误处理

SpringBoot 提供了 ErrorAttribute 类型
自定义 ErrorAttribute 类型的 bean 还是默认的两种响应形式,只不过扭转了响应内容项而已

package cn.soboys.core;


import cn.hutool.core.bean.BeanUtil;
import cn.soboys.core.ret.Result;
import cn.soboys.core.ret.ResultCode;
import cn.soboys.core.ret.ResultResponse;
import org.springframework.boot.web.error.ErrorAttributeOptions;
import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

/**
 * @author kenx
 * @version 1.0
 * @date 2021/6/18 14:14
 * 全局谬误
 */
@Component
public class GlobalErrorHandler extends DefaultErrorAttributes {


    /**
     * 自定义谬误返回页面
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @return
     */
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {return super.resolveException(request, response, handler, ex);
    }

    /**
     * 自定义谬误返回格局
     *
     * @param webRequest
     * @param options
     * @return
     */
    @Override
    public Map<String, Object> getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) {Map<String, Object> errorAttributes = super.getErrorAttributes(webRequest, options);
        Result result = ResultResponse.failure(ResultCode.NOT_FOUND, errorAttributes.get("path"));
        Map map = BeanUtil.beanToMap(result, true, true);
        return map;
    }
}

自定义业务异样类

继承 RuntimeException

package cn.soboys.core.authentication;

import cn.soboys.core.ret.ResultCode;
import lombok.Data;

/**
 * @author kenx
 * @version 1.0
 * @date 2021/6/22 13:58
 * 认证异样
 */
@Data
public class AuthenticationException extends RuntimeException {public AuthenticationException(String message) {super(message);
    }

}

全局捕捉异样

通过 SpringBoot 提供的 @RestControllerAdvice@ControllerAdvice 联合 @ExceptionHandler 应用

@RestControllerAdvice@ControllerAdvice 区别和 @RestController,@Controller 一样如果想返回 json 格局也能够独自应用 @ResponseBody 注解在办法上

须要捕捉什么异样通过 @ExceptionHandler 来指定对应异样类就能够了这里准则是依照 从小到大异样 进行顺次执行

艰深来讲就是当小的异样没有指定捕捉时,大的异样蕴含了此异样就会被执行比方Exception 异样蕴含了所有异样类,是所有异样超级父类,当呈现没有指定异样时此时对应捕捉了 Exception 异样的办法会执行

@ExceptionHandler 注解解决异样

@Controller
public class DemoController {@RequestMapping("/show")
    public String showInfo() {
        String str = null;
        str.length();
        return "index";
    }

    @RequestMapping("/show2")
    public String showInfo2() {
        int a = 10 / 0;
        return "index";
    }

    /**
     * java.lang.ArithmeticException 该办法须要返回一个 ModelAndView:目标是能够让咱们封装异样信息以及视
     * 图的指定 参数 Exception e: 会将产生异样对象注入到办法中
     */
    @ExceptionHandler(value = { java.lang.ArithmeticException.class})
    public ModelAndView arithmeticExceptionHandler(Exception e) {ModelAndView mv = new ModelAndView();
        mv.addObject("error", e.toString());
        mv.setViewName("error1");
        return mv;
    }

    /**
     * java.lang.NullPointerException 该办法须要返回一个 ModelAndView:目标是能够让咱们封装异样信息以及视
     * 图的指定 参数 Exception e: 会将产生异样对象注入到办法中
     */
    @ExceptionHandler(value = { java.lang.NullPointerException.class})
    public ModelAndView nullPointerExceptionHandler(Exception e) {ModelAndView mv = new ModelAndView();
        mv.addObject("error", e.toString());
        mv.setViewName("error2");
        return mv;
    }
}

长处:能够自定义异样信息存储的 key, 自定义跳转视图的名称

毛病:须要编写大量的异样解决办法, 不能跨 controller,如果两个 controller 中呈现同样的异样,须要从新编写异样解决的办法

@ControllerAdvice+@ExceptionHandler 注解解决异样

/**
 * @author kenx
 * @version 1.0
 * @date 2021/6/17 20:19
 * 全局异样对立解决
 */
@RestControllerAdvice
public class GlobalExceptionHandler {
 /**
     * 认证异样
     * @param e
     * @return
     */
    @ExceptionHandler(AuthenticationException.class)
    public Result UnNoException(AuthenticationException e) {return ResultResponse.failure(ResultCode.UNAUTHORIZED,e.getMessage());
    }

    /**
     *
     * @param e 未知异样捕捉
     * @return
     */
    @ExceptionHandler(Exception.class)
    public Result UnNoException(Exception e) {return ResultResponse.failure(ResultCode.INTERNAL_SERVER_ERROR, e.getMessage());
    }
}

长处:能够自定义异样信息存储的 key, 自定义跳转视图的名称, 跨 controller 对立拦挡对立捕捉,个别都是应用这种

关注公众号猿人生理解更多好文

退出移动版