多次读取请求request里数据

如果请求是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

多次读取请求request里数据的相关文章

java读取请求中body数据

/** * 获取request中body数据 * * @author lifq * * 2017年2月24日 下午2:29:06 * @throws IOException */ public static String getRequestBodyData(HttpServletRequest request) throws IOException{ BufferedReader bufferReader = new BufferedReader(request.getReader()); S

[原创]java WEB学习笔记59:Struts2学习之路---OGNL,值栈,读取对象栈中的对象的属性,读取 Context Map 里的对象的属性,调用字段和方法,数组,list,map

本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱好者,互联网技术发烧友 微博:伊直都在0221 QQ:951226918 -----------------------------------------------------------------------------------------------------------------

django HTTP请求(Request)和回应(Response)对象

Django使用request和response对象在系统间传递状态.—(阿伦)当一个页面被请示时,Django创建一个包含请求元数据的 HttpRequest 对象. 然后Django调入合适的视图,把HttpRequest 作为视图的函数的第一个参数 传入.每个视图要负责返回一个 HttpResponse 对象. HttpRequest对象HttpRequest 表示来自某客户端的一个单独的HTTP请求.HttpRequest实例的属性包含了关于此次请求的大多数重要信息(详见表H-1). 除

python的post请求抓取数据

python通过get方式,post方式发送http请求和接收http响应-urllib urllib2 python通过get方式,post方式发送http请求和接收http响应-- import urllib模块,urllib2模块, httplib模块 http://blog.163.com/[email protected]/blog/static/132229655201231085444250/ 测试用CGI,名字为test.py,放在apache的cgi-bin目录下:#!/usr

fiddler 设置断点修改请求,响应数据及模拟响应

在测试过程中,有时候需要修改请求或响应数据,或者直接模拟服务器响应,此时可以使用fiddler进行此类操作.可以使用断点功能完成. 一.修改请求数据 在发起请求后,需要修改请求的数据时,可以设置请求前设置断点 (1.设置请求前断点 Rules--Automatic breakpoints--before request(或者按F11键) 这种方式的断点会对所有请求生效 或者 在命令行中,输入bpu 域名 这种方式只会对配置的域名添加断点,访问其他的可以正常访问,不会有断点 (2.选中请求,进入I

Iris请求方式和数据返回类型

1. Iris起服务 package main import "github.com/kataras/iris" func main() { //1.创建app结构体对象 app := iris.New() //返回一个application //2.端口监听(启动服务本质就是监听端口) //iris.WithoutServerError 设置错误 app.Run(iris.Addr(":7999"), iris.WithoutServerError(iris.Er

对tomcat来说,每一个进来的请求(request)都需要一个线程,直到该请求结束。

这段时间折腾了哈java web应用的压力测试,部署容器是tomcat 7.期间学到了蛮多散碎的知识点,及时梳理总结,构建良好且易理解的知识架构把它们组织起来,以备忘.对web应用开发者来说,我们很关心应用可同时处理的请求数,以及响应时间.应用本身和它运行在其中的web容器是两个很重要的影响因素.对tomcat来说,每一个进来的请求(request)都需要一个线程,直到该请求结束.如果同时进来的请求多于当前可用的请求处理线程数,额外的线程就会被创建,直到到达配置的最大线程数(maxThreads

ajax请求获取的数据无法赋值给全局变量问题总结

一.总结: 1.问题描述: 今天做项目遇到在用表单显示详细信息的过程中ajax请求获取的数据无法赋值给全局变量的情况,从列表页面进入详情页,在详情页面被渲染了之后就会调用js文件里的接口向服务器请求数据,除了详情页面被加载之后需要向服务器发送ajax请求,在详情页面还有几个表单控件的属性需要去请求服务器获取实际项目中要求要显示的数据. 如下代码,直接在ajax请求中改变表单控件的值,避开了给全局变量赋值. 给全局变量赋值的解决办法,给ajax请求设置async为false,表示请求为同步请求:

如何使用multipart/form-data格式上传文件(POST请求时,数据是放在请求体内,而不是请求头内,在html协议中,用 “\r\n” 换行,而不是 “\n”)

在网络编程过程中需要向服务器上传文件.Multipart/form-data是上传文件的一种方式. Multipart/form-data其实就是浏览器用表单上传文件的方式.最常见的情境是:在写邮件时,向邮件后添加附件,附件通常使用表单添加,也就是用multipart/form-data格式上传到服务器. 表单形式上传附件 具体的步骤是怎样的呢? 首先,客户端和服务器建立连接(TCP协议). 第二,客户端可以向服务器端发送数据.因为上传文件实质上也是向服务器端发送请求. 第三,客户端按照符合“m