原文地址:https://segmentfault.com/a/1190000011650034
1 描述
使用Spring Cloud Zuul進行路由轉發時候嗎,文件上傳會造成中文亂碼“?”。
1.1 解決
Spring Cloud Zuul現在對於上傳文件有兩種處理方式,一種是用spring mvc,另一種是zuulServlet。spring mvc對文件處理不是很好,會導致亂碼問題,zuulServlet則不會。
網上比較常見的解決方案是在uri前加/zuul 使用zuul的servlet繞開springmvc來解決上傳文件亂碼問題
如原文:https://my.oschina.net/kmnztech/blog/1618636
比如:原來你上傳文件的路徑是/api/file/upload, 則你可以通過uri /zuul/api/file/upload來調用接口上傳文件,中文編碼問題解決。
但是,在我項目中,在api路徑前加個zuul總覺得變扭,於是嘗試找到一種在改變最少就能解決問題的方法。
通過閱讀如官網說明。
其實你可以通過zuul.servlet-path來配置使用zuul的servlet。接着,我在網關的application.properties中添加了:
zuul.servlet-path=/
1.2 解決
對於已經固定了請求路徑的來說,再次修改路徑不是很好的,所以就采用如下方案:
在過濾器中,有一個pre的過濾器 ServletDetectionFilter,他的執行順序是-3,也是最先執行的過濾器,在這個過濾器中,有這么一段代碼:
public class ServletDetectionFilter extends ZuulFilter { public ServletDetectionFilter() { } public String filterType() { return "pre"; } public int filterOrder() { return -3; } public boolean shouldFilter() { return true; } public Object run() { RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); if (!(request instanceof HttpServletRequestWrapper) && this.isDispatcherServletRequest(request)) { ctx.set("isDispatcherServletRequest", true); } else { ctx.set("isDispatcherServletRequest", false); } return null; } private boolean isDispatcherServletRequest(HttpServletRequest request) { return request.getAttribute(DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null; } }
在這個方法中,IS_DISPATCHER_SERVLET_REQUEST_KEY為false就會用ZuulServlet處理。如果沒加/zuul前綴IS_DISPATCHER_SERVLET_REQUEST_KEY就會置為true,就會用spring mvc上傳。會出現亂碼問題。
那么我們的一個解決方案是在在進入下一個過濾器之前我們就把我們的文件上傳的請求用ZuulServlet處理,所以我們可以重寫這個方法,根據contentType判斷請求如果是multipart就將IS_DISPATCHER_SERVLET_REQUEST_KEY置為false,那么它就會用ZuulServlet處理。
@Component public class ServletDetectionFilterFile extends ServletDetectionFilter { @Override public Object run() { RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); if (!(request instanceof HttpServletRequestWrapper) && this.isDispatcherServletRequest(request) && !this.isMultipartContent(request)) { ctx.set("isDispatcherServletRequest", true); } else { ctx.set("isDispatcherServletRequest", false); } return null; } private boolean isDispatcherServletRequest(HttpServletRequest request) { return request.getAttribute(DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null; } /** * 判斷是否是multipart/form-data請求 * @param request 請求 * @return 是否是 */ private boolean isMultipartContent(HttpServletRequest request) { String requesType = "post"; if(!requesType.equals(request.getMethod().toLowerCase())) { return false; } //獲取Content-Type String contentType = request.getContentType(); return (contentType != null) && (contentType.toLowerCase().startsWith("multipart/")); } }
到這里還沒有結束,還有一個很坑的地方,在最后以前pre過濾器中,他會對url進行處理。如果該請求是ZuulServlet處理的,那么他會把url的前面幾位用的zuulServletPath替代,zuulServletPath默認就是剛才我們替代的前綴 /zuul。那么如果你不處理的話,你的請求路徑將會變化。所以在這里我們將這個默認的zuulServletPath改成空值,就不會替換啦。在配置文件里面加上:
zuul.servletPath:
后面的值不填。