如果请求是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里读取到输入参数。
时间: 2024-10-11 06:01:39