1. Use @ ControllerAdvice , and @ ExceptionHandler , to handle global exceptions
This is a commonly used method at present, which is highly recommended. Junit 5 is used in the test code. If you create a new project to verify the following code, remember to add relevant dependencies.
1. Create an exception information entity class
Non essential class, mainly used to package exception information.
src/main/java/com/twuc/webApp/exception/ErrorResponse.java
/** * @author shuang.kou */public class ErrorResponse { private String message; private String errorTypeName; public ErrorResponse(Exception e) { this(e.getClass().getName(), e.getMessage()); } public ErrorResponse(String errorTypeName, String message) { this.errorTypeName = errorTypeName; this.message = message; } ......ellipsis getter/setter method}
2. Custom exception type
src/main/java/com/twuc/webApp/exception/ResourceNotFoundException.java
Generally, we deal with RuntimeException, so if you need to customize the exception type, you can directly integrate this class.
/** * @author shuang.kou * Custom exception type */public class ResourceNotFoundException extends RuntimeException { private String message; public ResourceNotFoundException() { super(); } public ResourceNotFoundException(String message) { super(message); this.message = message; } @Override public String getMessage() { return message; } public void setMessage(String message) { this.message = message; }}
3. Create a new exception handling class
We only need to add the @ ControllerAdvice annotation on the class, which will become the global exception handling class. Of course, you can also specify a specific Controller class through {assignableTypes to let the exception handling class only handle the exceptions thrown by a specific class.
src/main/java/com/twuc/webApp/exception/GlobalExceptionHandler.java
/** * @author shuang.kou */@ControllerAdvice(assignableTypes = {ExceptionController.class})@ResponseBodypublic class GlobalExceptionHandler { ErrorResponse illegalArgumentResponse = new ErrorResponse(new IllegalArgumentException("Parameter error!")); ErrorResponse resourseNotFoundResponse = new ErrorResponse(new ResourceNotFoundException("Sorry, the resourse not found!")); @ExceptionHandler(value = Exception.class)// Intercept all exceptions. This is just for demonstration. Generally, a method handles an exception specifically. Public responseentity < errorresponse > exceptionhandler (exception E){ if (e instanceof IllegalArgumentException) { return ResponseEntity.status(400).body(illegalArgumentResponse); } else if (e instanceof ResourceNotFoundException) { return ResponseEntity.status(404).body(resourseNotFoundResponse); } return null; }}
4. controller simulation throws an exception
src/main/java/com/twuc/webApp/web/ExceptionController.java
/** * @author shuang.kou */@RestController@RequestMapping("/api")public class ExceptionController { @GetMapping("/illegalArgumentException") public void throwException() { throw new IllegalArgumentException(); } @GetMapping("/resourceNotFoundException") public void throwException2() { throw new ResourceNotFoundException(); }}
Use Get request @ localhost:8080/api/resourceNotFoundException[1] (curl -i -s -X GET url), the JSON data returned by the server is as follows:
{ "message": "Sorry, the resourse not found!", "errorTypeName": "com.twuc.webApp.exception.ResourceNotFoundException"}
5. Write test class
MockMvc by org springframework. boot. Test package, which realizes the simulation of Http requests. It is generally used for us to test the controller layer.
/** * @author shuang.kou */@AutoConfigureMockMvc@SpringBootTestpublic class ExceptionTest { @Autowired MockMvc mockMvc; @Test void should_return_400_if_param_not_valid() throws Exception { mockMvc.perform(get("/api/illegalArgumentException")) .andExpect(status().is(400)) .andExpect(jsonPath("$.message").value("Parameter error!")); } @Test void should_return_404_if_resourse_not_found() throws Exception { mockMvc.perform(get("/api/resourceNotFoundException")) .andExpect(status().is(404)) .andExpect(jsonPath("$.message").value("Sorry, the resourse not found!")); }}
2. @ ExceptionHandler @ handle Controller level exceptions
We just said that using the @ ControllerAdvice annotation, you can specify a specific class through the @ assignableTypes so that the exception handling class can only handle the exceptions thrown by a specific class. Therefore, this method of handling exceptions is actually less used now.
Let's move the following code to Src / main / Java / COM / twuc / webapp / exception / globalexceptionhandler Java #.
@ExceptionHandler(value = Exception.class)// Intercept all exceptions public responseentity < errorresponse > exceptionhandler (exception E){ if (e instanceof IllegalArgumentException) { return ResponseEntity.status(400).body(illegalArgumentResponse); } else if (e instanceof ResourceNotFoundException) { return ResponseEntity.status(404).body(resourseNotFoundResponse); } return null; }
3. ResponseStatusException
To study the ResponseStatusException, let's take a look at the simple exception handling method (mapping the exception to a status code) through the {ResponseStatus annotation.
src/main/java/com/twuc/webApp/exception/ResourceNotFoundException.java
@ResponseStatus(code = HttpStatus.NOT_FOUND)public class ResourseNotFoundException2 extends RuntimeException { public ResourseNotFoundException2() { } public ResourseNotFoundException2(String message) { super(message); }}
src/main/java/com/twuc/webApp/web/ResponseStatusExceptionController.java
@RestController@RequestMapping("/api")public class ResponseStatusExceptionController { @GetMapping("/resourceNotFoundException2") public void throwException3() { throw new ResourseNotFoundException2("Sorry, the resourse not found!"); }}
Use Get to request localhost:8080/api/resourceNotFoundException2[2], and the JSON data returned by the server is as follows:
{ "timestamp": "2019-08-21T07:11:43.744+0000", "status": 404, "error": "Not Found", "message": "Sorry, the resourse not found!", "path": "/api/resourceNotFoundException2"}
The advantage of this method of simply handling exceptions through the ResponseStatus annotation is that it is relatively simple, but generally we will not do so. It will be more convenient to use the ResponseStatusException to avoid our additional exception classes.
@GetMapping("/resourceNotFoundException2") public void throwException3() { throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Sorry, the resourse not found!", new ResourceNotFoundException()); }
Use Get to request @ localhost:8080/api/resourceNotFoundException2[3], and the JSON data returned by the server is as follows, which is the same as the effect achieved by using @ ResponseStatus:
{ "timestamp": "2019-08-21T07:28:12.017+0000", "status": 404, "error": "Not Found", "message": "Sorry, the resourse not found!", "path": "/api/resourceNotFoundException3"}
ResponseStatusException provides three construction methods:
public ResponseStatusException(HttpStatus status) { this(status, null, null); } public ResponseStatusException(HttpStatus status, @Nullable String reason) { this(status, reason, null); } public ResponseStatusException(HttpStatus status, @Nullable String reason, @Nullable Throwable cause) { super(null, cause); Assert.notNull(status, "HttpStatus is required"); this.status = status; this.reason = reason; }
The parameters in the constructor are interpreted as follows:
• status: http status • reason: message content of response • cause: exception thrown