1. 概述
在本文中,我們將探討在控制器中如何拋出異常,以及如何使用 Spring MockMvc 測試這些異常。
2. 從控制器中拋出異常
讓我們開始學習如何從控制器中啓動異常。
我們可以將控制器中暴露的服務視為普通的 Java 函數:
@GetMapping("/exception/throw")
public void getException() throws Exception {
throw new Exception("error");
}
現在,讓我們看看當我們調用這個服務時會發生什麼。首先,我們會注意到該服務的響應代碼是 500,這意味着內部服務器錯誤。
其次,我們收到如下的響應體:
{
"timestamp": 1592074599854,
"status": 500,
"error": "Internal Server Error",
"message": "No message available",
"trace": "java.lang.Exception
at com.baeldung.controllers.ExceptionController.getException(ExceptionController.java:26)
..."
}綜上所述,當我們在 RestController 中拋出異常時,服務響應會自動映射為 500 響應代碼,並且異常的堆棧跟蹤包含在響應體中。
3. 異常映射到 HTTP 響應代碼
現在我們將學習如何將我們的異常映射到除了 500 之外的其他 HTTP 響應代碼。
為了實現這一點,我們將創建自定義異常並使用 Spring 提供的 <em>ResponseStatus</em> 註解。 讓我們創建這些自定義異常:
@ResponseStatus(HttpStatus.BAD_REQUEST)
public class BadArgumentsException extends RuntimeException {
public BadArgumentsException(String message) {
super(message);
}
}@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public class InternalException extends RuntimeException {
public InternalException(String message) {
super(message);
}
}@ResponseStatus(HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException(String message) {
super(message);
}
}第二步和最後一步是創建一個簡單的服務,在我們的控制器中拋出這些異常:
@GetMapping("/exception/{exception_id}")
public void getSpecificException(@PathVariable("exception_id") String pException) {
if("not_found".equals(pException)) {
throw new ResourceNotFoundException("resource not found");
}
else if("bad_arguments".equals(pException)) {
throw new BadArgumentsException("bad arguments");
}
else {
throw new InternalException("internal error");
}
}現在,讓我們來看一下為我們映射的不同異常而服務返回的不同響應:
- 對於 not_found 異常,我們收到 404 的響應代碼
- 給定值 bad_arguments,我們收到 400 的響應代碼
- 對於任何其他值,我們仍然收到 500 作為響應代碼
除了響應代碼之外,我們還會收到與上一部分中收到的響應正文中相同的格式的正文。
4. 測試我們的控制器
最後,我們將看到如何測試我們的控制器是否拋出正確的異常。
第一步是創建測試類並創建 MockMvc 的實例:
@Autowired
private MockMvc mvc;
接下來,讓我們為服務可以接收的每個值創建測試用例:
@Test
public void givenNotFound_whenGetSpecificException_thenNotFoundCode() throws Exception {
String exceptionParam = "not_found";
mvc.perform(get("/exception/{exception_id}", exceptionParam)
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isNotFound())
.andExpect(result -> assertTrue(result.getResolvedException() instanceof ResourceNotFoundException))
.andExpect(result -> assertEquals("resource not found", result.getResolvedException().getMessage()));
}
@Test
public void givenBadArguments_whenGetSpecificException_thenBadRequest() throws Exception {
String exceptionParam = "bad_arguments";
mvc.perform(get("/exception/{exception_id}", exceptionParam)
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isBadRequest())
.andExpect(result -> assertTrue(result.getResolvedException() instanceof BadArgumentsException))
.andExpect(result -> assertEquals("bad arguments", result.getResolvedException().getMessage()));
}
@Test
public void givenOther_whenGetSpecificException_thenInternalServerError() throws Exception {
String exceptionParam = "dummy";
mvc.perform(get("/exception/{exception_id}", exceptionParam)
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isInternalServerError())
.andExpect(result -> assertTrue(result.getResolvedException() instanceof InternalException))
.andExpect(result -> assertEquals("internal error", result.getResolvedException().getMessage()));
}通過這些測試,我們正在驗證響應代碼、引發的異常類型以及這些異常的消息是否與每個值相對應。
5. 結論
在本教程中,我們學習瞭如何在我們的 Spring RestController 中處理異常,以及如何測試每個暴露的服務是否拋出預期的異常。