您当前的位置: 首页 > 

石头wang

暂无认证

  • 8浏览

    0关注

    295博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

什么异常能被 @RestControllerAdvice 或 @ControllerAdvice 捕获,什么情况不能被捕获?

石头wang 发布时间:2022-10-04 14:30:10 ,浏览量:8

一、什么异常能被 @RestControllerAdvice 或 @ControllerAdvice 捕获,什么情况不能被捕获? 1、能捕获到的异常

以下发生异常的情况,都是请求进入controller方法前发生的,即如果在所请求的endpoint首行打断点,是不会停住的,因为是进入到断点前的时机发生的。下列这些情况是能被这两个注解捕获到的

  • 请求的Method错误:如GET/POST…
  • 请求时未传必填参数 @RequestParam(required=true)
  • 请求的参数转换错误:如字串无法转为整型、布尔类型、日期类型
  • 请求的Content-Type错误
  • 请求参数jsr303错误:即hibernate validator校验出来的@NotNull、@NotBlank、@NotEmpty、@Min、@Max、@Size、@Pattern…

(以上所有的点都实际测试过!)

进入controller方法后的一切异常自然是能够被上述两个注解捕获到的。

2、不能捕获到的异常
  • 域名、IP写错或端口写错都不会得到任何status code

  • 域名、IP和端口写正确,但endpoint路径写错,返回 404 的status code

    这种情况为什么还能返回status code?因为域名(或IP)和端口都写对了,说明有进程在监听着,这个进程就是Java进程,我们的例子使用springboot,而springboot自然会接收这个请求,只不过因为没有匹配的endpoint,所以返回了404以及下面的错误提示
    
    {"timestamp":"2022-10-04T06:19:07.930+00:00","status":404,"error":"Not Found","path":"/testEndpointNotExist"}
    
    这个其实就是springboot帮你返回的,有明显的特征,即timestamp/status/error/path四个标志性的字段
    
    由于还未进入任何一个endpoint,所以上述两个注解也没法拦截到,只是springboot拦截到了而已。
    
3、另一种不一定能进入这两个注解的情况

如果你的endpoint被nginx代理,当endpoint处理时间过长,当超过nginx设置的超时时间,此时不管endpoint后续执行成功亦或失败,代理都已经将结果返回给调用者了。

后续如果endpoint执行成功则不进入上述两个注解,否则进入。但无论如何,调用者接收到的status code为504,另外接收到的内容也不是JSON了,是一个html网页内容。

  • nginx代理springboot的一个endpoint

在这里插入图片描述

4、定时任务、单元测试 4.1 定时任务

你写了一个定时任务,无论你的定时任务调用controller的这个endpoint,还是直接调用service层的方法,这种情况都会绕过这两个注解。

很好理解,这两个注解是拦截HTTP请求的,而定时任务的调用属于Java方法调用,根本不是http请求,所以肯定拦截不了这种情况。

4.2 单元测试

有时候单元测试,会直接调用controller的这个endpoint,或者直接调用service层的方法,这个跟上面的情况一样,这种情况如果发生异常也是会绕过这两个注解的。同样的道理,因为这种调用不是发起http请求故不会拦截异常

如果单元测试你使用MockMvc的话,如果发生异常还是能拦截到的,虽然是Mock,但毕竟是模拟http请求

@Test
public void testHello() throws Exception {
    mockMvc.perform(MockMvcRequestBuilders
            .get("/hello")
            .accept(MediaType.APPLICATION_JSON_UTF8_VALUE)
            .param("name", "Tom"))
            .andExpect(MockMvcResultMatchers.status().isOk())
            .andExpect(MockMvcResultMatchers.content().string("Hello Tom!"))
            .andDo(MockMvcResultHandlers.print());
}
二、@RestControllerAdvice 或 @ControllerAdvice 是唯一的选择吗?

在全局处理异常的时候,我们思考几个问题

  • 这两个注解,比起AOP拦截controller方法、javax.servlet.Filter拦截controller方法、springboot的Interceptor拦截controller方法,谁先谁后?顺序问题!
  • 上面提到有一种,即endpoint不存在的时候进入不了上述两个注解,能否有什么方法解决?参考本博客下一篇:https://blog.csdn.net/w8y56f/article/details/127166432
三、附录 1、全局处理controller异常的类是怎么写的

如下是本次实验的异常处理类的写法,供参考:捕获到异常后返回ResultBean的 JSON 对象,保证一旦捕获,status code也是200(不能被这个处理器捕获的就不一定是200了)

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(Throwable.class)
    public ResultBean handleException(Throwable t, HttpServletResponse response) throws Throwable {
        return ResultBean.fail(BizCode.FAIL, StackTraceGetter.getStackTrace(t));
    }
}
2、@RestControllerAdvice 和 @ControllerAdvice 区别是什么

区别是,前者相当于是返回 JSON 版本的后者,即 @ResponseBody + @ControllerAdvice,如下图是其源码

在这里插入图片描述

关注
打赏
1663722529
查看更多评论
立即登录/注册

微信扫码登录

0.0362s