1. Overview
1.概述
In this short article, we’ll see how exceptions should be thrown in our controllers and how to test these exceptions using Spring MockMvc.
在这篇短文中,我们将看到应该如何在我们的控制器中抛出异常,以及如何使用Spring MockMvc测试这些异常。
2. Throwing Exceptions in Controllers
2.在控制器中抛出异常
Let’s start learning how to launch an exception from a controller.
让我们开始学习如何从控制器启动一个异常。
We can think of the services we expose from a controller in the same way as if they were normal Java functions:
我们可以把从控制器中暴露出来的服务看成是普通的Java函数。
@GetMapping("/exception/throw")
public void getException() throws Exception {
throw new Exception("error");
}
Now, let’s see what happens when we call this service. First, we’ll notice that the response code of the service is 500 which means Internal Server Error.
现在,让我们看看当我们调用这个服务时会发生什么。首先,我们会注意到该服务的响应代码是500,这意味着内部服务器错误。
Secondly, we receive a response body like this:
第二,我们收到这样的一个回应体。
{
"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)
..."
}
In conclusion, when we throw an exception from a RestController, the service response is automatically mapped to a 500 response code, and the stack trace of the exception is included in the response body.
总之,当我们从RestController抛出一个异常时,服务响应会被自动映射为500响应代码,并且异常的堆栈跟踪会包含在响应体中。
3. Mapping Exceptions to HTTP Response Codes
3.将异常情况映射到HTTP响应代码
Now we’re going to learn how to map our exceptions to different response codes other than 500.
现在我们要学习如何将我们的异常映射到500以外的不同响应代码。
To achieve this, we’re going to create custom exceptions and use the ResponseStatus annotation that’s provided by Spring. Let’s create those custom exceptions:
为了实现这个目标,我们将创建自定义异常,并使用Spring提供的ResponseStatus注解。让我们来创建这些自定义的异常。
@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);
}
}
The second and last step is to create a simple service in our controller to throw these exceptions:
第二步也是最后一步,是在我们的控制器中创建一个简单的服务来抛出这些异常。
@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");
}
}
Now, let’s see the different responses of the service for the different exceptions that we mapped:
现在,让我们看看服务对我们映射的不同异常的不同反应。
- For not_found, we receive a response code of 404
- Given the value bad_arguments, we receive a response code of 400
- For any other value, we still receive 500 as the response code
Apart from the response codes, we’ll receive a body with the same format as the response body received in the previous section.
除了响应代码外,我们将收到一个与上一节中收到的响应体格式相同的体。
4. Testing Our Controllers
4.测试我们的控制器
Finally, we’re going to see how to test that our controller is throwing the correct exceptions.
最后,我们将看到如何测试我们的控制器是否抛出了正确的异常。
The first step is to create a test class and create an instance of MockMvc:
第一步是创建一个测试类,并创建一个MockMvc的实例。
@Autowired
private MockMvc mvc;
Next, let’s create the test cases for each of the values that our service can receive:
接下来,让我们为我们的服务所能接收的每一个值创建测试案例。
@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()));
}
With these tests, we’re checking that the response code, the type of exception raised, and the messages of that exceptions are the expected ones for each of the values.
通过这些测试,我们要检查响应代码、引发的异常类型以及该异常的信息是否为每个值的预期值。
5. Conclusion
5.结论
In this tutorial, we have learned how to handle exceptions in our Spring RestControllers and how to test that each exposed service is throwing the expected exceptions.
在本教程中,我们已经学会了如何在Spring RestControllers中处理异常,以及如何测试每个暴露的服务是否抛出预期的异常。
As always, the full source code of the article is available in GitHub.
一如既往,文章的完整源代码可在GitHub中获得。