1. 程式人生 > >如何實現多次讀取request裡面的引數值

如何實現多次讀取request裡面的引數值

首先需要明確如下幾個概念:

1:web開發的時候,過濾器屬於java原生元件,而攔截器屬於spring框架的元件,從它們的引數就可以看出來,過濾器引數為ServletRequest, 而攔截器為HttpServeletRequest,因為spring本來就是web開發針對的就是http協議,而java則是針對所有網路通訊不單單是http協議。

2:需要了解一下ServletRequest  HttpServletRequest 之間的聯絡和區別

3:tomcat處理http請求的。Tomcat將請求轉換成了RequestFacade傳給過濾器,而RequestFacade實現了HttpServlestRequest介面。至於如何轉換可以看(

http://blog.csdn.net/aesop_wubo/article/details/7630440

4大部分IO輸入流是無法重複讀取的,只能讀取一次。再讀取時,會丟擲IO異常。無論是get請求還是post請求,在後端讀取一次之後,便無法再次讀取為了解決這個問題,我們需要包裝HttpServletRequest物件,快取body裡面的資料,再次讀取的時候從快取裡面讀取。

5: 我們可以定義自己的HttpServletRequest實現類來達到快取的目的。

public class MAPIHttpServletRequestWrapper extends HttpServletRequestWrapper {
    private static final Logger logger = LoggerFactory.getLogger(MAPIHttpServletRequestWrapper.class);

    private Map<String, String[]> parameterMap; // get方法 
    private byte[] requestBody = null;          // post 方法body資料

    public MAPIHttpServletRequestWrapper(HttpServletRequest request) {
        super(request);
        //快取請求body
        try {
            parameterMap = request.getParameterMap();
            requestBody = StreamUtils.copyToByteArray(request.getInputStream());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 獲取所有引數名, get相關方法重寫
     *
     * @return 返回所有引數名
     */
    @Override
    public Enumeration<String> getParameterNames() {
        Vector<String> vector = new Vector<>(parameterMap.keySet());
        return vector.elements();
    }

    /**
     * 獲取指定引數名的值,如果有重複的引數名,則返回第一個的值 接收一般變數 ,如text型別
     *
     * @param name
     *            指定引數名
     * @return 指定引數名的值
     */
    @Override
    public String getParameter(String name) {
        String[] results = parameterMap.get(name);
        if (results == null || results.length <= 0)
            return null;
        else {
            System.out.println("modify before:" + results[0]);
            return modify(results[0]);
        }
    }

    /**
     * 獲取指定引數名的所有值的陣列,如:checkbox的所有資料
     * 接收陣列變數 ,如checkobx型別
     */
    @Override
    public String[] getParameterValues(String name) {
        String[] results = parameterMap.get(name);
        if (results == null || results.length <= 0)
            return null;
        else {
            int length = results.length;
            for (int i = 0; i < length; i++) {
                results[i] = modify(results[i]);
                logger.info("modify before,{}:{}",name, results[i]);
            }
            return results;
        }
    }

    /**
     * 自定義的一個簡單修改原引數的方法,即:給原來的引數值前面添加了一個修改標誌的字串
     *
     * @param string
     *            原引數值
     * @return 修改之後的值 ,這裡並不進行改變
     */
    private String modify(String string) {
        return string;
    }


    /**
     * 重寫 getReader()
     */
    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }

    /**
     * 重寫 getInputStream()
     */
    @Override
    public ServletInputStream getInputStream() throws IOException {
        logger.info("modify before post");
        if(requestBody == null){
            requestBody= new byte[0];
        }
        final ByteArrayInputStream bais = new ByteArrayInputStream(requestBody);
        MyServletInputStream myServletInputStream = new MyServletInputStream(bais);
        return myServletInputStream;
    }

    // 定義自己的ServletInputStream
    class MyServletInputStream extends ServletInputStream{
        private InputStream inputStream;

        public MyServletInputStream(InputStream inputStream){
            super();
            this.inputStream = inputStream;
        }

        @Override
        public boolean isFinished() {
            return false;
        }

        @Override
        public boolean isReady() {
            return false;
        }

        @Override
        public void setReadListener(ReadListener readListener) {
            return;
        }

        @Override
        public int read() throws IOException {
            return inputStream.read();
        }
    }
}

6:修改過濾器 dofilter方法

		if(request instanceof  HttpServletRequest && response instanceof HttpServletResponse){
			request = new MAPIHttpServletRequestWrapper((HttpServletRequest) request);
			response = new MAPIHttpServletResponseWrapper((HttpServletResponse)response);
		}

將tomcat傳過來的httprequest實現類RequestFacade轉換成我們自己寫的類即可,這樣就可以實現多次讀取request裡面的引數值。

總結:其實除了自己可以實現之外也可以使用spring官方提供的元件ContentCachingRequestWrapper,其原理和上面講的是一樣的,只是具體實現細節上有少許不同。

參考資料:

相關推薦

如何實現讀取request裡面數值

首先需要明確如下幾個概念:1:web開發的時候,過濾器屬於java原生元件,而攔截器屬於spring框架的元件,從它們的引數就可以看出來,過濾器引數為ServletRequest, 而攔截器為HttpServeletRequest,因為spring本來就是web開發針對的就是

實現不完全恢復(RAC環境需要將另一節點關閉)

chang hang startup 節點 lec star med immediate 關閉 #查看以resetlogs打開的歷史SQL> SELECT a.INCARNATION#,a.RESETLOGS_CHANGE#,to_char(a.RESETLOGS_T

用面向物件重寫thread 實現呼叫一個執行緒

思路:   利用thread類中,run方法在子執行緒中呼叫,其他方法在主執行緒呼叫,所以將生產者寫入主執行緒,將消費者寫入run函式中在子執行緒中執行,完成生產者消費者模型 注意:   1.  要在 init 函式中例項化一個Queue佇列作為生產者消費者中介   2.  要在 init 函式中把d

用面向對象重寫thread 實現調用一個線程

reading img 面向對象 run方法 消費者 任務 調用 生產者 true 思路:   利用thread類中,run方法在子線程中調用,其他方法在主線程調用,所以將生產者寫入主線程,將消費者寫入run函數中在子線程中執行,完成生產者消費者模型 註意:   1.  要

Android開發之實現點選事件

 使用Google提供的api中採用的演算法 能夠實現n次點選事件,我們需要定義一個n長度的陣列,每點選一次將數組裡的內容按序號整體向左移動一格,然後給n-1出即陣列的最後添加當前的時間,如果0個位置的時間大於當前時間減去1000毫秒的話,那麼證明在1000毫秒內點選了n次。實現如

java在OJ上實現輸入

import java.util.Scanner; public class Main { public static void main(String args[]) { int a,b; Scanner reader=new Scann

《程式設計珠璣》程式碼之路13:陣列如何線上性時間內實現區間修改

給一個數組,每次對某個區間增加某個值,如何線上性時間內完成。 比如一個數組,剛開始都是0吧,如下表,第一行是下標: 0 1 2 3 4 5 6 7 0 0

關於在Spring過濾器中修改request數值遇到的問題(三)

繼上一遍為背景 問題描述: 成功解密了reqeust中的引數後,在控制層Controller中獲取到的引數,是沒有解密的引數。例如:在過濾器中修改 引數名為username 的引數值432895328195783915781(一串加密的密文) 為 xiaomin(解密後的

關於在Spring過濾器中修改request數值遇到的問題(二)

背景 見上一篇Blog關於在Spring過濾器中修改request的引數值遇到的問題(一) 問題描述 Spring框架,使用過濾器解密request中的引數(修改Request中的值),並傳遞到下一個過濾器 錯誤詳情 第一次的修改方式是這樣的(RSAUtil是封

關於在Spring過濾器中修改request數值遇到的問題(一)

關於在Spring過濾器中修改request的引數值遇到的問題 背景 專案上要對前臺傳輸到後臺的資料進行加密(二級等保什麼的),於是想到前臺使用JS進行RSA加密,後臺解密。於是在Spring中新增過濾器,來解密request中傳過來的引數值。 問題描述 前臺登入表

實現點選只觸發一點選事件

$('.sumbit1').click(function () { $('.sumbit1').unbind();//在點選事件裡移除觸發事件 var pad = $('input:radio[name=info]:checked').val(); i

spring應用中讀取http post方法中的流(附原始碼)

一、問題簡述 先說下為啥有這個需求,在基於spring的web應用中,一般會在controller層獲取http方法body中的資料。 方式1: 比如http請求的content-type為application/json的情況下,直接用@RequestBody接收。 方式2: 也有像目前我們在做的

python 讀取同樣的csv 會發生記憶體報錯 --解決方案

1.多次讀取同樣的csv時,記憶體儲存,可以通過刪除變數且釋放記憶體的形式恢復原有記憶體 (天池O2O) #1754884 record,1053282 with coupon_id,9738 coupon. date_received:20160101~20160615,date:2016

struts2的action從request獲取數值的兩種方式

無論是提交form表單還是從URL中直接獲取,struts2的action類中獲取請求引數值,總的來說有2種方式: 第一種在action中定義同名變數,提供get/set方法。 第二種方式是手動獲取H

springboot中的攔截器interceptor和過濾器filter,獲取request引數

大家好,我是烤鴨:    這是一篇關於springboot的攔截器(interceptor)和過濾器(Filter)。    先說一下過濾器和攔截器。區別:1.servlet請求,順序:Filter ——> interceptor。2.Filter的作用是對所有進行過濾

input file實現上傳檔案(不會覆蓋上次上傳的檔案)

html原生的file多選控制元件:<input class="className" type="file" name="name" accept="image/*" multiple /> 存在問題:第一次選擇了n個檔案,第二次選中m個檔案,第二次選擇結束

新手學習FFmpeg - 呼叫API編寫實現淡入淡出效果的濾鏡

前面幾篇文章聊了聊FFmpeg的基礎知識,我也是接觸FFmpeg不久,除了時間處理之外,很多高深(濾鏡)操作都沒接觸到。在學習時間處理的時候,都是通過在ffmpeg目前提供的avfilter基礎上面修修補補(補充各種debug log)來驗證想法。 而這次我將嘗試新建立一個avfilter,來實現一個新濾鏡。

recyclerview裡面實現佈局巢狀recyclerview第一進入的時候出現自動滾動到第二天reccyclerview問題

今天用recyclerview寫的時候出現一個問題,第一層用的一個imageview,第二層用的是recyclerview,第三層也是recyclerview,,第一層進去的時候出現一個問題,顯示不是從第一層imageview而是自動滾到了第二層recyclerview,查

js實現每次程序發送一個數據 ,發送不一樣,5秒後繼續執行程序,判斷如果五秒後發送過來的數據和上次不一樣,少的刪除的增加

增加 開始 後繼 tin key cli 監控 沒有 sop /*存儲設備ID*/var IDSNew = new Array();//判斷是否已經啟用服務var isopen = true;//需要放到接收設備數據處IDSNew[client.deviceId]=new

python文件操作:pickle模塊dump後出現的讀取問題

讀寫 class 取出 span 由於 無法 pen 寫文件 color pickle模塊在python中是用於數據持久化的,基本用法涉及到的也就是dump和load,亦或者dumps和loads。 pickle在使用過程中有一個特點,就是由於其特殊的內容標記,使得文件du