当前位置: 首页 > news >正文

解决HttpServletRequest无法获取@RequestBody修饰的参数

在使用springboot设计controller时我们通常会在某个请求如post中使用@RequestBody来修饰参数如:

在一些特殊场景下,我们需要在service层的代码去拿到当前上下文请求(HttpServletRequest)中的一些信息如请求体,这个时候被@RequestBody所修饰的请求是无法获取的,原因如下:
1、请求体流只能读取一次:Servlet 规范中,HttpServletRequest 的输入流 (getInputStream()) 是单向的,一旦被 @RequestBody 读取后,流就会关闭,无法再次读取。
2、@RequestBody 优先处理:Spring MVC 在处理控制器方法时,会先解析 @RequestBody,导致后续通过 HttpServletRequest 获取请求体时为空。

下面简单演示下解决方案:

一、先编写一个http工具类用来读取ServletRequest的内容

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.List;/*** http工具类*/
@Slf4j
public class HttpUtils {/*** 从request获取body的数据** @param request 请求* @return body数据字符串*/public static String getBodyStr(ServletRequest request) {StringBuilder sb = new StringBuilder();try {try (ServletInputStream inputStream = request.getInputStream()) {try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {String line;while ((line = reader.readLine()) != null) {sb.append(line);}}}} catch (Exception e) {log.error("get request body error: ", e);}return sb.toString();}/*** 从request获取body的数据,并转换成对象(适用于使用@RequestBody修饰的参数)** @param request request 请求* @param clazz   对象类型* @return 对象*/public static <T> T getBodyToObject(ServletRequest request, Class<T> clazz) {String bodyStr = getBodyStr(request);if (StringUtils.isBlank(bodyStr)) {return null;}return JSON.parseObject(bodyStr, clazz);}/*** 从request获取body的数据,并转换成集合对象(适用于使用@RequestBody修饰的参数)** @param request request 请求* @param clazz   集合对象类型* @return 集合对象*/public static <T> List<T> getBodyToList(ServletRequest request, Class<T> clazz) {String bodyStr = getBodyStr(request);if (StringUtils.isBlank(bodyStr)) {return null;}return JSON.parseArray(bodyStr, clazz);}
}

二、添加请求体复制包装器

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;/*** 请求体复制包装器*/
public class BodyCopyWrapper extends HttpServletRequestWrapper {private byte[] requestBody;public BodyCopyWrapper(HttpServletRequest request) throws IOException {super(request);requestBody = HttpUtils.getBodyStr(request).getBytes(StandardCharsets.UTF_8);}@Overridepublic BufferedReader getReader() throws IOException {return new BufferedReader(new InputStreamReader(getInputStream()));}@Overridepublic ServletInputStream getInputStream() throws IOException {final ByteArrayInputStream basis = new ByteArrayInputStream(requestBody);return new ServletInputStream() {@Overridepublic int read() throws IOException {return basis.read();}@Overridepublic boolean isFinished() {return false;}@Overridepublic boolean isReady() {return false;}@Overridepublic void setReadListener(ReadListener readListener) {}};}public void setInputStream(byte[] body) {this.requestBody = body;}
}

三、添加请求拦截器配置类

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;/*** 请求拦截器配置类*/
@Slf4j
@Configuration
public class FilterConfig {@Beanpublic FilterRegistrationBean<RequestWrapperFilter> requestWrapperFilter() {FilterRegistrationBean<RequestWrapperFilter> bean = new FilterRegistrationBean<>();bean.setFilter(new RequestWrapperFilter());bean.addUrlPatterns("/*");bean.setOrder(Ordered.HIGHEST_PRECEDENCE);return bean;}public static class RequestWrapperFilter implements Filter {@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {ServletRequest requestWrapper = null;if (request instanceof HttpServletRequest) {requestWrapper = new BodyCopyWrapper((HttpServletRequest) request);}if (null == requestWrapper) {log.warn("未进行request包装返回原来的request");chain.doFilter(request, response);} else {chain.doFilter(requestWrapper, response);}}}
}

四、业务代码中使用方式

HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
//转对象
UserLoginDTO userLoginDTO = HttpUtils.getBodyToObject(request, UserLoginDTO.class);
//直接获取内容字符串
String bodyStr = HttpUtils.getBodyStr(request);

http://www.dtcms.com/a/266570.html

相关文章:

  • Java并发性能优化|读写锁与互斥锁解析
  • Python 中的可迭代对象与迭代器:原理与项目实战
  • 【Verilog】parameter、localparam和 `define的区别
  • Android View的绘制原理详解
  • 基于虚拟化技术的网闸安全交换:物理隔离时代的智能数据流通引擎
  • 最快实现的前端灰度方案
  • python打卡day58@浙大疏锦行
  • 算法19天|回溯算法:理论基础、组合、组合总和Ⅲ、电话号码的字母组合
  • 用原生 JS + Vue 实现一套可复用的前端错误监控系统
  • Python 机器学习核心入门与实战进阶 Day 2 - KNN(K-近邻算法)分类实战与调参
  • 【MATLAB代码】AOA与TDOA混合定位例程,适用于三维环境、4个锚点的情况,订阅专栏后可以获得完整代码
  • 计算机网络笔记(不全)
  • Windows 本地安装部署 Apache Druid
  • 无人机载重模块技术要点分析
  • Science Robotics发表 | 20m/s自主飞行+避开2.5mm电线的微型无人机!
  • CSS长度单位问题
  • 通过Claude 生成图片的prompt集锦(一)
  • 7.4项目一问题准备
  • 实验五-Flask的简易登录系统
  • 数据结构 之 【堆】(堆的概念及结构、大根堆的实现、向上调整法、向下调整法)(C语言实现)
  • K8s服务发布基础
  • CI/CD持续集成与持续部署
  • 基于大模型的强直性脊柱炎全周期预测与诊疗方案研究
  • 力扣面试150(15/150)
  • 7.4 arm作业
  • 玩转n8n工作流教程(一):Windows系统本地部署n8n自动化工作流(n8n中文汉化)
  • 全平台兼容+3倍加载提速:GISBox将重新定义三维可视化标准
  • Java 实现excel大批量导出
  • 什么是金字塔思维?
  • 三体融合实战:Django+讯飞星火+Colossal-AI的企业级AI系统架构