文章目录
前言
一次SpringCloud Zuul的使用中,发现获取请求参数requestParam总是为null,遂进行了一次简单源码跟踪
一、SpringCloud中,zuul的相关配置类与功能接入类,以及部分调用链
- org.springframework.cloud.netflix.zuul.ZuulServerAutoConfiguration 中配置了一个 org.springframework.cloud.netflix.zuul.web.ZuulController
- ZuulController继承了org.springframework.web.servlet.mvc.ServletWrappingController
而ZuulController的构造器中,调用了方法 setServletClass(ZuulServlet.class)
- ZuulControlle同时重写了Spring-webmvc ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception; 方法
- 在netflix提供的com.netflix.zuul.http.ZuulServlet中,实例化了一个 ZuulRunner
- 而ZuulServlet本质上是一个Servlet,容器会调用init方法,进而调用ZuulRunner的 init方法
- 该方法中调用了 Requecom.netflix.zuul.context.RequestContext的setRequest()方法(zuul的上下文封装,本质是一个map)
- 而当一个请求进入zuulServlet后,会调用servlet的service()方法,这里是所有类型的ZuulFilter的调用
- 这里我们只看preRoute() 方法,其他同理。可以看到,其实是调用了 zuulRunner的preRoute()方法
- 接着调用com.netflix.zuul.FilterProcessor的preRoute方法,
- 可以看到,这里获取了所有类型为pre的过滤器
- 调用processZuulFilter(zuulFilter)方法
- 执行filter的runFilter方法,继而调用了 每个filter重写的run()方法
- 进入我们自己实现的ZuulFilter功能代码
二、记一次 使用 Zuul的 RequestContext获取RequestParameters为null的情况、
- 首先看下 RequestContext.getRequestQueryParams()方法,其实是调用ConcurrentHashMap的get(key)方法(上面说过RequestContext本质是map)
- 这里走自己的过滤器,直接获取requestParam为null,可以看到取得的 RequestContext 中并没有名称为requestQueryParams的key,并且上下文中存放的request是一个org.springframework.cloud.netflix.zuul.filters.pre.Servlet30RequestWrapper的包装类
- 我们继续往下走,获取Servlet30RequestWrapper对象并调用他的getParameterMap方法,而Servlet30RequestWrapper继承了com.netflix.zuul.http.HttpServletRequestWrapper,最终调用的是HttpServletRequestWrapper的getParameterMap()方法
- 进入parseRequest()方法,我们发现又进入了HTTPRequestUtils.getInstance().getQueryParams()这个方法调用
- 而在HTTPRequestUtils.getQueryParams()这个方法的最后,调用了上下文的setRequestQueryParams()方法
- 可以看到这里才真正把requestQueryParams这个key与value put进上下文这个map里面
- 现在 再使用上下文获requestQueryParams 时,就可以获取requestParam了
三、另一个发现:
在org.springframework.cloud.netflix.zuul.filters.pre.Servlet30RequestWrapper#getRequest中
四、解决方法
在过滤器中直接使用
Map<String, List<String>> queryParam = HTTPRequestUtils.getInstance().getQueryParams();
即可获得requestParam
这里贴上getQueryParams的源码
/**
* returns query params as a Map with String keys and Lists of Strings as values
* @return
*/
public Map<String, List<String>> getQueryParams() {
Map<String, List<String>> qp = RequestContext.getCurrentContext().getRequestQueryParams();
if (qp != null) return qp;
HttpServletRequest request = RequestContext.getCurrentContext().getRequest();
qp = new LinkedHashMap<String, List<String>>();
if (request.getQueryString() == null) return null;
StringTokenizer st = new StringTokenizer(request.getQueryString(), "&");
int i;
while (st.hasMoreTokens()) {
String s = st.nextToken();
i = s.indexOf("=");
if (i > 0 && s.length() >= i + 1) {
String name = s.substring(0, i);
String value = s.substring(i + 1);
try {
name = URLDecoder.decode(name, "UTF-8");
} catch (Exception e) {
}
try {
value = URLDecoder.decode(value, "UTF-8");
} catch (Exception e) {
}
List<String> valueList = qp.get(name);
if (valueList == null) {
valueList = new LinkedList<String>();
qp.put(name, valueList);
}
valueList.add(value);
}
else if (i == -1)
{
String name=s;
String value="";
try {
name = URLDecoder.decode(name, "UTF-8");
} catch (Exception e) {
}
List<String> valueList = qp.get(name);
if (valueList == null) {
valueList = new LinkedList<String>();
qp.put(name, valueList);
}
valueList.add(value);
}
}
RequestContext.getCurrentContext().setRequestQueryParams(qp);
return qp;
}