• SpringBoot 处理异常的几种常见姿势


    1. 使用 @ControllerAdvice 和 @ExceptionHandler 处理全局异常

    这是目前很常用的一种方式,非常推荐。测试代码中用到了 Junit 5,如果你新建项目验证下面的代码的话,记得添加上相关依赖。

    1. 新建异常信息实体类

    非必要的类,主要用于包装异常信息。

    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; } ......省略getter/setter方法}

    2. 自定义异常类型

    src/main/java/com/twuc/webApp/exception/ResourceNotFoundException.java

    一般我们处理的都是 RuntimeException ,所以如果你需要自定义异常类型的话直接集成这个类就可以了。

    /** * @author shuang.kou * 自定义异常类型 */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. 新建异常处理类

    我们只需要在类上加上@ControllerAdvice注解这个类就成为了全局异常处理类,当然你也可以通过 assignableTypes指定特定的 Controller 类,让异常处理类只处理特定类抛出的异常。

    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("参数错误!")); ErrorResponse resourseNotFoundResponse = new ErrorResponse(new ResourceNotFoundException("Sorry, the resourse not found!"));
    @ExceptionHandler(value = Exception.class)// 拦截所有异常, 这里只是为了演示,一般情况下一个方法特定处理一种异常 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模拟抛出异常

    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(); }}

    使用 Get 请求 localhost:8080/api/resourceNotFoundException[1] (curl -i -s -X GET url),服务端返回的 JSON 数据如下:

    {    "message": "Sorry, the resourse not found!",    "errorTypeName": "com.twuc.webApp.exception.ResourceNotFoundException"}

    5. 编写测试类

    MockMvc 由org.springframework.boot.test包提供,实现了对Http请求的模拟,一般用于我们测试 controller 层。

    /** * @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("参数错误!")); }
    @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 处理 Controller 级别的异常

    我们刚刚也说了使用@ControllerAdvice注解 可以通过 assignableTypes指定特定的类,让异常处理类只处理特定类抛出的异常。所以这种处理异常的方式,实际上现在使用的比较少了。

    我们把下面这段代码移到 src/main/java/com/twuc/webApp/exception/GlobalExceptionHandler.java 中就可以了。

        @ExceptionHandler(value = Exception.class)// 拦截所有异常    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

    研究 ResponseStatusException 我们先来看看,通过 ResponseStatus注解简单处理异常的方法(将异常映射为状态码)。

    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!");    }}

    使用 Get 请求 localhost:8080/api/resourceNotFoundException2[2] ,服务端返回的 JSON 数据如下:

    {    "timestamp": "2019-08-21T07:11:43.744+0000",    "status": 404,    "error": "Not Found",    "message": "Sorry, the resourse not found!",    "path": "/api/resourceNotFoundException2"}

    这种通过 ResponseStatus注解简单处理异常的方法是的好处是比较简单,但是一般我们不会这样做,通过ResponseStatusException会更加方便,可以避免我们额外的异常类。

        @GetMapping("/resourceNotFoundException2")    public void throwException3() {        throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Sorry, the resourse not found!", new ResourceNotFoundException());    }

    使用 Get 请求 localhost:8080/api/resourceNotFoundException2[3] ,服务端返回的 JSON 数据如下,和使用 ResponseStatus 实现的效果一样:

    {    "timestamp": "2019-08-21T07:28:12.017+0000",    "status": 404,    "error": "Not Found",    "message": "Sorry, the resourse not found!",    "path": "/api/resourceNotFoundException3"}

    ResponseStatusException 提供了三个构造方法:

        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; }

    构造函数中的参数解释如下:

    •status :http status•reason :response 的消息内容•cause :抛出的异常

  • 相关阅读:
    发一段非万能的sql分页代码
    手机版的首页不能翻页,用的是UC7.1
    关于Enterprise library logging中一个没搞明白的东西,希望有大大们进来帮忙释疑一下, THKS
    关于CodeSmith主模板与子模板同时从一个基类继承后的参数传值问题(提供模板下载)
    golang 去除字符串首尾空格
    构建一个SDK的打印信息
    PeekMessage抓取消息,如何把每个消息都获取到呢?
    DirectDraw学习 之 入门(二)
    20111121 —— wince6.0 BSP 里面加了 locale 后编译 failed 之解决办法
    DirectDraw学习 之 编译自带的sample代码
  • 原文地址:https://www.cnblogs.com/suizhikuo/p/14752170.html
Copyright © 2020-2023  润新知