异常统一处理(优雅)

本文最后更新于:2023年8月24日 晚上

在写spring项目的时候,特别是使用了安全框架security之后,异常的处理直接返回封装对象会导致结构非常多余,以下是我的统一异常处理

项目初始化

创建状态枚举类

stateEnum > RespResultEnum.java

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
package com.fsan.throw_demo.stateEnum;

public enum RespResultEnum {

SUCCESS(200, "成功!"),
ERROR(500, "服务器出现问题,请尽快联系管理员!"),
NOT_AUTHENTICATION(403, "权限认证失败!"),
NULL_USERNAME(1001, "用户名为空!非法请求!"),
NULL_PASSWORD(1002, "密码为空!非法请求!");


private Integer code;
private String msg;

RespResultEnum(Integer code, String msg) {
this.code = code;
this.msg = msg;
}

public Integer getCode() {
return code;
}

public void setCode(Integer code) {
this.code = code;
}

public String getMsg() {
return msg;
}

public void setMsg(String msg) {
this.msg = msg;
}
}

创建返回的实体类

entity > RespResult.java

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
package com.fsan.throw_demo.entity;

import com.fsan.throw_demo.stateEnum.RespResultEnum;
import lombok.Data;

@Data
public class RespResult {
private Integer code;
private String msg;
private Object data;

/**
* 有返回值的请求成功
*
* @param data 数据
* @return
*/
public static RespResult success(Object data) {
RespResult respResult = new RespResult();
respResult.setCode(RespResultEnum.SUCCESS.getCode());
respResult.setMsg(RespResultEnum.SUCCESS.getMsg());
respResult.setData(data);
return respResult;
}

/**
* 无返回值的请求成功
*
* @return
*/
public static RespResult success() {
RespResult respResult = new RespResult();
respResult.setCode(RespResultEnum.SUCCESS.getCode());
respResult.setMsg(RespResultEnum.SUCCESS.getMsg());
return respResult;
}

/**
* 自定义异常
*
* @param respResultEnum 枚举状态
* @return
*/
public static RespResult fail(RespResultEnum respResultEnum) {
RespResult respResult = new RespResult();
respResult.setCode(respResultEnum.getCode());
respResult.setMsg(respResultEnum.getMsg());
return respResult;
}

/**
* 未知异常
*
* @return
*/
public static RespResult error() {
RespResult respResult = new RespResult();
respResult.setCode(RespResultEnum.ERROR.getCode());
respResult.setMsg(RespResultEnum.ERROR.getMsg());
return respResult;
}
}

简单建立User类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.fsan.throw_demo.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Long userId;
private String username;
private String password;
}

异常统一处理

思路:

  1. 创建自定义异常类,接收状态枚举
  2. 定义响应拦截,接收抛出的自定义异常类的信息
  3. 使用HttpServletResponse封装返回

创建自定义异常

exception > ValidateException.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.fsan.throw_demo.exception;
import com.fsan.throw_demo.stateEnum.RespResultEnum;

public class ValidateException extends RuntimeException {
private RespResultEnum respResultEnum;

public ValidateException(RespResultEnum respResultEnum) {
super(respResultEnum.getMsg());
this.respResultEnum = respResultEnum;
}

RespResultEnum getRespResultEnum() {
return respResultEnum;
}
}

继承RuntimeException是自定义异常类的最佳选择,然后自定义异常类中存储状态枚举类方便后续封装返回

类的访问修饰符不写就是在同包下使用,如果要在其他包下调用,需要写上public

创建异常处理类

exception > GlobalException.java

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
package com.fsan.throw_demo.exception;

import com.alibaba.fastjson.JSON;
import com.fsan.throw_demo.entity.RespResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Slf4j
@RestControllerAdvice
public class GlobalException {

@ExceptionHandler(ValidateException.class)
public void handler(ValidateException exception, HttpServletResponse resp) {
resp.setStatus(200);
resp.setContentType("application/json");
resp.setCharacterEncoding("utf-8");
try {
resp.getWriter().write(JSON.toJSONString(RespResult.fail(exception.getRespResultEnum())));
} catch (IOException e) {
e.printStackTrace();
log.error("================\n出现未知错误!\n================");
}
}
}

@RestControllerAdvice 设置在响应前执行

@ExceptionHandler(ValidateException.class) 接收抛出的ValidateException异常类

这里也可以将HttpServletResponse对象单独封装一份供其他类调用,如下:

utils > WebUtils

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.fsan.throw_demo.utils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Slf4j
@Component
public class WebUtils {

public void sendResp(HttpServletResponse response, String respResult) {
try {
response.setStatus(200);
response.setContentType("application/json");
response.setCharacterEncoding("utf-8");
response.getWriter().write(respResult);
} catch (IOException e) {
e.printStackTrace();
log.error("================\n出现未知错误!\n================");
}
}
}

修改后的异常处理类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.fsan.throw_demo.exception;

import com.alibaba.fastjson.JSON;
import com.fsan.throw_demo.entity.RespResult;
import com.fsan.throw_demo.utils.WebUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletResponse;

@RestControllerAdvice
public class GlobalException {

@Autowired
private WebUtils webUtils;

@ExceptionHandler(ValidateException.class)
public void handler(ValidateException exception, HttpServletResponse resp) {
String respResult = JSON.toJSONString(RespResult.fail(exception.getRespResultEnum()));
webUtils.sendResp(resp, respResult);
}
}

异常统一处理(优雅)
https://xin-fas.github.io/2022/05/14/异常统一处理(优雅)/
作者
Xin-FAS
发布于
2022年5月14日
更新于
2023年8月24日
许可协议