`
liwx2000
  • 浏览: 131764 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

ServletRequest中getReader()和getInputStream()只能调用一次的解决办法

阅读更多
最近使用spring mvc做项目,数据格式是json,有一个功能是实现记录请求的参数,而请求的参数是整个RequestBody,Controller里是用过@RequestBody获取的。实现方法是通过一个Filter读取整个RequestBody并记录。但是这时就遇到一个问题,ServletRequest的getReader()和getInputStream()两个方法只能被调用一次,而且不能两个都调用。那么如果Filter中调用了一次,在Controller里面就不能再调用了。查看了下ServletRequest的说明,如下:

    /**
     * Retrieves the body of the request as binary data using
     * a {@link ServletInputStream}.  Either this method or 
     * {@link #getReader} may be called to read the body, not both.
     *
     * @return			a {@link ServletInputStream} object containing
     * 				the body of the request
     *
     * @exception IllegalStateException  if the {@link #getReader} method
     * 					 has already been called for this request
     *
     * @exception IOException    	if an input or output exception occurred
     *
     */

    public ServletInputStream getInputStream() throws IOException; 

    /**
     * Retrieves the body of the request as character data using
     * a <code>BufferedReader</code>.  The reader translates the character
     * data according to the character encoding used on the body.
     * Either this method or {@link #getInputStream} may be called to read the
     * body, not both.
     * 
     *
     * @return					a <code>BufferedReader</code>
     *						containing the body of the request	
     *
     * @exception UnsupportedEncodingException 	if the character set encoding
     * 						used is not supported and the 
     *						text cannot be decoded
     *
     * @exception IllegalStateException   	if {@link #getInputStream} method
     * 						has been called on this request
     *
     * @exception IOException  			if an input or output exception occurred
     *
     * @see 					#getInputStream
     *
     */

    public BufferedReader getReader() throws IOException;


两个方法都注明方法只能被调用一次,由于RequestBody是流的形式读取,那么流读了一次就没有了,所以只能被调用一次。既然是因为流只能读一次的原因,那么只要将流的内容保存下来,就可以实现反复读取了。

实现方法:先将RequestBody保存为一个byte数组,然后通过Servlet自带的HttpServletRequestWrapper类覆盖getReader()和getInputStream()方法,使流从保存的byte数组读取。然后再Filter中将ServletRequest替换为ServletRequestWrapper。代码如下:

BodyReaderHttpServletRequestWrapper类包装ServletRequest,将流保存为byte[],然后将getReader()和getInputStream()方法的流的读取指向byte[]
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

import jodd.JoddDefault;
import jodd.io.StreamUtil;

public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {

	private final byte[] body;
	
	public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) 
throws IOException {
		super(request);
		body = StreamUtil.readBytes(request.getReader(), JoddDefault.encoding);
	}

	@Override
	public BufferedReader getReader() throws IOException {
		return new BufferedReader(new InputStreamReader(getInputStream()));
	}

	@Override
	public ServletInputStream getInputStream() throws IOException {
		final ByteArrayInputStream bais = new ByteArrayInputStream(body);
		return new ServletInputStream() {

			@Override
			public int read() throws IOException {
				return bais.read();
			}
		};
	}

}


在Filter中将ServletRequest替换为ServletRequestWrapper
public class HttpServletRequestReplacedFilter implements Filter {

	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		//Do nothing
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		ServletRequest requestWrapper = null;
		if(request instanceof HttpServletRequest) {
			requestWrapper = new BodyReaderHttpServletRequestWrapper((HttpServletRequest) request);
		}
		if(null == requestWrapper) {
			chain.doFilter(request, response);
		} else {
			chain.doFilter(requestWrapper, response);
		}
		
	}

	@Override
	public void destroy() {
		//Do nothing
	}

}
分享到:
评论
3 楼 maoweiwer 2017-09-01  
这代码编译能过?
new ServletInputStream() 是一个抽象类,还有几个方法也需要实现吧
2 楼 jacktao219 2016-11-02  
赞一赞~~
1 楼 cuitzyj 2012-08-15  
good,好文章

相关推荐

    ServletRequest过滤程序

    提供了ServletRequest过滤程序,重新构造对象内容,并有效规避request.getParameter()、request.getInputStream()冲突的问题,同时提供了对跨站脚本攻击XSS和SQL注入的过滤程序。

    javaWEB总结(6):ServletRequest

    javaWEB总结(6):ServletRequest测试代码

    SpringMVC——处理器方法参数的处理.docx

    2、DataBinder 首先调用Spring Web环境中的ConversionService组件,进行数据类型转换和格式化等操作,将ServletRequest中的信息填充到形参对象中; 3、DataBinder 然后调用Validator组件对已经绑定了请求消息数据的形参...

    request.setAttribute 语句前总显示红色感叹号解决办法 HTTP Status 500 -

    type Exception report message description The server encountered an internal error () that prevented it from fulfilling this ... javax.servlet.http.HttpServlet.service(HttpServlet.java:717)解决办法:

    超全面javaweb教程28天第9天 7 Servlet相关类之ServletRequest和Servletresponse对象的简单介绍

    超全面javaweb教程28天第9天_7_Servlet相关类之ServletRequest和Servletresponse对象

    java-servlet-api.doc

    当客户端发出请求时,Servlet引擎传递给Servlet一个ServletRequest对象和一个ServletResponse对象,这两个对象作为参数传递到service()方法中。 Servlet也可以执行ServletRequest接口和ServletResponse接口。...

    ServletRequest使用介绍.docx

    HttpServletRequest对象代表客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP请求中的所有信息都封装在这个对象中,开发人员通过这个对象的方法,可以获得客户这些信息;

    servletAPI中文版(TXT格式,自己翻译)

    void service(ServletRequest request, ServletResponse response)throws ServletException,IOException 处理request对象中描述的请求,使用response对象返回请求结果 String getServletInfo() 返回描述servlet的一...

    Java+javabean

    The method setAttribute String Object in the type ServletRequest is not applicable for the arguments String int

    java 面试题 总结

    封装是把过程和数据包围起来,对数据的访问只能通过已定义的界面。面向对象计算始于这个基本概念,即现实世界可以被描绘成一系列完全自治、封装的对象,这些对象通过一个受保护的接口访问其他对象。 4. 多态性: 多...

    HttpServletRequest详解.docx

    HttpServletRequest 详解 HttpServletRequest对象代表客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP请求头中的所有信息都封装在这个对象中,通过这个对象提供的方法,可以获得客户端请求的所有信息。

    超级有影响力霸气的Java面试题大全文档

    当客户机第一次调用一个Stateful Session Bean 时,容器必须立即在服务器中创建一个新的Bean实例,并关联到客户机上,以后此客户机调用Stateful Session Bean 的方法时容器会把调用分派到与此客户机相关联的Bean实例...

    jsp内置对象的用法

    6 ServletInputStream getInputStream() 得到请求体中一行的二进制流 7 String getParameter(String name) 返回name指定参数的参数值 8 Enumeration getParameterNames() 返回可用参数名的枚举 9 String[] ...

    Servlet过滤器使用

    代码简单明了,易学。 过滤器实现类 1、Filter接口:所有的Servlet过滤器类都必须实现javax.servlet.Filter接口 ... Servlet容器在销毁过滤器实例前调用该方法,这个方法中可以释放Servlet过滤器占用的资源。

    servlet temple

    每当一个客户请求一个HttpServlet 对象,该对象的service() 方法就要被调用,而且传递给这个方法一个“请求”(ServletRequest)对象和一个“响应”(ServletResponse)对象作为参数。 在 HttpServlet 中已存在 ...

    javaee-ServletRequest 类相关源代码解析

    NULL 博文链接:https://sunfish.iteye.com/blog/1485374

    郑州大学软件学院机试题-2011-2012_JSP程序设计

    一、单选题 (共30题,每题1分,共30分) 1. 下列哪个不属于监听器接口ServletContextAttributeListener提供的方法() (1分) A.public void ...如果调用该方法的过滤器是链中最后一个,那么目标资源被调用。

    基于java的企业级应用开发:数据绑定介绍.ppt

    ? ? ? ? ? ? ? * * * * 数据绑定介绍 什么是数据绑定? 13.1 数据绑定介绍 在... 2 DataBinder调用ConversionService组件进行数据类型转换、数据格式化等工作,并将ServletRequest对象中的消息填充到参数对象中; 3 数

    EL表达式的详细使用

    它是一种简单的语言,基于可用的命名空间(PageContext 属性)、嵌套属性和对集合、操作符(算术型、关系型和逻辑型)的访问符、映射到 Java 类中静态方法的可扩展函数以及一组隐式对象。 EL 提供了在 JSP 脚本编制...

    java 编写文件上传类简单易用

    这种编码的具体规则可以在 rfc2231 里查到, 通常使用的表单也是采用这种方式编码的,Servlet 的 API 提供了对这种 编码方式解码的支持,只需要调用 ServletRequest 类中的方法就可以得到 用户表单中的字段和数据。...

Global site tag (gtag.js) - Google Analytics