天天看點

Spring Cloud Zuul(API網關服務)(3)

過濾器

在Spring Cloud Zuul中實作的過濾器必須包含4個基本特征:過濾類型,執行順序,執行條件,具體操作。這就是ZuulFilter接口中定義的4個抽象方法:

public abstract String filterType();

    public abstract int filterOrder();

    boolean shouldFilter();

    Object run() throws ZuulException;
           

1.filterType:該函數傳回一個字元串來代表過濾器的類型,而這個類型就是在HTTP請求過程中定義的各個階段。在Zuul中預設定義了4種不同生命周期的過濾類型,如:

    1.pre:可以在請求被路由之前調用。

    2.routing:在路由請求時被調用。

    3.post:在routing和error過濾器之後被調用。

    4.error:處理請求時發生錯誤時被調用。

2.filterOrder:通過int值來定義過濾器的執行順序,數值越小優先級越高。

3.shouldFilter:傳回一個boolean值來判斷該過濾器是否要執行。我們可以通過此方法來指定過濾器的有效範圍。

4.run:過濾器的具體邏輯。在該函數中,我們可以實作自定義的過濾邏輯,來确定是否要攔截目前的請求,不對其進行後續路由,或是在請求路由傳回結果之後,對處理結果做一些加工等。

請求聲明周期

Spring Cloud Zuul(API網關服務)(3)

當HTTP請求到達API網關服務的時候,首先會進入第一個階段pre,在這裡他會被pre類型的過濾器進行處理,該類型過濾器的主要目的是在進行請求路由之前做一些前置加工,比如請求的校驗等。在完成了pre類型的過濾器處理之後請求進入第二個階段routing(請求轉發階段),請求将會被routing類型過濾器處理。這裡的具體處理内容就是将外部請求轉發到具體服務執行個體上去的過程,當服務執行個體将請求結果都傳回之後,routing階段完成,請求進入第三個階段post,此時請求将會被post類型的過濾器進行處理,這些過濾器在處理的時候不僅可以擷取到請求資訊,還能擷取到服務執行個體的傳回資訊,是以在post類型的過濾器中,我們可以對處理結果進行一些加工或轉換等内容。另外,還有一個特殊的階段error,該階段隻有在上述三個階段中發生異常的時候才會觸發,但是它的最後流向還是post類型的過濾器,因為它需要通過post過濾器将最終結果傳回給請求用戶端。

異常處理

try-catch處理

在post過濾器中SendErrorFilter是用來處理異常資訊。它會調用這個方法:

public boolean shouldFilter() {
    RequestContext ctx = RequestContext.getCurrentContext();
    // only forward to errorPath if it hasn't been forwarded to already
    return ctx.getThrowable() != null
            && !ctx.getBoolean(SEND_ERROR_FILTER_RAN, false);
}
           

該方法的傳回值中有個判斷依據ctx.containsKey("error.status_code")也就是說請求上下文中必須有error.status_code參數,我們實作的ThorwExceptionFilter中沒有設定這個參數,是以不會進入SendErrorFilter過濾器的處理邏輯。下面修改下Filter過濾器:

@Component
public class AccessFilter extends ZuulFilter {

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

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

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

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

    @Override
    public Object run(){
        RequestContext ctx = RequestContext.getCurrentContext();
        try {

            throw new RuntimeException("異常");
        }catch (Exception ex){
            ctx.set("error.status_code",HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            ctx.set("error.exception",ex);
            ctx.set("error.message",ex.getLocalizedMessage());
        }
        return ctx;
    }
}
           

它會抛出這樣的異常:

{ 
"timestamp": 1513303905953, 
"status": 400, 
"error": "", 
"exception": "", 
"message": ""
}

           

status:對應error.status_code參數的值。

exception:對應error.exception參數中Exception的類型。

message:對應error.exception參數中Exception的message資訊。

ErrorFilter處理

@Component
public class ErrorFilter extends ZuulFilter {

    Logger log=LoggerFactory.getLogger(ErrorFilter.class);

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

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

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

    @Override
    public Object run() throws ZuulException {
        RequestContext ctx=RequestContext.getCurrentContext();
        Throwable thrrowable=ctx.getThrowable();
        log.error("this is a ErrorFilter:{}",thrrowable.getCause().getMessage());
        ctx.set("error.status_code",HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        ctx.set("error.exception",thrrowable.getCause());
        ctx.set("error.message",thrrowable.getMessage());
        return null;
    }
}
           

然後在主類上添加這個bean

@Bean
   public ErrorFilter errorFilter(){return new ErrorFilter();}
           
Spring Cloud Zuul(API網關服務)(3)

自定義異常資訊

自定義異常隻需要繼承DefaultErrorAttributes類然後實作getErrorAttributes方法:

@Component
public class DidiErrorAttributes extends DefaultErrorAttributes {

    @Override
    public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
        Map<String, Object> result = super.getErrorAttributes(webRequest, includeStackTrace);
        result.put("a","aa");
        return result;
    }
}
           

通過這個方法就能自由的自定義錯誤傳回資訊。

禁用過濾器

在zuul中提供了一個參數來禁用指定的過濾器:

zuul.<SimpleClassName>.<filterType>.disable=true
           

如果想要禁用AccessFilter:

zuul.AccessFilter.pre.disable=true
           

:這樣就能禁用AccessFilter過濾器了。

繼續閱讀