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


    本文转载自:https://blog.csdn.net/tianyaleixiaowu/article/details/77893822

    上一篇介绍了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的执行顺序。
    直接用一个简单的示例来看看结果
    第一个前置过滤器
    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;  
        }  
      
    }  

    第二个前置过滤器

    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;  
      
        }  
      
    }

    后置过滤器

    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;  
      
        }  
      
    }

    异常过滤器

    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
    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"方法里返回你想给客户端返回的值即可。

  • 相关阅读:
    推荐有关微信开发的十个开源项目
    curl 常见错误码
    mysql修改root密码
    BAT批处理之文件与文件夹操作代码(附xcopy命令详解)
    UE4 Pak包加载
    libcurl 文件上传
    win7右键在目录当前打开命令cmd窗口
    SQLiteDeveloper破解
    Excel替换应用
    解决局域网2级路由互相连接共享的问题
  • 原文地址:https://www.cnblogs.com/wpcnblog/p/9054837.html
Copyright © 2020-2023  润新知