gpt4 book ai didi

spring应用中多次读取http post方法中的流遇到的问题

转载 作者:qq735679552 更新时间:2022-09-28 22:32:09 26 4
gpt4 key购买 nike

CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.

这篇CFSDN的博客文章spring应用中多次读取http post方法中的流遇到的问题由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.

1、问题简述 。

先说下为啥有这个需求,在基于spring的web应用中,一般会在controller层获取http方法body中的数据.

方式1:

比如http请求的content-type为application/json的情况下,直接用@requestbody接收.

方式2:

也有像目前我们在做的这个项目,比较原始,是直接手动读取流。(不要问我为啥这么原始,第一版也不是我写的。) 。

?
1
2
3
4
5
6
@requestmapping ( "/xxx.do" )
   public void xxx(httpservletrequest request, httpservletresponse response) throws ioexception {
     jsonobject jsonobject = webutils.getparameters(request);
     //业务处理
     responseutil.setresponse(response, messagefactory.createsuccessmsg());
   }

webutils.getparameters如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static jsonobject getparameters(httpservletrequest request) throws ioexception {
   inputstream is = null ;
   is = new bufferedinputstream(request.getinputstream(), buffer_size);
   int contentlength = integer.valueof(request.getheader( "content-length" ));
   byte [] bytes = new byte [contentlength];
   int readcount = 0 ;
   while (readcount < contentlength) {
     readcount += is.read(bytes, readcount, contentlength - readcount);
   }
   string requestjson = new string(bytes, appconstants.utf8);
   if (stringutils.isblank(requestjson)) {
     return new jsonobject();
   }
   jsonobject jsonobj = jsonutils.tojsonobject(requestjson);
   return jsonobj;
}

当然,不管怎么说,都是对流进行读取.

问题是,假如我想在controller前面加一层aop,aop里面对进入controller层的方法进行日志记录,记录方法参数,应该怎么办呢.

如果是采用了方式1的话,简单。spring已经帮我们把参数从流里取出来,给我们提供好了,我们拿着打印一下日志即可.

如果是比较悲剧地采用了我们这种方式,参数里只有个httpservletrequest,那就只有自己去读取流了,然而,在aop中我们把流读了的话, 。

在controller层就读不到了.

毕竟,流只能读一次啊.

2、怎么一个流读多次呢 。

说一千道一万,流来自哪里,来自 。

?
1
javax.servlet.servletrequest#getinputstream

所以,我们的思路,是不是可以这样,定义一个filter,在filter中将request替换为我们自定义的request.

下面标红的为自定义的request.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
/**
  *
  */
package com.ckl.filter;
import com.ckl.utils.basewebutils;
import com.ckl.utils.multireadhttpservletrequest;
import org.slf4j.logger;
import org.slf4j.loggerfactory;
import org.springframework.core.annotation.order;
import org.springframework.http.httpmethod;
import org.springframework.http.mediatype;
import javax.servlet.*;
import javax.servlet.annotation.webfilter;
import javax.servlet.http.httpservletrequest;
import java.io.ioexception;
/**
  * web流多次读写过滤器
  *
  * 拦截所有请求,主要是针对第三方提交过来的请求.
  * 为什么要做成可多次读写的流,因为可以在aop层打印日志。
  * 但是不影响controller层继续读取该流
  *
  * 该filter的原理:https://stackoverflow.com/questions/10210645/http-servlet-request-lose-params-from-post-body-after-read-it-once/17129256#17129256
  * @author ckl
  */
@order ( 1 )
@webfilter (filtername = "cacherequestfilter" , urlpatterns = "*.do" )
public class cacherequestfilter implements filter {
   private static final logger logger = loggerfactory.getlogger(cacherequestfilter. class );
   @override
   public void init(filterconfig filterconfig) throws servletexception {
     // todo auto-generated method stub
   }
   @override
   public void dofilter(servletrequest request, servletresponse response,
              filterchain chain) throws ioexception, servletexception {
     httpservletrequest httpservletrequest = (httpservletrequest) request;
     logger.info( "request uri:{}" ,httpservletrequest.getrequesturi());
     if (basewebutils.isformpost(httpservletrequest)){
       httpservletrequest = new multireadhttpservletrequest(httpservletrequest);
       string parameters = basewebutils.getparameters(httpservletrequest);
       logger.info( "cacherequestfilter receive post req. body is {}" , parameters);
     } else if (ispost(httpservletrequest)){
       //文件上传请求,没必要缓存请求
       if (request.getcontenttype().contains(mediatype.multipart_form_data_value)){
       } else {
         httpservletrequest = new multireadhttpservletrequest(httpservletrequest);
         string parameters = basewebutils.getparameters(httpservletrequest);
         logger.info( "cacherequestfilter receive post req. body is {}" , parameters);
       }
     }
     chain.dofilter(httpservletrequest, response);
   }
   @override
   public void destroy() {
     // todo auto-generated method stub
   }
   public static boolean ispost(httpservletrequest request) {
     return httpmethod.post.matches(request.getmethod());
   }
}
multireadhttpservletrequest.java:
import org.apache.commons.io.ioutils;
import javax.servlet.servletinputstream;
import javax.servlet.http.httpservletrequest;
import javax.servlet.http.httpservletrequestwrapper;
import java.io.bufferedreader;
import java.io.bytearrayoutputstream;
import java.io.ioexception;
import java.io.inputstreamreader;
/**
  * desc:
  * https://stackoverflow.com/questions/10210645/http-servlet-request-lose-params-from-post-body-after-read-it-once/17129256#17129256
  * @author : ckl
  * creat_date: 2018/8/2 0002
  * creat_time: 13:46
  **/
public class multireadhttpservletrequest extends httpservletrequestwrapper {
   private bytearrayoutputstream cachedbytes;
   public multireadhttpservletrequest(httpservletrequest request) {
     super (request);
     cachedbytes = new bytearrayoutputstream();
     servletinputstream inputstream = null ;
     try {
       inputstream = super .getinputstream();
       ioutils.copy(inputstream, cachedbytes);
     } catch (ioexception e) {
       e.printstacktrace();
     }
   }
   @override
   public servletinputstream getinputstream() throws ioexception {
     return new cachedservletinputstream(cachedbytes);
   }
   @override
   public bufferedreader getreader() throws ioexception {
     return new bufferedreader( new inputstreamreader(getinputstream()));
   }
}

在自定义的request中,构造函数中,先把原始流中的数据读出来,放到bytearrayoutputstream cachedbytes中.

并且需要重新定义getinputstream方法.

以后每次程序中调用getinputstream方法时,都会从我们的偷梁换柱的request中的cachedbytes字段,new一个inputstream出来.

看上图红色部分:

getinputstream我们返回了自定义的cachedservletinputstream类.

那么,接下来是cachedservletinputstream:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.ceiec.webservice.utils;
import javax.servlet.readlistener;
import javax.servlet.servletinputstream;
import java.io.bytearrayinputstream;
import java.io.bytearrayoutputstream;
import java.io.ioexception;
/**
  * an inputstream which reads the cached request body
  */
public class cachedservletinputstream extends servletinputstream {
   private bytearrayinputstream input;
   public cachedservletinputstream(bytearrayoutputstream cachedbytes) {
     // create a new input stream from the cached request body
     byte [] bytes = cachedbytes.tobytearray();
     input = new bytearrayinputstream(bytes);
   }
   @override
   public int read() throws ioexception {
     return input.read();
   }
   @override
   public boolean isfinished() {
     return false ;
   }
   @override
   public boolean isready() {
     return false ;
   }
   @override
   public void setreadlistener(readlistener readlistener) {
   }
}

至此。完整的偷梁换柱就结束了.

现在,请再回过头去,看文章开头的代码,标红的部分.

是不是豁然开朗了?

3、代码地址 。

https://github.com/cctvckl/work_util/tree/master/spring-mvc-multiread-post 。

直接git 下载即可.

这是个单独的工程,直接eclipse或者idea导入即可.

spring应用中多次读取http post方法中的流遇到的问题

运行方法:

spring应用中多次读取http post方法中的流遇到的问题

我这边讲下idea:

直接运行jetty:run这个goal即可.

然后访问testpost.do即可(下面把curl贴出来,可以自己在接口测试工具里拼装):

?
1
2
3
4
5
6
curl -i -x post \
-h "content-type:application/json" \
-d \
'{ "id" : "32" }
' \
'http://localhost:8080/springmvc-multiread-post/testpost.do'

我这边演示下效果,可以发现,两次都读出来了:

spring应用中多次读取http post方法中的流遇到的问题

总结 。

以上所述是小编给大家介绍的spring应用中多次读取http post方法中的流,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我网站的支持! 。

原文链接:http://www.cnblogs.com/grey-wolf/p/9953661.html 。

最后此篇关于spring应用中多次读取http post方法中的流遇到的问题的文章就讲到这里了,如果你想了解更多关于spring应用中多次读取http post方法中的流遇到的问题的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com