1. 程式人生 > >Spring Cloud Zuul(API閘道器服務)(3)

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:過濾器的具體邏輯。在該函式中,我們可以實現自定義的過濾邏輯,來確定是否要攔截當前的請求,不對其進行後續路由,或是在請求路由返回結果之後,對處理結果做一些加工等。

請求宣告週期

當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();}

自定義異常資訊

自定義異常只需要繼承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過濾器了。