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

相关文章

使用Java读取本地文件并转换为MultipartFile对象的方法

《使用Java读取本地文件并转换为MultipartFile对象的方法》在许多JavaWeb应用中,我们经常会遇到将本地文件上传至服务器或其他系统的需求,在这种场景下,MultipartFile对象非... 目录1. 基本需求2. 自定义 MultipartFile 类3. 实现代码4. 代码解析5. 自定

MySQL 数据库表操作完全指南:创建、读取、更新与删除实战

《MySQL数据库表操作完全指南:创建、读取、更新与删除实战》本文系统讲解MySQL表的增删查改(CURD)操作,涵盖创建、更新、查询、删除及插入查询结果,也是贯穿各类项目开发全流程的基础数据交互原... 目录mysql系列前言一、Create(创建)并插入数据1.1 单行数据 + 全列插入1.2 多行数据

SpringBoot多环境配置数据读取方式

《SpringBoot多环境配置数据读取方式》SpringBoot通过环境隔离机制,支持properties/yaml/yml多格式配置,结合@Value、Environment和@Configura... 目录一、多环境配置的核心思路二、3种配置文件格式详解2.1 properties格式(传统格式)1.

解决pandas无法读取csv文件数据的问题

《解决pandas无法读取csv文件数据的问题》本文讲述作者用Pandas读取CSV文件时因参数设置不当导致数据错位,通过调整delimiter和on_bad_lines参数最终解决问题,并强调正确参... 目录一、前言二、问题复现1. 问题2. 通过 on_bad_lines=‘warn’ 跳过异常数据3

Python使用openpyxl读取Excel的操作详解

《Python使用openpyxl读取Excel的操作详解》本文介绍了使用Python的openpyxl库进行Excel文件的创建、读写、数据操作、工作簿与工作表管理,包括创建工作簿、加载工作簿、操作... 目录1 概述1.1 图示1.2 安装第三方库2 工作簿 workbook2.1 创建:Workboo

Java中读取YAML文件配置信息常见问题及解决方法

《Java中读取YAML文件配置信息常见问题及解决方法》:本文主要介绍Java中读取YAML文件配置信息常见问题及解决方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要... 目录1 使用Spring Boot的@ConfigurationProperties2. 使用@Valu

SpringBoot排查和解决JSON解析错误(400 Bad Request)的方法

《SpringBoot排查和解决JSON解析错误(400BadRequest)的方法》在开发SpringBootRESTfulAPI时,客户端与服务端的数据交互通常使用JSON格式,然而,JSON... 目录问题背景1. 问题描述2. 错误分析解决方案1. 手动重新输入jsON2. 使用工具清理JSON3.

SpringBoot读取ZooKeeper(ZK)属性的方法实现

《SpringBoot读取ZooKeeper(ZK)属性的方法实现》本文主要介绍了SpringBoot读取ZooKeeper(ZK)属性的方法实现,强调使用@ConfigurationProperti... 目录1. 在配置文件中定义 ZK 属性application.propertiesapplicati

Python中文件读取操作漏洞深度解析与防护指南

《Python中文件读取操作漏洞深度解析与防护指南》在Web应用开发中,文件操作是最基础也最危险的功能之一,这篇文章将全面剖析Python环境中常见的文件读取漏洞类型,成因及防护方案,感兴趣的小伙伴可... 目录引言一、静态资源处理中的路径穿越漏洞1.1 典型漏洞场景1.2 os.path.join()的陷

如何使用 Python 读取 Excel 数据

《如何使用Python读取Excel数据》:本文主要介绍使用Python读取Excel数据的详细教程,通过pandas和openpyxl,你可以轻松读取Excel文件,并进行各种数据处理操... 目录使用 python 读取 Excel 数据的详细教程1. 安装必要的依赖2. 读取 Excel 文件3. 读