kim.zhang

风在前,无惧!


  • 首页

  • 标签42

  • 分类12

  • 归档94

  • 搜索

自定义异常体系.md

发表于 2020-08-10 更新于 2021-11-21 分类于 SpringBoot
本文字数: 4.7k 阅读时长 ≈ 4 分钟

自定义异常体系在我们的日常开发中非常重要。一方面为了让用户体验更好,应该在发生异常的时候,在前端页面显示出错误信息。而另一方面,应该隐藏后端的异常,因为后端出现的异常对于前端开发者和用户来说毫无意义。所以,我们需要定义一种统一返回给前端的异常格式。

我们先明确两个概念:未知异常,已知异常

在开发过程中,我们有时会手动抛出一个异常,举个栗子:

1
2
3
4
5
public String test(String id) {
if (id == null){
throw new RuntimeException(10001);
}
}

像这种参数为空抛出异常,这是我们程序员可以预知的异常,就称为已知异常。但是我们程序员不可能捕获所有的异常,而那些未捕获而在运行期间抛出的异常,我们就称为未知异常。


我们可以使用SpringBoot的全局异常处理。无论在程序中抛出什么异常,最终都会经过全局异常处理后才返回给前端。这样,我们就可以在全局异常处理中统一返回给前端的异常格式。

首先,我们先来定义统一返回信息类。这里的getter和setter方法非常重要。

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
public class UnifyResponse {
private Integer code;
private String message;
private String request;
private String method;

public UnifyResponse(Integer code, String message, String request,String type) {
this.code = code;
this.message = message;
this.request = request;
this.method = type;
}

public Integer getCode() {
return code;
}

public String getMessage() {
return message;
}

public String getRequest() {
return request;
}

public String getMethod() {
return method;
}

public void setMethod(String method) {
this.method = method;
}
}

全局异常处理需要@ControllerAdvice和@ExceptionHandler:

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
@ControllerAdvice
public class GlobalExceptionAdvice {

@Autowired
private ExceptionCodeConfiguration exceptionCodeConfiguration;

/**
* handler unknown exception
*
* @param request the request of http
* @param e the exception that method throw
* @return the unify response message
*/
@ExceptionHandler(Exception.class)
@ResponseBody
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public UnifyResponse handlerException(HttpServletRequest request, Exception e) {
Map<String, String> requestInfo = this.getRequestUrlAndMethod(request);
return new UnifyResponse(9999, "服务器内部错误", requestInfo.get("url"), requestInfo.get("method"));
}

/**
* handler already known exception
*
* @param request the request of http
* @param e the exception that method throw
* @return the unify response message
*/
@ExceptionHandler(HttpException.class)
public ResponseEntity<UnifyResponse> handlerHttpException(HttpServletRequest request, HttpException e) {
Map<String, String> requestInfo = this.getRequestUrlAndMethod(request);
// header
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
// http status
HttpStatus httpStatus = HttpStatus.resolve(e.getHttpStatusCode());
// message
Integer code = e.getCode();
System.out.println(e);
String message = exceptionCodeConfiguration.getMessage(code);
UnifyResponse unifyResponse = new UnifyResponse(code, message, requestInfo.get("url"), requestInfo.get("method"));
return new ResponseEntity<>(unifyResponse, headers, httpStatus);
}

/**
* get http request information
*
* @param request the request of http
* @return the map of request information
*/
private Map<String, String> getRequestUrlAndMethod(HttpServletRequest request) {
Map<String, String> map = new HashMap<>(2);
map.put("url", request.getRequestURI());
map.put("method", request.getMethod());
return map;
}
}

介绍一下这里的两个方法:

1
2
3
4
@ExceptionHandler(Exception.class)
@ResponseBody
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public UnifyResponse handlerException(HttpServletRequest request, Exception e)

这是处理”未知异常”的处理方法。方法的参数是Exception。因为未知异常我们是无法预知错误码的,这里我们定义为9999。同样,返回给前端的消息固定为”服务器内部错误“。因为返回的是一个对象,需要@ResponseBody注解。@HttpStatus返回HTTP的状态码。

1
2
@ExceptionHandler(HttpException.class)
public ResponseEntity<UnifyResponse> handlerHttpException(HttpServletRequest request, HttpException e)

这是处理”已知异常“的处理方法。方法的参数是我们自定义的HttpException。由于message和httpStatus是动态决定的,所以返回的ResponseEntity,且不需要使用@ResponseBody。message和httpStatus的信息,我们要从自定义异常中获取。而没有使用@ResponseBody,我们需要在header中手动设置content-Type的值。

接下来介绍下自定义异常类:

1
2
3
4
5
6
7
8
9
10
11
12
public class HttpException extends RuntimeException {
protected Integer code;
protected Integer httpStatusCode = 500;

public Integer getCode() {
return code;
}

public Integer getHttpStatusCode() {
return httpStatusCode;
}
}
1
2
3
4
5
6
7
public class NotFoundException extends HttpException {

public NotFoundException(Integer code){
this.code = code;
this.httpStatusCode = 404;
}
}

自定义配置异常类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@ConfigurationProperties(value = "ming")
@PropertySource(value="classpath:config/exception-code.properties")
@Component
public class ExceptionCodeConfiguration {

private Map<Integer,String> codes = new HashMap<>(16);


public String getMessage(Integer code){
return codes.get(code);
}

public Map<Integer, String> getCodes() {
return codes;
}

public void setCodes(Map<Integer, String> codes) {
this.codes = codes;
}

}

@PropertySource关联了配置文件,而@ConfigurationProperties设置了配置文件中key的前缀,而成员变量codes对应者配置文件中的key值。

配置文件:

1
2
ming.codes[10000] = 通用错误
ming.codes[10001] = 通用参数错误

这里的前缀是不能少的,不然注入会失败。而且这里的字符串不需要加”“,否则返回前端的信息中也会带有引号。

一毛也是爱~
Kim.Zhang 微信支付

微信支付

# 异常
应对变化的两种方案.md
自动配置.md
Kim.Zhang

Kim.Zhang

且行且珍惜
94 日志
12 分类
42 标签
E-Mail Weibo
粵ICP备19091267号 © 2019 – 2022 Kim.Zhang | 629k | 9:32
本站总访问量 4 次 | 有 309 人看我的博客啦 |
博客全站共176.7k字
载入天数...载入时分秒...
0%