文章目錄
- 前言
- 一、SpringCloud中,zuul的相關配置類與功能接入類,以及部分調用鍊
- 二、記一次 使用 Zuul的 RequestContext擷取RequestParameters為null的情況、
- 三、另一個發現:
- 四、解決方法
前言
一次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中
四、解決方法
在過濾器中直接使用
即可獲得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;
}