8000 Create springboot-handle-exception.md · zhangmf-java/springboot-guide@6b66b48 · GitHub
[go: up one dir, main page]

Skip to content

Commit 6b66b48

Browse files
committed
Create springboot-handle-exception.md
1 parent 16c59ea commit 6b66b48

File tree

1 file changed

+267
-0
lines changed

1 file changed

+267
-0
lines changed

md/springboot-handle-exception.md

Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
### 1. 使用 **@ControllerAdvice****@ExceptionHandler** 处理全局异常
2+
3+
这是目前很常用的一种方式,非常推荐。测试代码中用到了 Junit 5,如果你新建项目验证下面的代码的话,记得添加上相关依赖。
4+
5+
**1. 新建异常信息实体类**
6+
7+
非必要的类,主要用于包装将异常信息。
8+
9+
`src/main/java/com/twuc/webApp/exception/ErrorResponse.java`
10+
11+
```java
12+
/**
13+
* @author shuang.kou
14+
*/
15+
public class ErrorResponse {
16+
17+
private String message;
18+
private String errorTypeName;
19+
20+
public ErrorResponse(Exception e) {
21+
this(e.getClass().getName(), e.getMessage());
22+
}
23+
24+
public ErrorResponse(String errorTypeName, String message) {
25+
this.errorTypeName = errorTypeName;
26+
this.message = message;
27+
}
28+
......省略getter/setter方法
29+
}
30+
```
31+
32+
**2. 自定义异常类型**
33+
34+
`src/main/java/com/twuc/webApp/exception/ResourceNotFoundException.java`
35+
36+
一般我们处理的都是 `RuntimeException` ,所以如果你需要自定义异常类型的话直接集成这个类就可以了。
37+
38+
```java
39+
/**
40+
* @author shuang.kou
41+
* 自定义异常类型
42+
*/
43+
public class ResourceNotFoundException extends RuntimeException {
44+
private String message;
45+
46+
public ResourceNotFoundException() {
47+
super();
48+
}
49+
50+
public ResourceNotFoundException(String message) {
51+
super(message);
52+
this.message = message;
53+
}
54+
55+
@Override
56+
public String getMessage() {
57+
return message;
58+
}
59+
60+
public void setMessage(String message) {
61+
this.message = message;
62+
}
63+
}
64+
```
65+
66+
**3. 新建异常处理类**
67+
68+
我们只需要在类上加上`@ControllerAdvice`注解这个类就成为了全局异常处理类,当然你也可以通过 `assignableTypes `指定特定的类,让异常处理类只处理特定类抛出的异常。
69+
70+
`src/main/java/com/twuc/webApp/exception/GlobalExceptionHandler.java`
71+
72+
```java
73+
/**
74+
* @author shuang.kou
75+
*/
76+
@ControllerAdvice(assignableTypes = {ExceptionController.class})
77+
@ResponseBody
78+
public class GlobalExceptionHandler {
79+
80+
ErrorResponse illegalArgumentResponse = new ErrorResponse(new IllegalArgumentException("参数错误!"));
81+
ErrorResponse resourseNotFoundResponse = new ErrorResponse(new ResourceNotFoundException("Sorry, the resourse not found!"));
82+
83+
@ExceptionHandler(value = Exception.class)// 拦截所有异常, 这里只是为了演示,一般情况下一个方法特定处理一种异常
84+
public ResponseEntity<ErrorResponse> exceptionHandler(Exception e) {
85+
86+
if (e instanceof IllegalArgumentException) {
87+
return ResponseEntity.status(400).body(illegalArgumentResponse);
88+
} else if (e instanceof ResourceNotFoundException) {
89+
return ResponseEntity.status(404).body(resourseNotFoundResponse);
90+
}
91+
return null;
92+
}
93+
}
94+
```
95+
96+
**4. controller模拟抛出异常**
97+
98+
`src/main/java/com/twuc/webApp/web/ExceptionController.java`
99+
100+
```java
101+
/**
102+
* @author shuang.kou
103+
*/
104+
@RestController
105+
@RequestMapping("/api")
106+
public class ExceptionController {
107+
108+
@GetMapping("/illegalArgumentException")
109+
public void throwException() {
110+
throw new IllegalArgumentException();
111+
}
112+
113+
@GetMapping("/resourceNotFoundException")
114+
public void throwException2() {
115+
throw new ResourceNotFoundException();
116+
}
117+
}
118+
```
119+
120+
使用 Get 请求 [localhost:8080/api/resourceNotFoundException](localhost:8333/api/resourceNotFoundException) (curl -i -s -X GET url),服务端返回的 JSON 数据如下:
121+
122+
```json
123+
{
124+
"message": "Sorry, the resourse not found!",
125+
"errorTypeName": "com.twuc.webApp.exception.ResourceNotFoundException"
126+
}
127+
```
128+
129+
**5. 编写测试类**
130+
131+
MockMvc 由`org.springframework.boot.test`包提供,实现了对Http请求的模拟,一般用于我们测试 controller 层。
132+
133+
```java
134+
/**
135+
* @author shuang.kou
136+
*/
137+
@AutoConfigureMockMvc
138+
@SpringBootTest
139+
public class ExceptionTest {
140+
@Autowired
141+
MockMvc mockMvc;
142+
143+
@Test
144+
void should_return_400_if_param_not_valid() throws Exception {
145+
mockMvc.perform(get("/api/illegalArgumentException"))
146+
.andExpect(status().is(400))
147+
.andExpect(jsonPath("$.message").value("参数错误!"));
148+
}
149+
150+
@Test
151+
void should_return_404_if_resourse_not_found() throws Exception {
152+
mockMvc.perform(get("/api/resourceNotFoundException"))
153+
.andExpect(status().is(404))
154+
.andExpect(jsonPath("$.message").value("Sorry, the resourse not found!"));
155+
}
156+
}
157+
```
158+
159+
### 2. @ExceptionHandler 处理 Controller 级别的异常
160+
161+
我们刚刚也说了使用`@ControllerAdvice`注解 可以通过 `assignableTypes `指定特定的类,让异常处理类只处理特定类抛出的异常。所以这种处理异常的方式,实际上现在使用的比较少了。
162+
163+
我们把下面这段代码移到 `src/main/java/com/twuc/webApp/exception/GlobalExceptionHandler.java` 中就可以了。
164+
165+
```java
166+
@ExceptionHandler(value = Exception.class)// 拦截所有异常
167+
public ResponseEntity<ErrorResponse> exceptionHandler(Exception e) {
168+
169+
if (e instanceof IllegalArgumentException) {
170+
return ResponseEntity.status(400).body(illegalArgumentResponse);
171+
} else if (e instanceof ResourceNotFoundException) {
172+
return ResponseEntity.status(404).body(resourseNotFoundResponse);
173+
}
174+
return null;
175+
}
176+
```
177+
178+
### 3. ResponseStatusException
179+
180+
研究 ResponseStatusException 我们先来看看,通过 `ResponseStatus`注解简单处理异常的方法。
181+
182+
`src/main/java/com/twuc/webApp/exception/ResourceNotFoundException.java`
183+
184+
```java
185+
@ResponseStatus(code = HttpStatus.NOT_FOUND)
186+
public class ResourseNotFoundException2 extends RuntimeException {
187+
188+
public ResourseNotFoundException2() {
189+
}
190+
191+
public ResourseNotFoundException2(String message) {
192+
super(message);
193+
}
194+
}
195+
```
196+
197+
`src/main/java/com/twuc/webApp/web/ResponseStatusExceptionController.java`
198+
199+
```java
200+
@RestController
201+
@RequestMapping("/api")
202+
public class ResponseStatusExceptionController {
203+
@GetMapping("/resourceNotFoundException2")
204+
public void throwException3() {
205+
throw new ResourseNotFoundException2("Sorry, the resourse not found!");
206+
}
207+
}
208+
```
209+
210+
使用 Get 请求 [localhost:8080/api/resourceNotFoundException2](localhost:8333/api/resourceNotFoundException2) ,服务端返回的 JSON 数据如下:
211+
212+
```json
213+
{
214+
"timestamp": "2019-08-21T07:11:43.744+0000",
215+
"status": 404,
216+
"error": "Not Found",
217+
"message": "Sorry, the resourse not found!",
218+
"path": "/api/resourceNotFoundException2"
219+
}
220+
```
221+
222+
这种通过 `ResponseStatus`注解简单处理异常的方法是的好处是比较简单,但是一般我们不会这样做,通过`ResponseStatusException`会更加方便,可以避免我们额外的异常类。
223+
224+
```java
225+
@GetMapping("/resourceNotFoundException2")
226+
public void throwException3() {
227+
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Sorry, the resourse not found!", new ResourceNotFoundException());
228+
}
229+
```
230+
231+
使用 Get 请求 [localhost:8080/api/resourceNotFoundException2](localhost:8333/api/resourceNotFoundException2) ,服务端返回的 JSON 数据如下,和使用 `ResponseStatus` 实现的效果一样:
232+
233+
```json
234+
{
235+
"timestamp": "2019-08-21T07:28:12.017+0000",
236+
"status": 404,
237+
"error": "Not Found",
238+
"message": "Sorry, the resourse not found!",
239+
"path": "/api/resourceNotFoundException3"
240+
}
241+
```
242+
243+
`ResponseStatusException` 提供了三个构造方法:
244+
245+
```java
246+
public ResponseStatusException(HttpStatus status) {
247+
this(status, null, null);
248+
}
249+
250+
public ResponseStatusException(HttpStatus status, @Nullable String reason) {
251+
this(status, reason, null);
252+
}
253+
254+
public ResponseStatusException(HttpStatus status, @Nullable String reason, @Nullable Throwable cause) {
255+
super(null, cause);
256+
Assert.notNull(status, "HttpStatus is required");
257+
this.status = status;
258+
this.reason = reason;
259+
}
260+
261+
```
262+
263+
构造函数中的参数解释如下:
264+
265+
- status : http status
266+
- reason :response 的消息内容
267+
- cause : 抛出的异常

0 commit comments

Comments
 (0)
0