激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - spring應用中多次讀取http post方法中的流遇到的問題

spring應用中多次讀取http post方法中的流遇到的問題

2021-06-14 14:19三國夢回 Java教程

這篇文章主要介紹了spring應用中多次讀取http post方法中的流,文中給大家列舉處理問題描述及解決方法,需要的朋友可以參考下

一、問題簡述

先說下為啥有這個需求,在基于spring的web應用中,一般會在controller層獲取http方法body中的數據。

方式1:

比如http請求的content-typeapplication/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層就讀不到了。

畢竟,流只能讀一次啊。

二、怎么一個流讀多次呢

說一千道一萬,流來自哪里,來自

?
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) {
  }
}

至此。完整的偷梁換柱就結束了。

現在,請再回過頭去,看文章開頭的代碼,標紅的部分。

是不是豁然開朗了?

三、代碼地址

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

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 精品国产乱码久久久久久丨区2区 | 日韩在线观看视频免费 | 欧美一级鲁丝片免费看 | 国产又粗又爽又深的免费视频 | 又黄又爽免费无遮挡在线观看 | 精品久久9999 | 国产1区在线观看 | 国产婷婷一区二区三区 | 日韩高清影视 | 国产精品免费一区二区三区在线观看 | 99综合视频| 未成年人在线观看 | av成人免费 | 国产精品亚洲欧美 | 日本黄网 | 色综合视频网 | 高潮娇喘嗯啊~文字 | 国产精品麻豆91 | 中国黄色一级生活片 | 在线成人免费网站 | 日韩av手机在线免费观看 | 制服丝袜日日夜夜 | 国内精品久久久久久久星辰影视 | 一级黄色片在线看 | av电影网站在线观看 | 羞羞视频免费观看入口 | 91麻豆精品国产91久久久无需广告 | 亚洲欧美日韩精品久久 | 久久中文字幕在线观看 | 黄色免费高清网站 | 中文字幕线观看 | 国产精品av久久久久久网址 | 久久99精品久久久久久园产越南 | 国产精品99免费视频 | 欧美在线观看视频一区 | 特片网久久 | 欧美日日操 | 亚洲成人午夜精品 | 久久中文字幕在线观看 | av在线播放观看 | 中国嫩模一级毛片 |