SpringBoot中全局异常处理以及错误页的定义方式

阅读原文
在开发中,当我们需要构建一个基础的开发框架时,自定义错误页面、全局异常处理、响应结果集封装等技术点,是我们必须考虑的问题,那么如何在SpringBoot框架的基础上来优雅的解决这些问题呢?

一、自定义错误页面

关于错误页面的处理,SpringBoot提供了默认的支持,基本思路如下:

1、当页面请求发生异常时,会自动请求“/error”控制器;
2、此控制器会根据处理类型自动匹配,将处理类型为“text/html”的异常请求特殊处理,其他的异常请求默认处理。
3、特殊处理的方式为:根据响应码去查找对应的错误页面,例如,响应码为404,则系统会会在以下两个路径去查找页面:

模板路径:/templates/error/404.html
静态资源路径:/resources/META-INF/resources/error/404.html

如果两者都不存在,则系统会自动返回一个名称为“error”的视图,如果视图名称对应的模板页面(/templates/error.html)不存在,则系统会自动拼接一个错误页面响应给客户端,如下:

4、默认处理方式为:除处理类型为“text/html”的请求外,其他异常请求,系统统一返回json格式数据,数据格式如下:

{
  "timestamp": "2020-03-06T05:01:50.054+0000",
  "status": 404,
  "error": "Not Found",
  "message": "No message available",
  "path": "/x/x"
}

通过以上对SpringBoot异常请求的默认处理分析,可知,要想自定义异常请求的处理,大致有两种方式:
1、依赖默认处理方式,只自定义错误页面内容,这种方式最简单,缺点是不够灵活。
2、自定义异常请求控制器,将所有的异常请求转发至此控制器处理,这种方式比较灵活,也最常用。

方式一、自定义错误页面

如果你想只定义一个错误页面,只需在templates目录下定义一个error.html即可,所有的异常请求将会自动跳转至此页面。
如果你想根据状态码来区分错误页面,只需在templates/error目录下,根据状态码定义错误页面,例如:

/error/404.html
/error/400.html
/error/500.html
方式二、自定义异常控制器
自定义异常控制器,是最为灵活的控制错误页面的方式,如下
@Controller
public class ExceptionController implements ErrorController {public static Logger LOG = LoggerFactory.getLogger(ExceptionController.class);//默认请求地址
    private static final  String ERROR_PATH = "/error";@Autowired
    private ErrorAttributes errorAttributes;/**
     * web页面异常请求处理
     * @param request
     * @return
     */
    @RequestMapping(value = ERROR_PATH, produces = MediaType.TEXT_HTML_VALUE)
    public String handlePageError(HttpServletRequest request) {
        LOG.info("进入异常跳转");
        Integer statusCode = getStatus(request);
        switch (statusCode) {
            case 404:
                LOG.info("404异常跳转");
                return "error/404";
            case 403:
                LOG.info("403异常跳转");
                return "error/403";
            case 500:
                LOG.info("500异常跳转");
                return "error/500";
            default:
                LOG.info("默认异常跳转");
                return "error/404";
        }}/**
     * 其他异常请求处理,例如:contentType:application/json等
     * @param request
     * @return
     */
    @RequestMapping(ERROR_PATH)
    @ResponseBody
    public Result handleAllError(HttpServletRequest request) {
        WebRequest webRequest = new ServletWebRequest(request);
        Integer statusCode = getStatus(request);
        Map<String, Object> body = this.errorAttributes.getErrorAttributes(webRequest, false);
        body.put("status", statusCode);
        return ResultUtil.result(CodeMsgFactory.REQ_EXCEPTION, body);
    }/**
     * 获取状态码
     * @param request
     * @return
     */
    public Integer getStatus(HttpServletRequest request) {
        Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
        if (statusCode == null) {
            return 500;
        }
        return  statusCode;
    }/**
     * Spring 默认错误页路径
     * @return
     */
    @Override
    public String getErrorPath() {
        return ERROR_PATH;
    }}

以上异常控制器,将请求状态返回码为403、404、500的请求分别指向了与之对应的错误页面,将无法预知的状态码默认指向404的错误页,当然这个可以根据自己的业务情况而定。

二、响应结果集封装

响应结果集封装,主要的应用场景是Api请求,现在主流的Api请求方式是http-json,而比较主流的接口交互方式是Restful,在这种交互模式下,我们对接口响应做统一封装,可以使得代码更加规范,接口响应数据的解析更为简洁。

1、结果集封装对象
public class Result {
  private String code;
  private String msg;
  private Object data;
  
  public Result(CodeMsg codeMsg, Object data) {
    this.code  = codeMsg.getCode();
    this.msg = codeMsg.getMsg();
    this.data = data;
  }
  
  public Result(Object data) {
    this.data = data;
  }
  
  public Object getData() {
    return data;
  }public String getCode() {
    return code;
  }public void setCode(String code) {
    this.code = code;
  }public String getMsg() {
    return msg;
  }public void setMsg(String msg) {
    this.msg = msg;
  }
}
2、响应码信息封装
public class CodeMsg {
  
  private String code;
  private String msg;
  
  public CodeMsg(String code, String msg) {
    this.code = code;
    this.msg = msg;
  }
  
  public String getCode() {
    return code;
  }
  public void setCode(String code) {
    this.code = code;
  }
  public String getMsg() {
    return msg;
  }
  public void setMsg(String msg) {
    this.msg = msg;
  }
  
}
3、响应码对象静态工厂
public class CodeMsgFactory {
  /**
   * 系统通用结果码
   */
  public static final CodeMsg SUCC = new CodeMsg("000000", "成功");
  public static final CodeMsg FAIL = new CodeMsg("FFFFFF", "系统异常");/**
   * 请求参数错误
   */
  public static final CodeMsg REQ_PARAM_ERROR = new CodeMsg("REQ_PARAM_ERROR", "请求参数错误");/**
   * 请求异常
   */
  public static final CodeMsg REQ_EXCEPTION = new CodeMsg("REQ_EXCEPTION", "请求异常");
}
4、结果集工具类
public class ResultUtil {
  /**
     * 成功
   * @return
     */
  public static Result succ() {
    return succ(null) ;
  }/**
   * 成功
   * @return
   */
  public static Result succ(Object data) {
    return new Result(CodeMsgFactory.SUCC, data) ;
  }/**
     * 失败
   * @return
     */
  public static Result fail() {
    return fail(null) ;
  }
  
  public static Result fail( Object data) {
    return new Result(CodeMsgFactory.FAIL, data) ;
  }
  
  public static Result result(CodeMsg codeMsg, Object data) {
    return new Result(codeMsg, data) ;
  }public static Result result(CodeMsg codeMsg) {
    return new Result(codeMsg, null) ;
  }
  
  public static Result result(String code, String msg, Object data) {
    CodeMsg codeMsg = new CodeMsg(code, msg);
    return new Result(codeMsg, data) ;
  }

通过以上四个类,就可以完成响应结果集的封装,这种封装方式虽然不是最简单的,但是个人认为是比较实用的方式,当然也可以通过枚举封装响应码信息,这个在之前Java基础部分文章中已经提到过,就不赘述了。

三、全局异常处理

全局异常处理的主要应用场景也是Api请求,例如:当我们在service中处理业务流程时,需要向接口请求返回一些异常信息,但是局限于service方法的返回值无法接收此类信息,此时使用全局异常就是最好的解决方案。我们可以定义一个RuntimeException类型的业务异常,由于此类异常不需要做异常处理,所以对我们原有的代码没有任何的侵入,只需在需要返回异常信息的位置,手动抛出一个业务异常,这个业务异常携带着我们事先定义好的响应码对象,最终由全局异常处理器进行捕捉,通过响应结果集工具类返回给接口请求。

1、定义业务异常
**
 * 定义系统异常类
 */
public class SysException extends RuntimeException {
    /**
     * 异常码及对应异常信息
     */
    private CodeMsg codeMsg;/**
     * 无参构造
     */
    public SysException () {}/**
     * 自定义异常信息
     * @param codeMsg
     */
    public SysException(CodeMsg codeMsg) {
        super(codeMsg.getMsg());
        this.codeMsg = codeMsg;
    }/**
     * 自定义异常信息
     * @param codeMsg
     */
    public SysException(CodeMsg codeMsg, Exception e) {
        super(codeMsg.getMsg());
        this.codeMsg = codeMsg;
        e.printStackTrace();
    }/**
     * 获取异常信息
     * @return
     */
    public CodeMsg getCodeMsg() {
        return codeMsg;
    }
}
2、全局异常处理器
@Component
@ControllerAdvice
public class ExceptionHandle {
    /**
     * 系统异常处理
     * @param e
     * @return
     */
    @ExceptionHandler(value = SysException.class)
    @ResponseBody
    public Object systemExceptionHandle(SysException e) {
        e.printStackTrace();
        return ResultUtil.result(e.getCodeMsg());
    }
}

@ControllerAdvice,其实就是一个增强的控制器,它可以对controller中被 @RequestMapping注解的方法加一些逻辑处理,常见的用法有:异常处理,数据绑定,数据预处理等操作,这里我们使用的就是它的异常处理功能。

至此,关于定义错误页面跳转以及全局异常处理就介绍完了,下篇我们来看SpringBoot中关于Mybatis的一些使用姿势,顺便总结一下mybatis的一些使用技巧。

更多最新技术文章,请关注“冰点IT”公众号

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值