restful 全局异常捕获处理

本贴最后更新于 2551 天前,其中的信息可能已经时移世改

全局异常处理

本文项目已发布到 github,后续学习项目也会添加到此工程下,欢迎 fork 点赞。
https://github.com/wangyuheng/spring-boot-sample

偷懒代码

偷懒是程序员的美德,但是有些偷懒是为了少写代码,有些则是少思考,直接 copy。

不知道你有没有见过这种代码,在最外层 catch Exception,避免抛出异常信息。。。

    @RestController
    @RequestMapping("/user")
    public class UserApi {
    
        @Autowired
        private UserService userService;
    
        @GetMapping("/{id}")
        public Object getInfo(@PathVariable("id") int id) {
            try {
                return userService.getUsernameById(id);
            } catch (UserException) {
                return "用户id异常";
            } catch (Exception e) {
                return "服务异常,请稍后再试!";
            }
        }

    }

UserService 用于模拟异常抛出

    @Service
    public class UserService {
    
        public String getUsernameById(long id) {
            if (0 == id % 2) {
                throw new IllegalArgumentException("error param!");
            } else {
                throw new UserException("custom exception!");
            }
        }
    }

全局异常处理

自定义异常

通过自定义异常,区分业务异常,并增加 errorCode 支持,返回给接口调用方。

    public enum ErrorCode {
        Error(10000, "服务异常"),
        UserIdError(10001, "用户id异常");
    
        private int code;
        private String message;
    
        ErrorCode(int code, String message) {
            this.code = code;
            this.message = message;
        }
    
        public int getCode() {
            return code;
        }
    
        public String getMessage() {
            return message;
        }
    }
    public class CustomException extends RuntimeException {
    
        private int errorCode;
    
        public CustomException(int errorCode, String message) {
            super(message);
            this.errorCode = errorCode;
        }
    
        public int getErrorCode() {
            return errorCode;
        }
    }
    public class UserException extends CustomException {
        public UserException(String message) {
            super(ErrorCode.UserIdError.getCode(), message);
        }
    
        public UserException() {
            super(ErrorCode.UserIdError.getCode(), ErrorCode.UserIdError.getMessage());
        }
    }

@ControllerAdvice

会应用到所有的 Controller 中的 @RequestMapping 注解的方法中,通过 annotations = RestController.class 指定代理的 Controller 类中。

@ExceptionHandler

用于捕获异常,@ExceptionHandler 本身只能捕获当前类的异常信息,结合 @ControllerAdvice 可以捕获全部 controller 异常。

    @ControllerAdvice(annotations = RestController.class)
    @ResponseBody
    public class GlobalExceptionHandler {
    
        private Map<String, Object> getErrorObject(int code, String message) {
            Map<String, Object> error = new HashMap<>();
            error.put("code", code);
            error.put("message", message);
            return error;
        }
    
        @ExceptionHandler(Exception.class)
        public Object exceptionHandler() {
            return getErrorObject(ErrorCode.Error.getCode(), ErrorCode.Error.getMessage());
        }
    
        @ExceptionHandler(CustomException.class)
        public Object customException(CustomException e) {
            return getErrorObject(e.getErrorCode(), e.getMessage());
        }
    
    }

优化代码

优化后的代码简洁了,无需每次 try...catch,统一管理异常信息。

    @RestController
    @RequestMapping("/user")
    public class UserApi {
    
        @Autowired
        private UserService userService;
    
        @GetMapping("/{id}")
        public Object getInfo(@PathVariable("id") int id) {
            return userService.getUsernameById(id);
        }
        
    }

测试

mockMvc 请求 api 接口,判断返回值

    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class UserApiTest {
    
        private MockMvc mockMvc;
    
        @Autowired
        private WebApplicationContext context;
    
        @Autowired
        private UserApi userApi;
    
        @Before
        public void setup() {
            mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
        }
    
        private JSONObject getUserApiResult(int id) throws Exception {
            String path = "/user/" + id;
            return new JSONObject(mockMvc.perform(MockMvcRequestBuilders.get(path))
                    .andExpect(MockMvcResultMatchers.status().isOk())
                    .andDo(MockMvcResultHandlers.print())
                    .andReturn()
                    .getResponse()
                    .getContentAsString());
        }
    
        @Test
        public void test_exception_handler() throws Exception {
            int i = 1;
            assertTrue(ErrorCode.UserIdError.getCode() == getUserApiResult(i).getInt("code"));
            i++;
            assertTrue(ErrorCode.Error.getCode() == getUserApiResult(i).getInt("code"));
        }
    
    }
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3187 引用 • 8213 回帖
  • Spring

    Spring 是一个开源框架,是于 2003 年兴起的一个轻量级的 Java 开发框架,由 Rod Johnson 在其著作《Expert One-On-One J2EE Development and Design》中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 JavaEE 应用程序开发提供集成的框架。

    944 引用 • 1459 回帖 • 17 关注
  • 异常
    20 引用 • 47 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
  • DrJoseph

    总能在报废的边缘找到学习的曙光

    2 回复
  • 不放弃不抛弃

  • crick77
    作者

    也可能是因为年底了

    1 回复
  • DrJoseph

    是因为这段时间经历的太多,有点颓吧;其次是这家公司做的东西越来越没劲了,学习的动力也丧失了。

  • loveWct

    棒棒的哟