springmvc在拦截器操作请求体requestBody

springmvc在拦截器操作请求体requestBody

一般需要在springmvc中获取请求体中的内容.对请求体中的内容记录或操作后传给下游,springmvc项目中请求体中的内容只能获取一次,在下游无法获取,在下游获取将报如下错误(Required request body is missing: ),需要处理后在下游可获取.

一.环境

  1. 项目环境
    springboot的项目
  <dependency> 			
  	 <groupId>org.springframework.boot</groupId>
  	<artifactId>spring-boot-starter-web</artifactId>
  	<version>2.1.5.RELEASE</version> 		
 </dependency>
  1. 请求数据样例
    –data-raw中的部分是请求体RequestBody中的数据内容即POST请求传的json数据部分
curl 'http://localhost:9104/sys/loginAction/login'   
-H 'Connection: keep-alive'   
-H 'Accept: application/json, text/plain, */*'  
-H 'Content-Type: application/json'   
-H 'Origin: http://dipc.data-pivot.com:81'  
-H 'Referer: http://dipc.data-pivot.com:81/'  
-H 'Accept-Language: zh-CN,zh;q=0.9'  
--data-raw '{"userCode":"zhangsanl","userPassword":"23","platform":1}'  -v

二.POST请求

获取请求内容并操作,一般requestBody中的请求内容消费一次后无法在下游进行二次消费,此时需要在消费后重新设置,即可在Controller中的方法参数@RequestBody
SysUserReqeust userReq中获取到.

  1. springmvc中的HandlerInterceptor前置拦截器
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            String clientIp = null;
            BufferedReader br = request.getReader();
            char[] bys = new char[2048];
            int length = 0;
            String body = null;
            CharArrayWriter caw = null;
            try {
                caw = new CharArrayWriter();
                while ((length = br.read(bys,0,bys.length)) != -1){
                    caw.write(bys,0,length);
                }
                body = new String(caw.toCharArray());
            } finally {
                if(caw != null)
                    caw.close();
                if(br != null)
                    br.close();
            }
            if(request instanceof RequestFacade){
                // 1.获取RequestFacade中的字段request
                Field req = request.getClass().getDeclaredField("request");
                req.setAccessible(true);
                Request newReq = (Request)req.get(request);
                // 2.获取RequestFacade中的字段usingInputStream并设置为false
                Field usingInputStream = newReq.getClass().getDeclaredField("usingInputStream");
                usingInputStream.setAccessible(true);
                usingInputStream.set(newReq,false);
                // 3.获取RequestFacade中的字段usingReader并设置为false
                Field usingReader = newReq.getClass().getDeclaredField("usingReader");
                usingReader.setAccessible(true);
                usingReader.set(newReq,false);
                // 4.获取RequestFacade中的字段inputStream
                Field inputStream = newReq.getClass().getDeclaredField("inputStream");
                inputStream.setAccessible(true);
                CoyoteInputStream cis = (CoyoteInputStream)inputStream.get(newReq);
                // 4-1.获取RequestFacade中的字段inputStream的lib字段
                Field ib = cis.getClass().getDeclaredField("ib");
                ib.setAccessible(true);

                // 4-2.新建InputBuffer并设置ByteBuffer和request(该request是RequestFacade中request字段的coyoteRequest字段值)
                InputBuffer inputBuffer = new InputBuffer();
                inputBuffer.setByteBuffer(ByteBuffer.wrap(body.getBytes("UTF-8")));

                Field coyoteRequest = newReq.getClass().getDeclaredField("coyoteRequest");
                coyoteRequest.setAccessible(true);
                org.apache.coyote.Request cr = (org.apache.coyote.Request)coyoteRequest.get(newReq);
                inputBuffer.setRequest(cr);
                // 4-3.设置RequestFacade中的字段inputStream的lib字段为新建inputBuffer
                ib.set(cis,inputBuffer);

                request.setAttribute("loginBody",body);
                SysUserReqeust user = JSON.parseObject(body, SysUserReqeust.class);
                clientIp = user.getUserCode();
            }
            return true;
        }

  1. controller中的相关方法
    @PostMapping("login")
    public ReturnMsg login(HttpServletRequest request,
                           @RequestBody
                           @Validated({ValidateGroup.UsernameAndPwd.class}) SysUserReqeust user,
                           HttpServletResponse response) throws Exception {
        SysUser sysUser = BeanUtil.copyEvalueToT(user, SysUser.class);
        ReturnMsg returnMsg = userService.validateUser(user.getPlatform(),sysUser);
        return returnMsg;
    }


三. 写到最后:

各位看官如果支持博文请点个小爱心支持下博主,博主将在您的支持下继续编写更多的博文进行交流。如有问题也可联系博主进行交流学习,博主QQ:676621871

<think>好的,用户的问题是关于如何在Spring MVC拦截器中处理JSON数据并修改HTTP请求体。首先,我需要回忆一下Spring拦截器的相关知识。拦截器是基于HandlerInterceptor接口实现的,可以在请求处理的不同阶段执行逻辑。用户提到的问题主要是如何在拦截器中读取和修改请求体,同时避免流只能读取一次的问题。 记得之前在处理HTTP请求时,特别是使用@RequestBody注解时,请求体是通过输入流读取的,而流一旦被读取,就不能再次读取了。所以如果在拦截器中直接读取了请求体,后续Controller中的@RequestBody就会失败。这个问题需要解决,如何让拦截器读取后,还能让后续的处理正常获取数据。 根据引用[1],用户提到使用拦截器读取数据导致后续无法获取,因为流只能读一次。这里可能需要使用HttpServletRequestWrapper来包装原始的请求,使得可以多次读取请求体。HttpServletRequestWrapper允许我们缓存请求体的内容,这样在拦截器中读取之后,后续的处理仍然可以获取到数据。 步骤大概是这样的:首先,在拦截器的preHandle方法中,将原始的HttpServletRequest替换为自定义的包装类。这个包装类会在第一次读取输入流时,将内容缓存起来,后续的读取操作都从这个缓存中获取,而不是直接从流中读取。这样,无论拦截器还是Controller中的@RequestBody,都能正常获取到请求体的数据。 然后,处理JSON数据。在拦截器中,读取到请求体后,可以将其转换为JSON对象,进行修改或者其他处理。修改完成后,再将修改后的JSON字符串重新写入到请求体中。这里需要注意的是,修改后的数据需要重新设置到包装类的缓存中,并且要更新内容的长度,否则可能会引发错误。 另外,引用[3]提到使用@WebFilter配置过滤器,但这里用户的问题是关于拦截器Interceptor,而不是Filter。拦截器过滤器虽然类似,但属于不同的机制。拦截器属于Spring MVC的一部分,而过滤器是Servlet规范的一部分。所以解决方案应该基于拦截器,而不是过滤器。 引用[4]提到Spring Boot中的拦截器配置,需要实现HandlerInterceptor接口,并注册到拦截器注册表中。所以用户需要创建一个自定义拦截器,并覆盖preHandle方法,在其中处理请求体的读取和修改。 具体实现步骤可能包括: 1. 创建自定义的HttpServletRequestWrapper,重写getInputStream和getReader方法,允许多次读取请求体。 2. 在拦截器的preHandle方法中,将原始请求替换为自定义的包装请求。 3. 读取请求体内容,解析为JSON对象。 4. 修改JSON数据。 5. 将修改后的JSON数据重新写入包装类的缓存中。 6. 确保后续的Controller可以正确获取修改后的请求体。 需要注意的是,请求体的内容长度可能会因为修改而改变,因此在包装类中需要更新对应的头信息,如Content-Length,否则可能会导致客户端或服务器端处理异常。 此外,可能还需要处理字符编码的问题,确保读取和写入的字符集一致,避免乱码。 最后,要验证解决方案的有效性,编写测试用例,确保拦截器能够正确修改请求体,并且Controller能够接收到修改后的数据。</think>在拦截器中处理JSON请求体需要解决流只能读取一次的问题,可通过自定义`HttpServletRequestWrapper`实现多次读取。以下是具体实现步骤: ### 一、创建可重复读取的Request包装类 ```java public class BodyCachingRequestWrapper extends HttpServletRequestWrapper { private final byte[] body; public BodyCachingRequestWrapper(HttpServletRequest request) throws IOException { super(request); InputStream inputStream = request.getInputStream(); this.body = StreamUtils.copyToByteArray(inputStream); } @Override public ServletInputStream getInputStream() { return new CachedBodyServletInputStream(this.body); } @Override public BufferedReader getReader() { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(this.body); return new BufferedReader(new InputStreamReader(byteArrayInputStream)); } } // 自定义ServletInputStream实现 class CachedBodyServletInputStream extends ServletInputStream { private final InputStream cachedBodyInputStream; public CachedBodyServletInputStream(byte[] cachedBody) { this.cachedBodyInputStream = new ByteArrayInputStream(cachedBody); } @Override public int read() throws IOException { return cachedBodyInputStream.read(); } @Override public boolean isFinished() { try { return cachedBodyInputStream.available() == 0; } catch (IOException e) { throw new RuntimeException(e); } } @Override public boolean isReady() { return true; } @Override public void setReadListener(ReadListener listener) { throw new UnsupportedOperationException(); } } ``` ### 二、实现拦截器逻辑 ```java public class JsonInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { BodyCachingRequestWrapper wrappedRequest = new BodyCachingRequestWrapper(request); // 1. 获取JSON数据 String bodyStr = new String(wrappedRequest.getBody(), request.getCharacterEncoding()); JSONObject json = JSON.parseObject(bodyStr); // 2. 修改JSON内容 json.put("modifiedTime", LocalDateTime.now()); // 3. 重新写入请求体 wrappedRequest.setBody(json.toJSONString().getBytes()); // 替换原始Request对象 request = wrappedRequest; return true; } } ``` ### 三、注册拦截器 ```java @Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new JsonInterceptor()) .addPathPatterns("/api/**"); } } ``` ### 关键点说明 1. **流复用原理**:通过缓存请求体字节数组实现多次读取[^1] 2. **JSON处理**:使用Fastjson/Alibaba JSON等工具解析修改数据 3. **字符编码**:必须使用`request.getCharacterEncoding()`保持一致 4. **内容长度**:修改后需要重置`Content-Length`头信息 ### 注意事项 1. 拦截器路径需精确匹配需要处理JSON的接口 2. 修改后的JSON字段类型需与接收对象匹配 3. 大文件请求需要考虑内存优化
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值