如果請求是GET方法,可以直接通過getParameter(String param)方法讀取指定參數,可讀取多次;
而POST方法的參數是存儲在輸入流中,只能讀一次,不能多次讀取。
有時需要在filter里打印請求參數,因而在filter里讀取post請求里的輸入流后,會導致具體的controller里拿不到請求參數。
解決方法:
- 采用ThreadLocal,在filter里把讀取到的post參數存入ThreadLocal里,而controller就可以再從ThreadLocal里把請求參數讀取出來
- 使用servlet提供的HttpServletRequestWrapper類,重寫相關ServletRequest方法,實現多次讀取的能力
1.ThreadLocal方法
ThreadLocal實現:
public class ThreadCache {
// ThreadLocal里只存儲了簡單的String對象,也可以自己定義對象,存儲更加復雜的參數 private static ThreadLocal<String> threadLocal = new ThreadLocal<String>(); public static String getPostRequestParams{ return threadLocal.get(); } public static void setPostRequestParams(String postRequestParams){ threadLocal.set(postRequestParams); } public static void removePostRequestParams(){ threadLocal.remove(); } }
一個簡單的filter:
import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * Created by EndStart on 16/12/19. */ @WebFilter(value = {"/test/threadLocal/*"}) public class SimpleFilter implements Filter { private static Logger log = LoggerFactory.getLogger(SimpleFilter.class); @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse res = (HttpServletResponse) response; try { if ("POST".equals(req.getMethod().toUpperCase())) {
// 獲取請求參數 byte[] bytes = IOUtils.toByteArray(request.getInputStream()); String params = new String(bytes, req.getCharacterEncoding()); ThreadCache.setPostRequestParams(params); log.info("filer-post請求參數:[params={}]", params); } else { log.info("非post請求"); } chain.doFilter(request, response); } catch (Exception e) { log.error(e.getMessage(), e); } } @Override public void destroy() { } }
簡單的測試controller:
import com.sankuai.xm.ems.auth.filter.ThreadCache; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; /** * Created by Endstart on 16/12/19. */ @Controller @RequestMapping("/test") public class TestController { private static Logger log = LoggerFactory.getLogger(TestController.class); @RequestMapping(value = "/threadLocal/getPostRequestParams",method = RequestMethod.POST) @ResponseBody public void getPostRequestParams() { String params = ThreadCache.getPostRequestParams(); log.info("controller-post請求參數:[params={}]", params); } }
這里我們只保存了post參數,從ThreadLocal中反復讀取,而get方法的還需要從request里獲取;
2.HttpServletRequestWrapper方法
實現一個HttpServletRequestWrapper子類:
import org.apache.commons.io.IOUtils; import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import java.io.*; /** * Created by Endstart on 16/11/30. */ public class WrappedHttpServletRequest extends HttpServletRequestWrapper { private byte[] bytes; private WrappedServletInputStream wrappedServletInputStream; public WrappedHttpServletRequest(HttpServletRequest request) throws IOException { super(request); // 讀取輸入流里的請求參數,並保存到bytes里 bytes = IOUtils.toByteArray(request.getInputStream()); ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes); this.wrappedServletInputStream = new WrappedServletInputStream(byteArrayInputStream);
// 很重要,把post參數重新寫入請求流 reWriteInputStream(); } /** * 把參數重新寫進請求里 */ public void reWriteInputStream() { wrappedServletInputStream.setStream(new ByteArrayInputStream(bytes != null ? bytes : new byte[0])); } @Override public ServletInputStream getInputStream() throws IOException { return wrappedServletInputStream; } @Override public BufferedReader getReader() throws IOException { return new BufferedReader(new InputStreamReader(wrappedServletInputStream)); } /** * 獲取post參數,可以自己再轉為相應格式 */ public String getRequestParams() throws IOException { return new String(bytes, this.getCharacterEncoding()); } private class WrappedServletInputStream extends ServletInputStream { public void setStream(InputStream stream) { this.stream = stream; } private InputStream stream; public WrappedServletInputStream(InputStream stream) { this.stream = stream; } @Override public int read() throws IOException { return stream.read(); } @Override public boolean isFinished() { return true; } @Override public boolean isReady() { return true; } @Override public void setReadListener(ReadListener readListener) { } } }
實現另一個filter:
import com.sankuai.xm.ems.utils.wrap.WrappedHttpServletRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import java.io.IOException; /** * Created by EndStart on 16/12/19. */ @WebFilter(value = {"/test/wrapped/*"}) public class SimpleWrappedFilter implements Filter { private static Logger log = LoggerFactory.getLogger(SimpleWrappedFilter.class); @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { try { WrappedHttpServletRequest requestWrapper = new WrappedHttpServletRequest((HttpServletRequest) request); if ("POST".equals(requestWrapper.getMethod().toUpperCase())) {
// 獲取請求參數 String params = requestWrapper.getRequestParams(); log.info("filer-post請求參數:[params={}]", params); } else { log.info("非post請求"); } // 這里doFilter傳入我們實現的子類 chain.doFilter(requestWrapper, response); } catch (Exception e) { log.error(e.getMessage(), e); } } @Override public void destroy() { } }
我們在上面的TestController里加入一個新的處理方法:
@RequestMapping(value = "/wrapped/getPostRequestParams",method = RequestMethod.POST) @ResponseBody // public void getPostRequestParams(@RequestBody String params) { public void getPostRequestParams(HttpServletRequest request) throws Exception{ byte[] bytes = IOUtils.toByteArray(request.getInputStream()); String params = new String(bytes, request.getCharacterEncoding()); log.info("controller-post請求參數:[params={}]", params); }
這種方法里,我們在SimpleWrappedFilter里一個實現了WrappedHttpServletRequest類,其構造器自動讀取了servletRequest里的輸入流,並把數據保存了下來,最后又把數據重新寫入servletRequest里,使得cotroller可以再次從request里讀取到輸入參數。