最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

 更新時間:2018年11月14日 08:34:02   作者:三國夢回  
這篇文章主要介紹了spring應用中多次讀取http post方法中的流,文中給大家列舉處理問題描述及解決方法,需要的朋友可以參考下

一、問題簡述

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

方式1:

比如http請求的content-typeapplication/json的情況下,直接用@RequestBody接收。

方式2:

也有像目前我們在做的這個項目,比較原始,是直接手動讀取流。(不要問我為啥這么原始,第一版也不是我寫的。)

@RequestMapping("/XXX.do")
  public void XXX(HttpServletRequest request, HttpServletResponse response) throws IOException {
    JSONObject jsonObject = WebUtils.getParameters(request);
     //業(yè)務處理
    ResponseUtil.setResponse(response, MessageFactory.createSuccessMsg());
  }

WebUtils.getParameters如下:

  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層的方法進行日志記錄,記錄方法參數(shù),應該怎么辦呢。

如果是采用了方式1的話,簡單。spring已經(jīng)幫我們把參數(shù)從流里取出來,給我們提供好了,我們拿著打印一下日志即可。

如果是比較悲劇地采用了我們這種方式,參數(shù)里只有個httpServletRequest,那就只有自己去讀取流了,然而,在aop中我們把流讀了的話,

在controller層就讀不到了。

畢竟,流只能讀一次啊。

二、怎么一個流讀多次呢

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

javax.servlet.ServletRequest#getInputStream

所以,我們的思路,是不是可以這樣,定義一個filter,在filter中將request替換為我們自定義的request。

下面標紅的為自定義的request。

/**
 *
 */
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層繼續(xù)讀取該流
 *
 * 該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中,構(gòu)造函數(shù)中,先把原始流中的數(shù)據(jù)讀出來,放到ByteArrayOutputStream cachedBytes中。

并且需要重新定義getInputStream方法。

以后每次程序中調(diào)用getInputStream方法時,都會從我們的偷梁換柱的request中的cachedBytes字段,new一個InputStream出來。

看上圖紅色部分:

getInputStream我們返回了自定義的CachedServletInputStream類。

那么,接下來是CachedServletInputStream:

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

至此。完整的偷梁換柱就結(jié)束了。

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

是不是豁然開朗了?

三、代碼地址

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

直接git 下載即可。

這是個單獨的工程,直接eclipse或者idea導入即可。

運行方法:

我這邊講下idea:

直接運行jetty:run這個goal即可。

然后訪問testPost.do即可(下面把curl貼出來,可以自己在接口測試工具里拼裝):

curl -i -X POST \
-H "Content-Type:application/json" \
-d \
'{"id":"32"}
' \
'http://localhost:8080/springmvc-multiread-post/testPost.do'

我這邊演示下效果,可以發(fā)現(xiàn),兩次都讀出來了:

總結(jié)

以上所述是小編給大家介紹的spring應用中多次讀取http post方法中的流,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!

相關文章

  • Java中Class類的作用與深入理解

    Java中Class類的作用與深入理解

    這篇文章主要介紹了Java中Class類的作用與深入理解的相關資料,希望通過本文能幫助到大家讓大家理解這部分內(nèi)容,需要的朋友可以參考下
    2017-10-10
  • Mybatis模糊查詢之三種定義參數(shù)方法和聚合查詢、主鍵回填實現(xiàn)方法

    Mybatis模糊查詢之三種定義參數(shù)方法和聚合查詢、主鍵回填實現(xiàn)方法

    這篇文章主要介紹了Mybatis模糊查詢之三種定義參數(shù)方法和聚合查詢、主鍵回填實現(xiàn)方法,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-03-03
  • 現(xiàn)代高效的java構(gòu)建工具gradle的快速入門

    現(xiàn)代高效的java構(gòu)建工具gradle的快速入門

    和Maven一樣,Gradle只是提供了構(gòu)建項目的一個框架,真正起作用的是Plugin,本文主要介紹了gradle入門,文中通過示例代碼介紹的非常詳細,感興趣的小伙伴們可以參考一下
    2021-11-11
  • java中的GC收集器詳情

    java中的GC收集器詳情

    這篇文章主要介紹了java中的GC收集器,GC(Garbage collection )指的是程序內(nèi)存管理分手動和自動,手動內(nèi)存管理,需要我們編程的時候顯式分配和釋放空間,但如果忘記釋放,會造成嚴重的內(nèi)存泄漏問題,下面文章內(nèi)容我們就來實例說明情況,需要的朋友可以參考一下
    2021-10-10
  • Netty粘包問題的常見解決方案

    Netty粘包問題的常見解決方案

    粘包和拆包問題也叫做粘包和半包問題,它是指在數(shù)據(jù)傳輸時,接收方未能正常讀取到一條完整數(shù)據(jù)的情況(只讀取了部分數(shù)據(jù),或多讀取到了另一條數(shù)據(jù)的情況)就叫做粘包或拆包問題,本文介紹了Netty如何解決粘包問題,需要的朋友可以參考下
    2024-06-06
  • Spring AOP注解案例及基本原理詳解

    Spring AOP注解案例及基本原理詳解

    這篇文章主要介紹了Spring AOP注解案例及基本原理詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-06-06
  • HttpClient的DnsResolver自定義DNS解析另一種選擇深入研究

    HttpClient的DnsResolver自定義DNS解析另一種選擇深入研究

    這篇文章主要為大家介紹了HttpClient的DnsResolver自定義DNS解析另一種選擇深入研究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-10-10
  • SpringBoot開發(fā)技巧之如何處理跨域請求CORS

    SpringBoot開發(fā)技巧之如何處理跨域請求CORS

    CORS(Cross-Origin Resource Sharing)"跨域資源共享",是一個W3C標準,它允許瀏覽器向跨域服務器發(fā)送Ajax請求,打破了Ajax只能訪問本站內(nèi)的資源限制
    2021-10-10
  • 淺談mybatis中的#和$的區(qū)別

    淺談mybatis中的#和$的區(qū)別

    下面小編就為大家?guī)硪黄獪\談mybatis中的#和$的區(qū)別。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-06-06
  • Spring實戰(zhàn)之Bean的作用域singleton和prototype用法分析

    Spring實戰(zhàn)之Bean的作用域singleton和prototype用法分析

    這篇文章主要介紹了Spring實戰(zhàn)之Bean的作用域singleton和prototype用法,結(jié)合實例形式分析了Bean的作用域singleton和prototype相關使用方法及操作注意事項,需要的朋友可以參考下
    2019-11-11

最新評論

仙游县| 友谊县| 独山县| 资兴市| 巢湖市| 莱西市| 南涧| 黄山市| 当阳市| 金塔县| 理塘县| 兰溪市| 北川| 塔河县| 中江县| 廊坊市| 遂溪县| 华宁县| 宜丰县| 区。| 敖汉旗| 松溪县| 凯里市| 玉田县| 青冈县| 新田县| 平南县| 海淀区| 娄烦县| 昂仁县| 柞水县| 定兴县| 武功县| 洛扎县| 微博| 苏尼特右旗| 灵寿县| 浮梁县| 曲水县| 伊通| 赤城县|