前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >zuul网关Filter处理流程及异常处理

zuul网关Filter处理流程及异常处理

作者头像
天涯泪小武
发布2019-01-17 11:43:35
9750
发布2019-01-17 11:43:35
举报
文章被收录于专栏:SpringCloud专栏SpringCloud专栏

上一篇介绍了java网关Zuul的简单使用,进行请求路由转发和过滤器的基本操作。

这一篇主要看一下它的过滤器Filter的工作流程及异常处理。

首先看到Filter的四个方法,FilterType,filterOrder,shouldFilter,run。

filterType代表过滤类型

PRE: 该类型的filters在Request routing到源web-service之前执行。用来实现Authentication、选择源服务地址等 ROUTING:该类型的filters用于把Request routing到源web-service,源web-service是实现业务逻辑的服务。这里使用HttpClient请求web-service。 POST:该类型的filters在ROUTING返回Response后执行。用来实现对Response结果进行修改,收集统计数据以及把Response传输会客户端。 ERROR:上面三个过程中任何一个出现错误都交由ERROR类型的filters进行处理。

主要关注 pre、post和error。分别代表前置过滤,后置过滤和异常过滤。

如果你的filter是pre的,像上一篇那种,就是指请求先进入pre的filter类,你可以进行一些权限认证,日志记录,或者额外给Request增加一些属性供后续的filter使用。pre会优先按照order从小到大执行,然后再去执行请求转发到业务服务。

再说post,如果type为post,那么就会执行完被路由的业务服务后,再进入post的filter,在post的filter里,一般做一些日志记录,或者额外增加response属性什么的。

最后error,如果在上面的任何一个地方出现了异常,就会进入到type为error的filter中。

filterOrder代表过滤器顺序

这个不多说,试一下就知道了。

shouldFilter代表这个过滤器是否生效

true代表生效,false代表不生效。那么什么情况下使用不生效呢,不生效干嘛还要写这个filter类呢?

其实是有用的,有时我们会动态的决定让不让一个filter生效,譬如我们可能根据Request里是否携带某个参数来判断是否需要生效,或者我们需要从上一个filter里接收某个数据来决定,再或者我们希望能手工控制是否生效(使用如Appolo之类的配置中心,来动态设置该字段)。

Run方法

这个是主要的处理逻辑的地方,我们做权限控制、日志等都是在这里。

下图是filter的执行顺序。

直接用一个简单的示例来看看结果

第一个前置过滤器

代码语言:javascript
复制
package com.tianyalei.testzuul;

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

import javax.servlet.http.HttpServletRequest;

@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() {
        //是否执行该过滤器,true代表需要过滤
        return true;
    }

    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();

        log.info("send {} request to {}", request.getMethod(), request.getRequestURL().toString());

        //获取传来的参数accessToken
        Object accessToken = request.getParameter("accessToken");
        if(accessToken == null) {
            log.warn("access token is empty");
            //过滤该请求,不往下级服务去转发请求,到此结束
            ctx.setSendZuulResponse(false);
            ctx.setResponseStatusCode(401);
            ctx.setResponseBody("{\"result\":\"accessToken为空!\"}");
            ctx.getResponse().setContentType("text/html;charset=UTF-8");
            return null;
        }
        //如果有token,则进行路由转发
        log.info("access token ok");
        //这里return的值没有意义,zuul框架没有使用该返回值
        return null;
    }

}

第二个前置过滤器

代码语言:javascript
复制
package com.tianyalei.testzuul;

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

import javax.servlet.http.HttpServletRequest;

@Component
public class SecondFilter extends ZuulFilter {

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

    @Override
    public String filterType() {
        //前置过滤器
        return "pre";
    }

    @Override
    public int filterOrder() {
        //优先级,数字越大,优先级越低
        return 1;
    }

    @Override
    public boolean shouldFilter() {
        //是否执行该过滤器,true代表需要过滤
        return true;
    }

    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();

        log.info("second过滤器");

        return null;

    }

}

后置过滤器

代码语言:javascript
复制
package com.tianyalei.testzuul;

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

@Component
public class PostFilter extends ZuulFilter {

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

    @Override
    public String filterType() {
        //后置过滤器
        return "post";
    }

    @Override
    public int filterOrder() {
        //优先级,数字越大,优先级越低
        return 0;
    }

    @Override
    public boolean shouldFilter() {
        //是否执行该过滤器,true代表需要过滤
        return true;
    }

    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        log.info("进入post过滤器");
        System.out.println(ctx.getResponseBody());

        ctx.setResponseBody("post后置数据");

        int i = 1 / 0;

        return null;

    }

}

异常过滤器

代码语言:javascript
复制
package com.tianyalei.testzuul;

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

@Component
public class ErrorFilter extends ZuulFilter {

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

    @Override
    public String filterType() {
        //异常过滤器
        return "error";
    }

    @Override
    public int filterOrder() {
        //优先级,数字越大,优先级越低
        return 0;
    }

    @Override
    public boolean shouldFilter() {
        //是否执行该过滤器,true代表需要过滤
        return true;
    }

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

        log.info("进入异常过滤器");

        System.out.println(ctx.getResponseBody());

        ctx.setResponseBody("出现异常");

        return null;

    }

}

定义好之后,直接测试看看

可以看到结果就是按照上面说的顺序在执行。

但是最终给用户呈现这样一个界面就不合适的,我们应该去处理这个"/error"映射的问题。

所以我再定义一个Controller

代码语言:javascript
复制
package com.tianyalei.testzuul;

import org.springframework.boot.autoconfigure.web.ErrorController;
import org.springframework.web.bind.annotation.RequestMapping;
@RestController
public class ErrorHandlerController implements ErrorController {

    /**
     * 出异常后进入该方法,交由下面的方法处理
     */
    @Override
    public String getErrorPath() {
        return "/error";
    }

    @RequestMapping("/error")
    public String error() {
        return "出现异常";
    }
}

在"/error"方法里返回你想给客户端返回的值即可。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017年09月08日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • filterType代表过滤类型
  • filterOrder代表过滤器顺序
  • shouldFilter代表这个过滤器是否生效
  • Run方法
相关产品与服务
API 网关
腾讯云 API 网关(API Gateway)是腾讯云推出的一种 API 托管服务,能提供 API 的完整生命周期管理,包括创建、维护、发布、运行、下线等。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档