Spring cloud Zuul网关异常处理

Posted 陈小兵

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring cloud Zuul网关异常处理相关的知识,希望对你有一定的参考价值。

Spring cloud Zuul网关异常处理

 

一 异常测试: 

1> 创建一个pre类型的过滤器,并在该过滤器的run方法实现中抛出一个异常。比如下面的实现,在run方法中调用的doSomething方法将抛出RuntimeException异常

 

package com.xbchen.springcloud.filter.post;

import com.netflix.zuul.ZuulFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Component
public class ThrowExceptionPostFilter extends ZuulFilter  {

    private static Logger log = LoggerFactory.getLogger(ThrowExceptionPostFilter.class);

    @Override
    public String filterType() {
        return "post";
    }

    @Override
    public int filterOrder() {
        return 10;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() {
        log.info("This is a post filter, it will throw a RuntimeException");
        doSomething();
        return null;
    }

    private void doSomething() {
        throw new RuntimeException("Exist some errors...");
    }

}

 

 

2> 在启动类中为过滤器创建Bean

@Bean
public ThrowExceptionFilter throwExceptionFilter() {
  return new ThrowExceptionFilter();
}

 

3> 运行启动类

4> 发现后台并没抛出异常信息

 

                                                                                                                                              

二 问题分析:

查看postRoute阶段的异常处理过滤器SendErrorFilter中的方法shouldFilter

 

 

注意: 新版本中已经修复了该问题,取消了error.status_code的判断。

 

即: 只有上线文中包含error.status_code,才能被SendErrorFilter处理。

以此需要自行处理。

处理方式1: 在抛异常时,自行捕获设置error.status_code

@Override
public Object run() {
    log.info("This is a post filter, it will throw a RuntimeException");
    //doSomething();
    RequestContext context=RequestContext.getCurrentContext();
    try{
        doSomething();
    }catch (Exception e){
        context.set("error.status_code", HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        context.set("error.exception",3);
    }
    return null;
}

 

处理方式2:新增一个error处理的过滤器

 

@Component
public class ErrorFilter extends ZuulFilter {
    Logger log = LoggerFactory.getLogger(ErrorFilter.class);
    @Override
    public String filterType() {
        return "error";
    }
    @Override
    public int filterOrder() {
        return 20;
    }
    @Override
    public boolean shouldFilter() {
        return true;
    }
    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        Throwable throwable = RequestContext.getCurrentContext().getThrowable();
        log.error("this is a ErrorFilter : {}", throwable.getCause().getMessage());
        ctx.set("error.status_code", HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        ctx.set("error.exception", throwable.getCause());
        return null;
    }

}

 

三:遗留问题:路由包括3阶段, 正常处理过程:preRoute-->route-->postRoute

其中preRoute 和route异常时,会被捕获直接进入postRoute;

会经过postRoute的过滤器(SendErrorFilter extends ZuulFilter)

但是postRoute阶段的异常,是没有处理的过滤器的。以此需要针对postRoute阶段的异常进行单独处理。

 

处理方式:

新增处理类,扩展过滤器处理类FilterProcessor的processZuulFilter方法

public class DidiFilterProcessor extends FilterProcessor {

    @Override
    public Object processZuulFilter(ZuulFilter filter) throws ZuulException {
        try {
            return super.processZuulFilter(filter);
        } catch (ZuulException e) {
            RequestContext ctx = RequestContext.getCurrentContext();
            ctx.set("failed.exception", e);
            ctx.set("failed.filter", filter);
            throw e;
        }
    }
}

 

在启动类中设置处理类

@SpringBootApplication
@EnableZuulProxy
public class GatewayApplication {

  public static void main(String[] args) {
    FilterProcessor.setProcessor(new DidiFilterProcessor());
    SpringApplication.run(GatewayApplication.class, args);
  }
}

 

 

新增post阶段的异常处理过滤器

/**
 * 从POST抛出的异常,使用该过滤器返回错误信息
 */
@Component
public class ErrorExtFilter extends SendErrorFilter {
    Logger log = LoggerFactory.getLogger(ErrorExtFilter.class);
    @Override
    public String filterType() {
        return "error";
    }
    @Override
    public int filterOrder() {
        return 30;
    }
    @Override
    public boolean shouldFilter() {
        RequestContext ctx = RequestContext.getCurrentContext();
        ZuulFilter failedFilter = (ZuulFilter) ctx.get("failed.filter");
        if(failedFilter != null && failedFilter.filterType().equals("post")) {
            return true;
        }
        return false;
    }
}

 

四:异常自定义

 

以上异常已经正常抛出,但是格式并不一定符合项目要求,或者有时并不需要将异常返回给客户端。

 

由于异常信息格式是在DefaultErrorAttributes定义的,以此可对该类进行扩展。

比如:异常异常信息,避免返回到客户端。

public class DidiErrorAttributes extends DefaultErrorAttributes {

    @Override
    public Map<String, Object> getErrorAttributes (
            RequestAttributes requestAttributes, boolean includeStackTrace){
        Map<String, Object> result = super.getErrorAttributes(requestAttributes, includeStackTrace);
        result.remove("exception");
        return result;
    }
}

 


在启动类中新增异常属性扩展类的Bean创建。

@Bean
  public DefaultErrorAttributes errorAttributes() {
    return new DidiErrorAttributes();
  }

 

以上是关于Spring cloud Zuul网关异常处理的主要内容,如果未能解决你的问题,请参考以下文章

Zuul源码分析之 网关处理流程

Zuul源码分析之 网关处理流程

(34)java Spring Cloud+Spring boot+mybatis企业快速开发架构之SpringCloud-Zuul过滤器介绍及使用(传递数据拦截请求和异常处理)

Zuul源码分析之 网关处理流程

Zuul源码分析之 网关处理流程

网关 zuul 与 spring-cloud gateway的区别