`
liwx2000
  • 浏览: 132222 次
  • 性别: 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,好文章

相关推荐

    springboot 解决InputStream只能读取一次的问题

    这样,我们就成功地解决了Spring Boot中`InputStream`只能读取一次的问题,使得我们可以多次读取并处理请求数据,无论是用于文件上传、消息解析还是其他需要多次读取的场景。在实际项目中,根据具体需求,可能还需要...

    完美解决request请求流只能读取一次的问题

    解决request请求流只能读取一次的问题,我们可以使用自定义的HttpServletRequestWrapper,覆写getInputStream()和getReader()方法,从而实现流的重复读取。这可以在SpringBoot项目中使用Filter拦截器对所有请求流中...

    javaWEB总结(6):ServletRequest

    `getReader()`返回一个`BufferedReader`,用于读取请求体中的文本数据。 5. **获取客户端信息**:`getRemoteAddr()`和`getRemoteHost()`分别获取客户端的IP地址和主机名,`getRemoteUser()`获取经过验证的用户名。 ...

    ServletRequest过滤程序

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

    ServletRequest使用介绍.docx

    请求转发是通过`RequestDispatcher`对象实现的,`getRequestDispatcher(String path)`获取转发器,然后调用`forward(ServletRequest request, ServletResponse response)`将请求转发给其他资源。这种方式在服务器...

    java 防盗链详解及解决办法

    java 防盗链详解及解决办法 Java 防盗链的概念 防盗链的概念是指在自己的服务器上,通过技术手段将其他网站的内容(比如一些音乐、图片、软件的下载地址)放置在自己的网站中,通过这种方法盗取其他网站的空间和...

    JSP中乱码解决办法

    本篇将详细探讨如何在JSP中解决POST方式和GET方式下产生的乱码问题,确保数据正确无误地传输与展示。 ### POST方式下的乱码解决方案 #### 1. 使用`request.setCharacterEncoding()`方法 在JSP页面中,可以通过...

    jsp和servlet操作mysql中文乱码问题的解决办法.docx

    在 JSP 和 Servlet 操作 MySQL 过程中,中文乱码问题是一个常见的问题,而解决这个问题需要从多方面入手,包括 JSP 页面、Servlet 编程和 Filter 配置等。本文将详细介绍解决 JSP 和 Servlet 操作 MySQL 中文乱码...

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

    在Java Web开发中,我们经常遇到各种运行时错误或编译错误,其中一种较为常见的问题是`request.setAttribute`方法调用时出现红色感叹号提示,并且伴随着HTTP 500错误。这种问题通常是由类型不匹配导致的,比如尝试将...

    乱码的各种解决办法

    乱码问题是多语言支持中常见的问题,解决这一问题不仅需要对服务器配置进行调整,还需要关注前端页面及后端逻辑的编码一致性。通过上述方法可以有效地解决大部分场景下的乱码问题,提高系统的稳定性和用户体验。

    JAVA Web 用过滤器和包装器消除乱码

    综上所述,通过编写`CharacterEncodingFilter`和`RequestWrapper`,我们可以有效地解决JAVA Web应用中的乱码问题。过滤器确保了请求和响应的默认编码,而包装器则提供了对特定请求的定制化处理。这样的解决方案不仅...

    javaee-ServletRequest 类相关源代码解析

    在Java EE平台中,Servlet是核心组件之一,用于处理HTTP请求和响应。ServletRequest接口是所有HTTP请求对象的父接口,它提供了访问请求参数、头信息、会话以及与请求相关的其他信息的方法。本文将深入解析...

    struts解决中文乱码问题

    在这个例子中,我们在`processPreprocess`方法中设置了请求和响应的字符集为GBK,这样在数据传输过程中就不会出现乱码问题了。 通过这两种方式,我们可以有效地解决Struts框架下中文乱码的问题。对于大型项目来说,...

    解决web开发中的中文问题.doc

    因此,解决这一问题的关键在于确保数据在传输过程中的编码一致性。 #### 二、解决中文乱码的方法 ##### 方法一:页面级别的编码设置 在JSP页面中,可以通过设置`&lt;%@ page %&gt;`指令来指定页面的编码格式。例如,...

    springMVC解决中文乱码

    下面我们将详细介绍 SpringMVC 中解决中文乱码问题的思路和方法。 配置文件中的字符编码 在 SpringMVC 中,我们可以在 web.xml 文件中配置字符编码。例如,我们可以在 web.xml 文件中添加以下配置: ```xml ...

    JSP乱码解决过滤器

    在给定的描述和标签中,"JSP乱码解决过滤器"指的就是一个专门用于处理乱码问题的Filter。 首先,我们需要了解过滤器(Filter)在Java Web中的作用。过滤器是Servlet规范的一部分,它允许我们在请求到达目标Servlet...

    Filter解决中文乱码

    Filter(过滤器)是Servlet规范的一部分,用于在请求处理前后执行一些预处理或后处理任务,解决乱码问题就是其中之一。本篇文章将详细讲解如何使用Filter来解决中文乱码问题,并提供相关源码和配置示例。 首先,...

    java web 修改request携带的参数信息

    Filter是Java Servlet API的一部分,主要用于拦截和处理Servlet请求和响应。它们在请求到达目标Servlet之前和响应离开目标Servlet之后介入,可以看作是Web应用中的“中间件”。Filter可以链式调用,多个Filter按照...

    解决J2EE开发中乱码问题

    本文将详细介绍如何解决这些问题,以一个基于Eclipse 3.3 + MyEclipse 6.0 + Tomcat 5.5的项目为例,该项目采用了Ext、Struts和Hibernate框架。 首先,乱码问题主要出现在以下几个方面: 1. Ext框架:作为流行的...

    filter过滤器流程及中文乱码解决和客户端IP地址控制

    它遵循Servlet规范中的Filter接口,允许开发者在请求到达目标Servlet或JSP之前以及响应离开Servlet之后进行拦截和处理。Filter的生命周期包括初始化、过滤请求和销毁三个阶段。 1. 初始化(init) 当应用服务器(如...

Global site tag (gtag.js) - Google Analytics