SpringBoot 过滤器filter当中的自定义异常捕获问题

项目场景:

在SpringBoot项目中引入Spring-Cloud-Oauth2 实现客户端权限管理,项目中使用的是客户端模式,由于框架自带的校验及异常信息给的并不友好,不符合我们自己项目异常格式。需要我们自己在拦截器当中对客户端信息进行校验,校验成功时放行,失败时抛出自定义异常

   filter逻辑如下:

@Component
public class CheckClientInfoFilter extends OncePerRequestFilter {

   
    @Override
    protected void doFilterInternal(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull FilterChain filterChain) throws ServletException, IOException {
            //客户端信息校验..
            boolean isSuccess = checkClientInfo(request);
            if(!isSuccess){
                throw new CustomException("客户端信息非法!!!")
            }
            
            filterChain.doFilter(request, response);
    }
}

 全局异常处理:

@RestControllerAdvice
public class GlobalExceptionHandler {

	/**
	 * 处理自定义的业务异常
	 *
	 * @param req
	 * @param e
	 * @return ResponseResult
	 */
	@ExceptionHandler(value = BusinessException.class)
	public ResponseResult<Object> customExceptionHandler(HttpServletRequest req, CustomException e) {
		log.error("发生业务异常!原因是:{}", e.getErrorMsg());
        //按统一响应格式返回错误信息
		return ResponseResult.fail(e.getErrorCode(), e.getMessage());
	}
}

 

 


问题描述

一开始以为,上面这种直接在filter抛出的异常能够被全局异常处理类捕获,但实际上的错误响应却是这样

{
    "timestamp": "2021-05-13T08:53:18.355+0000",
    "status": 500,
    "error": "Internal Server Error",
    "message": "客户端信息非法!!!",
    "path": "/oauth/token"
}

并不是我们期望的响应格式,我们的自定义异常并没有被全局异常处理类(GlobalExceptionHandler)捕获,而是被Spring框架捕获了

 


原因分析:

在Spring Boot由于全局异常处理@RestControllerAdvice只会去捕获所有Controller层抛出的异常,所以在filter当中抛出的异常GlobalExceptionHandler类是没有感知的,

所以在filter当中抛出的异常最终会被Spring框架自带的全局异常处理类BasicErrorController捕获,返回上面我们看到的Json响应


解决方案:

方法一:

继承上面所说的BasicErrorController类,并重写error()方法,代码如下:

@Component
public class FilterErrorController extends BasicErrorController {
    public FilterErrorController() {
        super(new DefaultErrorAttributes(), new ErrorProperties());
    }

    @Override
    @RequestMapping
    public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
        HttpStatus status = getStatus(request);
        if (status == HttpStatus.NO_CONTENT) {
            return new ResponseEntity<>(status);
        }
        Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL));
        //重写body自定义反参格式
        Map<String,Object> myBody = new HashMap<>();

        return new ResponseEntity<>(myBody, status);
    }

}

这种方法虽然可以满足我们的要求,但是值得注意的是,如果重写该方法那么所有未被@RestControllerAdvice捕获的异常都会进入该方法当中,如果在filter当中抛出的异常信息不止一种,这个时候就会比较麻烦,需要通过body中携带的异常信息做判断才能做出相应的处理,但是body中携带的异常信息包括如下几个字段:

由于所有未被全局异常捕获的自定义异常,框架都会返回http状态码为500,我们只能拿到自定义异常的message而拿不到错误码,所以可用的字段只有message一项,我们要通过错误信息的字符串比对才能做出相应处理,这样的处理方式虽然也可以实现,但是非常不灵活。

方法二:

在filter当中引入HandlerExceptionResolver类,通过该类的resolveException方法抛出自定义异常,代码如下:

@Component
public class CheckClientInfoFilter extends OncePerRequestFilter {
    @Autowired
    @Qualifier("handlerExceptionResolver")
    private HandlerExceptionResolver resolver;
   
    @Override
    protected void doFilterInternal(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull FilterChain filterChain) throws ServletException, IOException {
            //客户端信息校验..
            boolean isSuccess = checkClientInfo(request);
            if(!isSuccess){
                throw resolver.resolveException(request,response,                 
                               null,newCustomException("客户端信息非法!!!");
                return;
            }
            
            filterChain.doFilter(request, response);
    }
}

通过resolveException方法抛出的自定义异常可以被RestControllerAdvice捕获,从而满足我们的需求,最终得到的响应格式:

{
    
    "info": {
        "resultCode": "9999",
        "resultMsg": "客户端信息非法!!!"
    },
    "data": null
}

 

Spring Boot中,要自定义过滤器,需要遵循以下步骤: 1. 创建一个类,实现javax.servlet.Filter接口。例如: ``` @Component public class MyFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { // 初始化 } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // 过滤逻辑 chain.doFilter(request, response); } @Override public void destroy() { // 销毁 } } ``` 2. 在类上添加@Component注解,使其成为Spring Bean。 3. 在Spring Boot应用程序中添加过滤器。可以通过注解@WebFilter或者通过FilterRegistrationBean来实现。例如: ``` @Configuration public class MyFilterConfig { @Bean public FilterRegistrationBean<MyFilter> myFilter() { FilterRegistrationBean<MyFilter> registrationBean = new FilterRegistrationBean<>(); registrationBean.setFilter(new MyFilter()); registrationBean.addUrlPatterns("/api/*"); return registrationBean; } } ``` 以上代码创建了一个名为“myFilter”的过滤器,并将其添加到“/api/*”路径下。可以根据需要更改名称和路径。 4. 在应用程序的配置文件(如application.properties)中配置过滤器。例如: ``` spring.servlet.filter-order.myFilter=1 ``` 以上代码将“myFilter过滤器的顺序设置为1。可以根据需要更改顺序。 5. 运行应用程序并测试过滤器。 以上就是Spring Boot中自定义过滤器的步骤。
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值