request.getInputStream()只能被读取一次

2024-01-31 17:12

本文主要是介绍request.getInputStream()只能被读取一次,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

需求背景:需要对每个Http请求都要记录入参和出参
在spring boot中,我们很容易就想到要用Interceptor拦截器去做拦截HttpServletRequest请求。
RequestLogInterceptor中实现preHandle()方法,然后在该方法内读取请求参数。RequestLogInterceptor.java 代码:

    /*** 在SpringMVC中的 Interceptor拦截器才用实现 HandlerInterceptor的方式,实现其中的三个方法* @param request* @param response* @param handler* @return* @throws Exception*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {long startTimeMillis = System.currentTimeMillis();//请求日志HttpRequestLog requestLog = new HttpRequestLog();// 获取并设置 RequestIDrequestLog.setRequestId(String.valueOf(HttpServletRequestUtil.getRequestID(request)));// 本次请求的 parentID = 上游 SpanIDrequestLog.setParentSpanId(HttpServletRequestUtil.getParentSpanID(request));// 当前请求的 SpanID = UpstreamSpanID + 1requestLog.setSpanId(HttpServletRequestUtil.getIncrSpanID(request));requestLog.setProto(request.getProtocol());requestLog.setMethod(request.getMethod());requestLog.setUrl(String.valueOf(request.getRequestURL()));requestLog.setApi(request.getRequestURI());requestLog.setClientIP(request.getRemoteAddr());requestLog.setClient(request.getHeader("User-Agent"));requestLog.setServer(AppConfig.systemCode);requestLog.setRequest(HttpServletRequestUtil.getRequestInfo(request));// c.Next() 之前无响应数据requestLog.setResponse(null);requestLog.setResult(RequestResult.SUCCESS);requestLog.setCreatedBy("0");requestLog.setCreatedName("");requestLog.setCreatedAt(String.valueOf(System.currentTimeMillis()));// 跟go不同,需要在request的全生命周期中保存requestLog、startTimeMillis变量RequestContextHolder.currentRequestAttributes().setAttribute("requestLog", requestLog, RequestAttributes.SCOPE_REQUEST);RequestContextHolder.currentRequestAttributes().setAttribute("startTimeMillis", startTimeMillis, RequestAttributes.SCOPE_REQUEST);return true;}/*** 从request获取请求参数或者请求参数multipart数据* @param request* @return* @throws ServletException* @throws IOException*/public static Request getRequestInfo(HttpServletRequest request) throws ServletException, IOException {Request requestLog = new Request();requestLog.setContentType(request.getContentType());requestLog.setContentLength(Long.parseLong(String.valueOf(request.getContentLength())));// request的请求头需要遍历Enumeration<String> headerNames = request.getHeaderNames();HashMap<String,String[]> headerMap = new HashMap<>(16);while (headerNames.hasMoreElements()) {String headerName = headerNames.nextElement();String headerValue = request.getHeader(headerName);headerMap.put(headerName,new String[]{headerValue});}// 获取所有的请求头requestLog.setHeader(headerMap);// 根据请求的 Content-Type 决定 Request 的取值if ("application/json".equals(request.getContentType())) {// 通过getInputStream() 获取请求体body里面的数据,因为request.getInputStream()只能被执行一次,因此做了RequestWrapper和RequestWrapperFilter去缓存servletInputStream(),这样controller层的@RequestBody就能读到body数据了ServletInputStream inputStream = request.getInputStream();BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));String line;StringBuilder body = new StringBuilder();while ((line = reader.readLine()) != null) {body.append(line);}requestLog.setBody(body.toString());}else if (request.getContentType().contains("multipart/form-data")) {requestLog.setForm(request.getParts());}else {}return requestLog;}

但是问题是:我们都知道 request.getInputStream()只能被执行一次读取请求参数,下次再执行一次就会获得null。所以要怎么才能让Controller层的@RequestBody读取到请求参数并写给inputVO呢?
用RequestWrapperFilter。
通过getInputStream() 获取请求体body里面的数据,因为request.getInputStream()只能被执行一次,因此做了RequestWrapper和RequestWrapperFilter去缓存servletInputStream(),这样controller层的@RequestBody就能读到body数据了

RequestWrapper.java 代码如下:

public class RequestWrapper extends HttpServletRequestWrapper {private final String body;public RequestWrapper(HttpServletRequest request) {super(request);StringBuilder stringBuilder = new StringBuilder();BufferedReader bufferedReader = null;ServletInputStream inputStream = null;try {inputStream = request.getInputStream();if (inputStream != null) {bufferedReader = new BufferedReader(new InputStreamReader(inputStream));char[] charBuffer = new char[128];int bytesRead = -1;while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {stringBuilder.append(charBuffer, 0, bytesRead);}} else {stringBuilder.append("");}} catch (IOException ex) {} finally {if (inputStream != null) {try {inputStream.close();}catch (IOException e) {e.printStackTrace();}}if (bufferedReader != null) {try {bufferedReader.close();}catch (IOException e) {e.printStackTrace();}}}body = stringBuilder.toString();}@Overridepublic ServletInputStream getInputStream() throws IOException {final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());ServletInputStream servletInputStream = new ServletInputStream() {@Overridepublic boolean isFinished() {return false;}@Overridepublic boolean isReady() {return false;}@Overridepublic void setReadListener(ReadListener readListener) {}@Overridepublic int read() throws IOException {return byteArrayInputStream.read();}};return servletInputStream;}@Overridepublic BufferedReader getReader() throws IOException {return new BufferedReader(new InputStreamReader(this.getInputStream()));}public String getBody() {return this.body;}
}

RequestWrapperFilter.java代码如下

@Component
@WebFilter(urlPatterns = "/**")
public class RequestWrapperFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {Filter.super.init(filterConfig);}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {ServletRequest requestWrapper = null;if(servletRequest instanceof HttpServletRequest) {requestWrapper = new RequestWrapper((HttpServletRequest) servletRequest);}if(requestWrapper == null) {//防止流读取一次就没有了,将流传递下去filterChain.doFilter(servletRequest, servletResponse);} else {filterChain.doFilter(requestWrapper, servletResponse);}}@Overridepublic void destroy() {Filter.super.destroy();}
}

这篇关于request.getInputStream()只能被读取一次的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/664482

相关文章

Spring定时任务只执行一次的原因分析与解决方案

《Spring定时任务只执行一次的原因分析与解决方案》在使用Spring的@Scheduled定时任务时,你是否遇到过任务只执行一次,后续不再触发的情况?这种情况可能由多种原因导致,如未启用调度、线程... 目录1. 问题背景2. Spring定时任务的基本用法3. 为什么定时任务只执行一次?3.1 未启用

解决Java中基于GeoTools的Shapefile读取乱码的问题

《解决Java中基于GeoTools的Shapefile读取乱码的问题》本文主要讨论了在使用Java编程语言进行地理信息数据解析时遇到的Shapefile属性信息乱码问题,以及根据不同的编码设置进行属... 目录前言1、Shapefile属性字段编码的情况:一、Shp文件常见的字符集编码1、System编码

利用Python实现添加或读取Excel公式

《利用Python实现添加或读取Excel公式》Excel公式是数据处理的核心工具,从简单的加减运算到复杂的逻辑判断,掌握基础语法是高效工作的起点,下面我们就来看看如何使用Python进行Excel公... 目录python Excel 库安装Python 在 Excel 中添加公式/函数Python 读取

Python如何实现读取csv文件时忽略文件的编码格式

《Python如何实现读取csv文件时忽略文件的编码格式》我们再日常读取csv文件的时候经常会发现csv文件的格式有多种,所以这篇文章为大家介绍了Python如何实现读取csv文件时忽略文件的编码格式... 目录1、背景介绍2、库的安装3、核心代码4、完整代码1、背景介绍我们再日常读取csv文件的时候经常

C#中读取XML文件的四种常用方法

《C#中读取XML文件的四种常用方法》Xml是Internet环境中跨平台的,依赖于内容的技术,是当前处理结构化文档信息的有力工具,下面我们就来看看C#中读取XML文件的方法都有哪些吧... 目录XML简介格式C#读取XML文件方法使用XmlDocument使用XmlTextReader/XmlTextWr

Python判断for循环最后一次的6种方法

《Python判断for循环最后一次的6种方法》在Python中,通常我们不会直接判断for循环是否正在执行最后一次迭代,因为Python的for循环是基于可迭代对象的,它不知道也不关心迭代的内部状态... 目录1.使用enuhttp://www.chinasem.cnmerate()和len()来判断for

Java读取InfluxDB数据库的方法详解

《Java读取InfluxDB数据库的方法详解》本文介绍基于Java语言,读取InfluxDB数据库的方法,包括读取InfluxDB的所有数据库,以及指定数据库中的measurement、field、... 首先,创建一个Java项目,用于撰写代码。接下来,配置所需要的依赖;这里我们就选择可用于与Infl

C#读取本地网络配置信息全攻略分享

《C#读取本地网络配置信息全攻略分享》在当今数字化时代,网络已深度融入我们生活与工作的方方面面,对于软件开发而言,掌握本地计算机的网络配置信息显得尤为关键,而在C#编程的世界里,我们又该如何巧妙地读取... 目录一、引言二、C# 读取本地网络配置信息的基础准备2.1 引入关键命名空间2.2 理解核心类与方法

SpringBoot使用Apache POI库读取Excel文件的操作详解

《SpringBoot使用ApachePOI库读取Excel文件的操作详解》在日常开发中,我们经常需要处理Excel文件中的数据,无论是从数据库导入数据、处理数据报表,还是批量生成数据,都可能会遇到... 目录项目背景依赖导入读取Excel模板的实现代码实现代码解析ExcelDemoInfoDTO 数据传输

电脑多久清理一次灰尘合? 合理清理电脑上灰尘的科普文

《电脑多久清理一次灰尘合?合理清理电脑上灰尘的科普文》聊起电脑清理灰尘这个话题,我可有不少话要说,你知道吗,电脑就像个勤劳的工人,每天不停地为我们服务,但时间一长,它也会“出汗”——也就是积累灰尘,... 灰尘的堆积几乎是所有电脑用户面临的问题。无论你的房间有多干净,或者你的电脑是否安装了灰尘过滤器,灰尘都