请求中多次调用getInputStream解决方案

请求中多次调用getInputStream后续获取为空的问题

原因

在处理HTTP请求时,HttpServletRequest 对象的 getInputStream 方法只能被调用一次。这是因为请求的输入流(InputStream)是一个一次性资源,一旦被读取,就不能再次读取。因此,如果你在同一个请求中多次调用 getInputStream,后续的调用将会返回一个空的输入流或者抛出异常。

解决方案

解决方案如下:

思路为利用过滤器对请求进行缓存,需要自定义类继承HttpServletRequestWrapper并实现缓存逻辑

自定义RequestWrapper类

import org.springframework.util.StreamUtils;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.support.StandardServletMultipartResolver;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

public class RequestWrapper extends HttpServletRequestWrapper {

	private final byte[] body;

	private final Map<String, MultipartFile> fileMap = new HashMap<>();

	public RequestWrapper(HttpServletRequest request) throws IOException {
		super(request);
		StandardServletMultipartResolver resolver = new StandardServletMultipartResolver();
		if (resolver.isMultipart(request)) {
			MultipartHttpServletRequest multipartHttpServletRequest = resolver.resolveMultipart(request);
			Collection<MultipartFile> files = multipartHttpServletRequest.getFiles("file");
			for (MultipartFile file : files) {
				fileMap.put(file.getName(), file);
			}
		}
		body = StreamUtils.copyToByteArray(request.getInputStream());
	}

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

	@Override
	public ServletInputStream getInputStream() throws IOException {

		final ByteArrayInputStream inputStream = new ByteArrayInputStream(body);

		return new ServletInputStream() {

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

			@Override
			public boolean isFinished() {
				return inputStream.available() == 0;
			}

			@Override
			public boolean isReady() {
				return true;
			}

			@Override
			public void setReadListener(ReadListener readListener) {
				throw new UnsupportedOperationException();
			}
		};
	}
}

自定义过滤器

import club.tgucsdn.common.pojo.RequestWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

@WebFilter(filterName = "streamFilter",urlPatterns = "/*")
@ConditionalOnClass({MybatisPlusInterceptor.class, BaseMapper.class})
public class RequestStreamFilter implements Filter {

	@Override
	public void init(javax.servlet.FilterConfig filterConfig) throws ServletException {
	}

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

	@Override
	public void destroy() {
	}
}

随后在后续需要的地方可以使用getInputStream方法,其返回值将不在为null

解释

自定义的RequestWrapper中存放两个对象

  • body

    缓存请求的原始内容

  • fileMap

    存储从多部分请求(multipart request)中解析出来的文件。具体来说,fileMap是一个Map<String, MultipartFile>,用于将文件名与对应的 MultipartFile 对象关联起来。这样可以在后续的处理中方便地访问这些文件

fileMap 属性的具体用途

  1. 存储文件
    • 当请求是多部分请求时,StandardServletMultipartResolver 会解析请求中的文件部分,并将这些文件存储在 fileMap 中。
    • fileMap 的键是文件的名称(即表单字段的名称),值是 MultipartFile 对象,表示上传的文件。
  2. 方便后续访问
    • 通过 fileMap,你可以在后续的处理逻辑中通过文件名快速访问到上传的文件。例如,你可以从 fileMap 中获取某个特定的文件,进行进一步的处理,如保存到文件系统、读取文件内容等。

body 属性的具体用途

  1. 允许多次读取请求体

    HTTP请求的输入流(InputStream)是一次性的资源,一旦被读取,就不能再次读取。通过将请求体的内容缓存到 body 数组中,可以在后续的处理中多次读取请求体的内容,而不会导致输入流耗尽。

  2. 提供一致的请求体访问

    通过缓存请求体内容,可以确保在不同的处理阶段或不同的方法调用中,请求体的内容保持一致。这对于需要多次访问请求体的场景非常有用,例如在日志记录、安全检查、业务逻辑处理等过程中。

  3. 支持 getInputStreamgetReader 方法的重写

    RequestWrapper 类重写了 getInputStreamgetReader 方法,返回基于 body 数组的新输入流和读取器。这样可以确保每次调用这些方法时都能获得完整的请求体内容,而不会因为输入流已被读取而返回空或抛出异常。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值