SpringBoot学习之旅(三)---响应规范及优雅处理异常

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lupengfei1009/article/details/88065593

导读

本文基于以下文章开发:
SpringBoot学习之旅(二)—整合MyBatis(MyBatis Generator)

源码地址

点击下载

前言

所谓了系统,就是将一系列的请求响应的交互,按一定的流程及规则,将其串联起来,每一次的请求交互做其中的某一项小的工作;然后一步一步的将其串联成一个完整的业务线;正因为每次做的事情及交互的数据不一致,因此我们就需要规范化每一次的请求及响应;这样公共部分的请求及响应的数据即可统一去处理,业务中只需要去处理本次操作个性化的数据及相关的校验,从而降低不必要的校验及冗余的代码。

响应规范

响应示例
  • 响应示例
    • 成功响应
      状态为0时标识
      {
      	"status": 1,
      	"data": {
      		"key1": "va1",
      		"key2": 2,
      		"key3": true
      	}
      }
      
    • 错误响应
      {
      	"status": -1,
      	"data": {
      		"errCode": 10001,
      		"errMsg": "参数错误!"
      	}
      }
      
代码实现
  • 定义公共错误信息接口

    /**
     * 公共异常的接口
     */
    public interface ComErrorInf {
        /**
         * 获取错误码
         *
         * @return
         */
        Integer getErrCode();
    
        /**
         * 获取错误描述
         *
         * @return
         */
        String getErrMsg();
    
        /**
         * 修改错误描述信息
         *
         * @param errMsg
         * @return
         */
        ComErrorInf setErrMsg(String errMsg);
    }
    
  • 定义错误码枚举(BusiErrCodeEm)
    该枚举实现平台公共的错误接口ComErrorInf
    具体的错误码信息,根据个人业务定义,这里只说一个大致的规范

    public enum BusiErrCodeEm implements ComErrorInf {
        //>10000的错误码约束为参数才错误码
        REQ_PARAM_10001(10001, "参数信息有误"),
        REQ_PARAM_10002(10002, "参数校验失败"),
    
        //>20000 约束为用户的错误码
        USER_20001(20001, "未找到用户"),
    
        //>30000 约束为订单的错误码
        BUSI_ORDER_30001(30001, "订单信息未找到"),
    
        UNKNOWN(999999, "未知错误");
        /**
         * 错误码
         */
        private Integer errCode;
    
        /**
         * 错误描述
         */
        private String errMsg;
    
        BusiErrCodeEm(Integer errCode, String errMsg) {
            this.errCode = errCode;
            this.errMsg = errMsg;
        }
    
        @Override
        public Integer getErrCode() {
            return this.errCode;
        }
    
        @Override
        public String getErrMsg() {
            return this.errMsg;
        }
    
        @Override
        public ComErrorInf setErrMsg(String errMsg) {
            this.errMsg = errMsg;
            return this;
        }
    }
    
  • 定义业务异常类(BusiException)
    业务异常实现平台公共的错误接口ComErrorInf
    由于错误码枚举和异常实现了同一个接口,因此就可以将枚举中的错误码信息转化为异常信息并返回

    public class BusiException extends Exception implements ComErrorInf {
        private ComErrorInf comErrorInf;
    
        /**
         * 接受BusiErrCodeEm传参,构造业务异常
         *
         * @param comErrorInf
         */
        public BusiException(ComErrorInf comErrorInf) {
            super();
            this.comErrorInf = comErrorInf;
        }
    
        public BusiException(ComErrorInf comErrorInf, String errMsg) {
            super();
            this.comErrorInf = comErrorInf;
            this.comErrorInf.setErrMsg(errMsg);
        }
    
        @Override
        public Integer getErrCode() {
            return this.comErrorInf.getErrCode();
        }
    
        @Override
        public String getErrMsg() {
            return this.comErrorInf.getErrMsg();
        }
    
        @Override
        public ComErrorInf setErrMsg(String errMsg) {
            this.comErrorInf.setErrMsg(errMsg);
            return this;
        }
    }
    
  • 使用示例
    业务中如果出现异常,比如用户信息不存在,那么就可以使用以下方式向上抛异常

    throw new BusiException(BusiErrCodeEm.USER_20001);
    
如何优雅处理异常
  • 异常处理场景
    • 场景一
      对于系统来说,异常是很常见的,比如前端传参不对,参数校验不对,数据库数据未找到,空指针等等的错误;如果说我们没有一个公共的处理异常的地方,就会出现到处都是异常处理机制,这样对于开发人员来说,是一个很没有营养、很恶心、很耗时的体力活,同时还不利于系统的维护及健壮;我们需要的正确做法是,在各自的业务流程中抛出对应的异常,由一个统一的地方去接受到异常,并友好的返回给前端;
    • 场景二
      使用传统的MVC涉及模式,Controller为整个系统处理完之后,响应给用户的最后一道关口,MV层出现了异常且异常没有捕获并处理的话,如果Controller不加以处理,那么Spring就会以HttpStatus为500等错误状态返回给前端,这样用户端就会出现一个很不友好的错误信息;对于系统来说,只要接收到用户的请求,那么就应该回一个HttpStatus.OK的状态回去,如果业务出现异常,那么我们只需要根据响应报文去友好的去做处理;
  • 使用ExceptionHandler
    • 定义BaseController
      将ExceptionHandler的捕获代码写在BaseController,系统级的所有Controller都继承自BaseController
      package com.lupf.springboottest.controller;
      
      import com.lupf.springboottest.error.BusiErrCodeEm;
      import com.lupf.springboottest.error.BusiException;
      import com.lupf.springboottest.response.BaseRespObj;
      import org.springframework.http.HttpStatus;
      import org.springframework.web.bind.annotation.ExceptionHandler;
      import org.springframework.web.bind.annotation.ResponseBody;
      import org.springframework.web.bind.annotation.ResponseStatus;
      
      import java.util.HashMap;
      import java.util.Map;
      
      public class BaseController {
          /**
           * 通过ExceptionHandler 捕获controller未捕获到的异常,给用户一个友好的返回
           *
           * @param ex 异常信息
           * @return
           */
          //捕获为Exception的异常
          @ExceptionHandler(Exception.class)
          //指明响应的状态为200
          @ResponseStatus(HttpStatus.OK)
          //返回业务对象
          @ResponseBody
          public Object exceptionHandler(Exception ex) {
              BaseRespObj baseRespObj = new BaseRespObj();
              baseRespObj.setStatus(-1);
              Map<String, Object> errObjs = new HashMap<>();
              if (ex instanceof BusiException) {
                  BusiException busiException = (BusiException) ex;
                  errObjs.put("errCode", busiException.getErrCode());
                  errObjs.put("errMsg", busiException.getErrMsg());
              } else {
                  errObjs.put("errCode", BusiErrCodeEm.UNKNOWN.getErrCode());
                  errObjs.put("errMsg", BusiErrCodeEm.UNKNOWN.getErrMsg());
              }
              baseRespObj.setData(errObjs);
              return baseRespObj;
          }
      }
      
      
    • 用户Controller
      package com.lupf.springboottest.controller;
      
      import com.lupf.springboottest.dao.UserDOMapper;
      import com.lupf.springboottest.dataobject.UserDO;
      import com.lupf.springboottest.error.BusiErrCodeEm;
      import com.lupf.springboottest.error.BusiException;
      import com.lupf.springboottest.response.BaseRespObj;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.web.bind.annotation.RequestMapping;
      import org.springframework.web.bind.annotation.RequestParam;
      import org.springframework.web.bind.annotation.RestController;
      
      @RestController
      @RequestMapping("/user")
      public class UserController extends BaseController {
      
          @Autowired
          UserDOMapper userDOMapper;
      
          @RequestMapping("/hello")
          public BaseRespObj getUserById(@RequestParam(name = "id") Integer id) throws BusiException {
              UserDO userDO = userDOMapper.selectByPrimaryKey(id);
              if (null == userDO) {
                  throw new BusiException(BusiErrCodeEm.USER_20001);
              }
              BaseRespObj baseRespObj = BaseRespObj.create(userDO);
              return baseRespObj;
          }
      }
      
      • 正常的请求

      • 未找到用户

        扫描二维码关注公众号,回复: 5443665 查看本文章
      • 模拟空指针,测试未捕获到的异常

猜你喜欢

转载自blog.csdn.net/lupengfei1009/article/details/88065593