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

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

    
    
  1. package com.tianyalei.testzuul;
  2. import com.netflix.zuul.ZuulFilter;
  3. import com.netflix.zuul.context.RequestContext;
  4. import org.slf4j.Logger;
  5. import org.slf4j.LoggerFactory;
  6. import org.springframework.stereotype.Component;
  7. import javax.servlet.http.HttpServletRequest;
  8. @Component
  9. public class AccessFilter extends ZuulFilter {
  10. private static Logger log = LoggerFactory.getLogger(AccessFilter.class);
  11. @Override
  12. public String filterType() {
  13. //前置过滤器
  14. return "pre";
  15. }
  16. @Override
  17. public int filterOrder() {
  18. //优先级,数字越大,优先级越低
  19. return 0;
  20. }
  21. @Override
  22. public boolean shouldFilter() {
  23. //是否执行该过滤器,true代表需要过滤
  24. return true;
  25. }
  26. @Override
  27. public Object run() {
  28. RequestContext ctx = RequestContext.getCurrentContext();
  29. HttpServletRequest request = ctx.getRequest();
  30. log.info( "send {} request to {}", request.getMethod(), request.getRequestURL().toString());
  31. //获取传来的参数accessToken
  32. Object accessToken = request.getParameter( "accessToken");
  33. if(accessToken == null) {
  34. log.warn( "access token is empty");
  35. //过滤该请求,不往下级服务去转发请求,到此结束
  36. ctx.setSendZuulResponse( false);
  37. ctx.setResponseStatusCode( 401);
  38. ctx.setResponseBody( "{\"result\":\"accessToken为空!\"}");
  39. ctx.getResponse().setContentType( "text/html;charset=UTF-8");
  40. return null;
  41. }
  42. //如果有token,则进行路由转发
  43. log.info( "access token ok");
  44. //这里return的值没有意义,zuul框架没有使用该返回值
  45. return null;
  46. }
  47. }
第二个前置过滤器

    
    
  1. package com.tianyalei.testzuul;
  2. import com.netflix.zuul.ZuulFilter;
  3. import com.netflix.zuul.context.RequestContext;
  4. import org.slf4j.Logger;
  5. import org.slf4j.LoggerFactory;
  6. import org.springframework.stereotype.Component;
  7. import javax.servlet.http.HttpServletRequest;
  8. @Component
  9. public class SecondFilter extends ZuulFilter {
  10. private static Logger log = LoggerFactory.getLogger(SecondFilter.class);
  11. @Override
  12. public String filterType() {
  13. //前置过滤器
  14. return "pre";
  15. }
  16. @Override
  17. public int filterOrder() {
  18. //优先级,数字越大,优先级越低
  19. return 1;
  20. }
  21. @Override
  22. public boolean shouldFilter() {
  23. //是否执行该过滤器,true代表需要过滤
  24. return true;
  25. }
  26. @Override
  27. public Object run() {
  28. RequestContext ctx = RequestContext.getCurrentContext();
  29. HttpServletRequest request = ctx.getRequest();
  30. log.info( "second过滤器");
  31. return null;
  32. }
  33. }

后置过滤器

    
    
  1. package com.tianyalei.testzuul;
  2. import com.netflix.zuul.ZuulFilter;
  3. import com.netflix.zuul.context.RequestContext;
  4. import org.slf4j.Logger;
  5. import org.slf4j.LoggerFactory;
  6. import org.springframework.stereotype.Component;
  7. @Component
  8. public class PostFilter extends ZuulFilter {
  9. private static Logger log = LoggerFactory.getLogger(PostFilter.class);
  10. @Override
  11. public String filterType() {
  12. //后置过滤器
  13. return "post";
  14. }
  15. @Override
  16. public int filterOrder() {
  17. //优先级,数字越大,优先级越低
  18. return 0;
  19. }
  20. @Override
  21. public boolean shouldFilter() {
  22. //是否执行该过滤器,true代表需要过滤
  23. return true;
  24. }
  25. @Override
  26. public Object run() {
  27. RequestContext ctx = RequestContext.getCurrentContext();
  28. log.info( "进入post过滤器");
  29. System.out.println(ctx.getResponseBody());
  30. ctx.setResponseBody( "post后置数据");
  31. int i = 1 / 0;
  32. return null;
  33. }
  34. }
异常过滤器


    
    
  1. package com.tianyalei.testzuul;
  2. import com.netflix.zuul.ZuulFilter;
  3. import com.netflix.zuul.context.RequestContext;
  4. import org.slf4j.Logger;
  5. import org.slf4j.LoggerFactory;
  6. import org.springframework.stereotype.Component;
  7. @Component
  8. public class ErrorFilter extends ZuulFilter {
  9. private static Logger log = LoggerFactory.getLogger(ErrorFilter.class);
  10. @Override
  11. public String filterType() {
  12. //异常过滤器
  13. return "error";
  14. }
  15. @Override
  16. public int filterOrder() {
  17. //优先级,数字越大,优先级越低
  18. return 0;
  19. }
  20. @Override
  21. public boolean shouldFilter() {
  22. //是否执行该过滤器,true代表需要过滤
  23. return true;
  24. }
  25. @Override
  26. public Object run() {
  27. RequestContext ctx = RequestContext.getCurrentContext();
  28. log.info( "进入异常过滤器");
  29. System.out.println(ctx.getResponseBody());
  30. ctx.setResponseBody( "出现异常");
  31. return null;
  32. }
  33. }
定义好之后,直接测试看看
可以看到结果就是按照上面说的顺序在执行。
但是最终给用户呈现这样一个界面就不合适的,我们应该去处理这个"/error"映射的问题。
所以我再定义一个Controller

    
    
  1. package com.tianyalei.testzuul;
  2. import org.springframework.boot.autoconfigure.web.ErrorController;
  3. import org.springframework.web.bind.annotation.RequestMapping;
  4. @RestController
  5. public class ErrorHandlerController implements ErrorController {
  6. /**
  7. * 出异常后进入该方法,交由下面的方法处理
  8. */
  9. @Override
  10. public String getErrorPath() {
  11. return "/error";
  12. }
  13. @RequestMapping( "/error")
  14. public String error() {
  15. return "出现异常";
  16. }
  17. }
在"/error"方法里返回你想给客户端返回的值即可。








  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值