自定义异常和统一结果返回对象

1/9/2023 开发SpringBoot

[toc]

# 自定义异常和统一结果返回对象

# 统一结果返回对象

使用场景:随着业务的增长,需要返回的数据类型会越来越多。对于前端来说,如果后台能够返回一个统一的数据结果,前端在解析的时候就可以按照一种方式进行解析。开发就会变得更加简单。

统一响应结果封装思路分析为:

  • 封装data数据:创建响应结果类,封装响应数据到data属性中
  • 封装结果码数据:封装操作结果到code属性中
  • 封装错误信息数据:封装错误消息到message(msg)属性中

例如下图: springmvc20220916150142.png

① 先创建结果编码枚举类

/**
 * 结果代码枚举类
 */
public enum ResultCodeEnum {
    /* 成功状态码 */
    HTTP_REQUEST_SUCCESS(200, "请求成功"),
    /* 失败状态码 */
    HTTP_REQUEST_ERROR(500, "请求错误"),

    /* 参数错误:10001-19999 */
    PARAM_IS_INVALID(10001, "参数无效"),
    PARAM_IS_BLANK(10002, "参数为空"),
    PARAM_IS_MISS(10003, "参数缺失"),

    /* 用户错误:20001-29999*/
    USER_NOT_LOGIN(20001, "用户未登录"),
    USER_LOGIN_ERROR(20002, "用户登录失败,账号不存在或密码错误"),
    USER_REGISTER_ERROR(20003, "用户注册失败"),
    USER_ACCOUNT_FORBIDDEN(20004, "账号已被禁用"),
    USER_NOT_EXIST(20005, "用户不存在"),

    /* token错误:30001-39999 */
    TOKEN_IS_ERROR(30001, "token错误"),

    /* 系统错误:40001-49999 */
    SYSTEM_INNER_ERROR(40001, "系统繁忙,请稍后重试"),

    /* 数据错误:50001-599999 */
    DATA_IS_MISS(50001, "数据缺失"),
    DATA_IS_WRONG(50002, "数据有误"),
    DATA_IS_EXISTED(50003, "数据已存在"),

    /* 接口错误:60001-69999 */
    INTERFACE_INNER_INVOKE_ERROR(60001, "内部系统接口调用异常"),
    INTERFACE_OUTTER_INVOKE_ERROR(60002, "外部系统接口调用异常"),
    INTERFACE_FORBID_VISIT(60003, "该接口禁止访问"),
    INTERFACE_ADDRESS_INVALID(60004, "接口地址无效"),
    INTERFACE_REQUEST_TIMEOUT(60005, "接口请求超时"),
    INTERFACE_EXCEED_LOAD(60006, "接口负载过高"),

    /* 权限错误:70001-79999 */
    PERMISSION_NO_ACCESS(70001, "无访问权限"),

    /*业务错误:80001-89999*/
    BUSINESS_IS_WRONG(80001,"该业务功能出现问题");

    private int code;
    private String message;

    private ResultCodeEnum() {}
    private ResultCodeEnum(int code, String message) {
        this.code = code;
        this.message = message;
    }

    public int getCode() {
        return code;
    }
    public String getMessage() {
        return message;
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

② 创建统一结果返回类

/**
 * 统一结果返回工具类
 * @param
 */
public class ReturnUtil {
    //成功
    public static String success(){
        JSONObject json = new JSONObject();
        json.put("message",ResultCodeEnum.HTTP_REQUEST_SUCCESS.getMessage());
        json.put("code",ResultCodeEnum.HTTP_REQUEST_SUCCESS.getCode());
        return json.toString();
    }

    //成功,JSONObject数据
    public static String success(JSONObject json){
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("data",json);
        jsonObject.put("message",ResultCodeEnum.HTTP_REQUEST_SUCCESS.getMessage());
        jsonObject.put("code",ResultCodeEnum.HTTP_REQUEST_SUCCESS.getCode());
        return jsonObject.toString();
    }

    //成功,JSONArray数据
    public static String success(JSONArray jsonArray){
        JSONObject json = new JSONObject();
        json.put("data",jsonArray);
        json.put("message",ResultCodeEnum.HTTP_REQUEST_SUCCESS.getMessage());
        json.put("code",ResultCodeEnum.HTTP_REQUEST_SUCCESS.getCode());
        return json.toString();
    }

    //失败
    public static String fail(){
        JSONObject json = new JSONObject();
        json.put("message",ResultCodeEnum.HTTP_REQUEST_ERROR.getMessage());
        json.put("code",ResultCodeEnum.HTTP_REQUEST_ERROR.getCode());
        return json.toString();
    }

    //失败
    public static String fail(int code,String message){
        JSONObject json = new JSONObject();
        json.put("message",message);
        json.put("code",code);
        return json.toString();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

# 自定义异常

使用场景:java的异常信息对于用户难以理解,因此将系统产生的异常信息封装为更加容易理解的自定义异常信息。当程序中出现异常时,结合统一结果返回对象,将自定义异常信息返回给用户看到。

SpringMVC对于异常的统一处理已经提供了一套解决方案:

  • 自定义异常处理器: 集中的、统一的处理项目中出现的异常。
  • 自定义异常处理器需要添加@RestControllerAdvice注解修饰

springmvc20220916152802.png

① 先编写自定义异常类

/**
 * 自定义异常
 */
public class MyException extends RuntimeException {

    private int code;  //状态码
    private String message;  //异常信息

    public MyException() {}

    public MyException(int code, String message) {
        this.code = code;
        this.message = message;
    }

    public int getCode() {
        return code;
    }

    @Override
    public String getMessage() {
        return message;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

② 再编写异常处理器

/**
 * 自定义异常处理类
 * 用于统一管理,处理自定义异常
 */
@ControllerAdvice
public class MyExceptionHandler {
    //日志
    private static final Logger logger = LoggerFactory.getLogger(MyExceptionHandler.class);

    /**
     * 自定义异常发生时,
     * 调用该方法,处理并返回异常信息
     * @param e
     * @return
     */
    @ExceptionHandler(value = MyException.class)
    @ResponseBody
    public Object exceptionHandler(MyException e) {
        logger.error("MyExceptionHandler code:{}, message:{}",e.getCode(),e.getMessage());
        return new ReturnUtil().fail(e.getCode(),e.getMessage());
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 将自定义异常和统一结果返回对象结合使用

两种逻辑思路:

  1. 对可能发生异常的地方,在程序中用try catch捕获java异常。把java异常转化为自定义异常,然后手动throw抛出。此时异常会被异常处理器处理。在异常处理器的方法中将异常对象封装进统一结果中,最后将统一结果返回给用户。
  2. 也可以当程序进行到某个地方时,手动throw自定义异常来中止程序继续运行。

① 步骤1: 创建结果代码枚举类。将错误信息与错误代码封装到一起

/**
 * 结果代码枚举类
 */
public enum ResultCodeEnum {
    /* 成功状态码 */
    HTTP_REQUEST_SUCCESS(200, "请求成功"),
    /* 失败状态码 */
    HTTP_REQUEST_ERROR(500, "请求错误"),

    /* 参数错误:10001-19999 */
    PARAM_IS_INVALID(10001, "参数无效"),
    PARAM_IS_BLANK(10002, "参数为空"),
    PARAM_IS_MISS(10003, "参数缺失"),
    
    /* 用户错误:20001-29999*/
    USER_NOT_LOGIN(20001, "用户未登录"),
    USER_LOGIN_ERROR(20002, "用户登录失败,账号不存在或密码错误"),
    USER_REGISTER_ERROR(20003, "用户注册失败"),
    USER_ACCOUNT_FORBIDDEN(20004, "账号已被禁用"),
    USER_NOT_EXIST(20005, "用户不存在"),

    /* Token错误:30001-39999 */
    TOKEN_IS_ERROR(30001, "token错误"),
    TOKEN_IS_NULL(30002, "token缺失"),

    /* 系统错误:40001-49999 */
    SYSTEM_INNER_ERROR(40001, "系统繁忙,请稍后重试"),
    
    /* 数据错误:50001-599999 */
    DATA_IS_MISS(50001, "数据缺失"),
    DATA_IS_WRONG(50002, "数据有误"),
    DATA_IS_EXISTED(50003, "数据已存在"),

    /* 接口错误:60001-69999 */
    INTERFACE_INNER_INVOKE_ERROR(60001, "内部系统接口调用异常"),
    INTERFACE_OUTTER_INVOKE_ERROR(60002, "外部系统接口调用异常"),
    INTERFACE_FORBID_VISIT(60003, "该接口禁止访问"),
    INTERFACE_ADDRESS_INVALID(60004, "接口地址无效"),
    INTERFACE_REQUEST_TIMEOUT(60005, "接口请求超时"),
    INTERFACE_EXCEED_LOAD(60006, "接口负载过高"),

    /* 权限错误:70001-79999 */
    PERMISSION_NO_ACCESS(70001, "无访问权限"),

    /*业务错误:80001-89999*/
    BUSINESS_IS_WRONG(80001,"该业务出现问题");

    private int code;
    private String message;

    private ResultCodeEnum() {}
    private ResultCodeEnum(int code, String message) {
        this.code = code;
        this.message = message;
    }
    public int getCode() {
        return code;
    }
    public String getMessage() {
        return message;
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

② 步骤2: 创建自定义异常类

自定义异常类继承RuntimeException类的好处是,后面在抛出自定义异常的时候,就不用在try...catch...或throws了。

public class MyException extends RuntimeException{
    private int code;  //状态码
    private String message;  //异常信息

    public MyException() {}

    //传入结果枚举类来构造自定义异常对象
    public MyException(ResultCodeEnum codeEnum) {
        this.code = codeEnum.getCode();
        this.message = codeEnum.getMessage();
    }

    public int getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

③ 步骤3: 创建统一结果对象类ReturnUtil

/**
 * 统一结果对象
 * @param
 */
public class ReturnUtil {
    //成功
    //请求成功时,不传入data数据
    public static JSONObject success(){
        JSONObject json = new JSONObject();
        json.put("message", ResultCodeEnum.HTTP_REQUEST_SUCCESS.getMessage());
        json.put("code",ResultCodeEnum.HTTP_REQUEST_SUCCESS.getCode());
        return json;
    }

    //成功
    //请求成功时,传入要返回的data数据
    public static JSONObject success(Object obj){
        JSONObject json = new JSONObject();
        json.put("data",obj);
        json.put("message",ResultCodeEnum.HTTP_REQUEST_SUCCESS.getMessage());
        json.put("code",ResultCodeEnum.HTTP_REQUEST_SUCCESS.getCode());
        return json;
    }

    //失败
    //传入结果枚举类,当请求失败时,返回明确的失败原因
    public static JSONObject fail(ResultCodeEnum codeEnum){
        JSONObject json = new JSONObject();
        json.put("message",codeEnum.getMessage());
        json.put("code",codeEnum.getCode());
        return json;
    }

    //请求结果失败
    //传入自定义异常类,当发生异常时,直接返回请求失败时产生的异常错误信息
    public static JSONObject fail(MyException exception){
        JSONObject json = new JSONObject();
        json.put("message",exception.getMessage());
        json.put("code",exception.getCode());
        return json;
    }

    //请求结果失败
    //传入Exception异常类,当发生非自定义异常时,直接返回Exception异常错误信息
    public static JSONObject fail(Exception exception){
        JSONObject json = new JSONObject();
        json.put("message",exception.getMessage());
        json.put("code",ResultCodeEnum.HTTP_REQUEST_ERROR.getCode());
        return json;
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

④ 步骤4: 创建自定义异常处理类

@RestControllerAdvice
public class MyExceptionAdvice {
    //对MyException类型的异常处理,用于处理自定义异常。这个方法是针对MyException的异常处理
    @ExceptionHandler(MyException.class)
    public Object doMyException(MyException e){
        //记录日志
        //发送消息给运维
        //发送邮件给开发人员,ex对象发送给开发人员
        //日志打印异常信息
        e.printStackTrace();
        //用统一结果类封装异常信息并返回给用户
        return ReturnUtil.fail(e);
    }

    //对Exception类型的异常处理,用于处理非自定义异常。这个方法是针对Exception的异常处理
    @ExceptionHandler(Exception.class)
    public Object doException(Exception e){
        //记录日志
        //发送消息给运维
        //发送邮件给开发人员
        //日志打印异常信息
        e.printStackTrace();
        //用统一结果类封装异常信息并返回给用户
        return ReturnUtil.fail(e);
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
  • @ExceptionHandler 注解:异常拦截器。
  • @RestControllerAdvice注解 = @ControllerAdvice + @ResponseBody + @Component
  • @ControllerAdvice 是 @Controller 的增强版。@ControllerAdvice主要用来处理全局数据,可以搭配@ExceptionHandler注解来定义全局异常处理机制。

⑤ 步骤5:编写测试方法。

两种使用思路:

  1. 对可能发生异常的地方,在程序中用try catch捕获java异常。把java异常转化为自定义异常,然后手动throw抛出。此时异常会被异常处理器处理。在异常处理器的方法中将异常对象封装进统一结果中,最后将统一结果返回给用户。
  2. 也可以当程序进行到某个地方时,手动throw自定义异常来中止程序继续运行。
@Service
@Slf4j
public class EncUtilServiceImpl implements EncUtilService {
    @Override
    public Object test() {
        log.info("开始--------------------------");
        try {
            log.info("执行业务代码,若此处发生java异常。会被try catch 捕获");
            return resDTO;
        }catch (Exception e){
            //打印原始异常日志信息
            e.printStackTrace();
            //将捕获的java异常转换为自定义异常并抛出,该自定义异常会被异常处理器处理
            throw new MyException(ResultCodeEnum.REQUEST_ERROR);
        }
    }

    @Override
    public Object test2() {
        log.info("开始--------------------------");
        //当条件满足时,直接手动抛出自定义异常,来中止程序继续运行。
        if("条件表达式"){
            throw new MyException(ResultCodeEnum.REQUEST_ERROR);
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

⑥ 步骤6:运行测试。

20230109160447.png

Last Updated: 12/5/2023, 1:54:07 AM