上一篇介绍了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进行处理。
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的执行顺序。
![](https://i-blog.csdnimg.cn/blog_migrate/0d093e4e6466127c48b5326f463d4919.jpeg)
直接用一个简单的示例来看看结果
第一个前置过滤器
-
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;
-
-
}
-
-
}
定义好之后,直接测试看看
![](https://i-blog.csdnimg.cn/blog_migrate/0054c01d16335530b7396d35db15f70a.jpeg)
![](https://i-blog.csdnimg.cn/blog_migrate/7c06fd0f88985941375faacb2b2f8e7d.jpeg)
![](https://i-blog.csdnimg.cn/blog_migrate/01e27a9b3afa245d9e354c084c31e607.jpeg)
可以看到结果就是按照上面说的顺序在执行。
但是最终给用户呈现这样一个界面就不合适的,我们应该去处理这个"/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"方法里返回你想给客户端返回的值即可。