一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

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

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

服務器之家 - 編程語言 - Java教程 - Request的包裝類HttpServletRequestWrapper的使用說明

Request的包裝類HttpServletRequestWrapper的使用說明

2021-11-30 11:43SuperPurse Java教程

這篇文章主要介紹了Request的包裝類HttpServletRequestWrapper的使用說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

Request的包裝類HttpServletRequestWrapper使用

在使用zuul進行鑒權的時候,我們希望從請求Request中獲取輸入流,解析里面的內容,奈何InputStream只能被讀取一次。為啥呢?源碼里是這樣說的:

public int read(byte[] b,int off, int len)

   Reads up to len bytes of data into an array of bytes from this input stream. Ifpos equals count, then -1 is returned to indicate end of file. Otherwise, the number k of bytes read is equal to the smaller of len and count-pos.If k is positive, then bytes buf[pos] through buf[pos+k-1] are copied into b[off] through b[off+k-1] in the manner performed by System.arraycopy. The value k is added into pos and k is returned.  

大致的意思是:

在InputStream讀取的時候,會有一個pos指針,它指示每次讀取之后下一次要讀取的起始位置。在每次讀取后會更新pos的值,當你下次再來讀取的時候是從pos的位置開始的,而不是從頭開始,所以第二次獲取String中的值的時候是不全的,API中提供了一個解決辦法:reset()。但我發現在inputStream和servlet中根本不起作用。提示 mark/reset not supported 。意思是只有重寫過markSupported()方法的IO流才可以用。所以一般我們使用inputStream,最好在一次內處理完所有邏輯。

那么就沒法在中途獲取請求流中的數據么?當然有辦法了,我可是PPZ,只需要重寫Request緩存一下流中的數據就好了,實現代碼如下:

BodyReaderHttpServletRequestWrapper.java

?
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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package com.neo.authUtils;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.Enumeration;
import java.util.NoSuchElementException;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper; 
public class BodyReaderHttpServletRequestWrapper extends
        HttpServletRequestWrapper { 
   // private final byte[] body; 
     -----》private byte[] body;《-------
    public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException { 
        super(request); 
        System.out.println("-------------------打印請求的頭信息------------------------------");   
        Enumeration<?> e = request.getHeaderNames()   ;   
         while(e.hasMoreElements()){   
             String name = (String) e.nextElement();   
             String value = request.getHeader(name);   
            // System.out.println(name+" = "+value);   
         }
         -----》獲取流中的數據緩存到字節數組中,以后要讀數據就用這里的《------
        body = HttpHelper.getBodyString(request).getBytes(Charset.forName("UTF-8")); 
    }
    /**
     * 從請求的頭部獲取用戶的身份識別id;
     * @param request
     * @return
     */
    public String getJsessionidFromHeader(HttpServletRequest request) {
        String jsessionid = null;//識別用戶身份的id;
        Enumeration<?> e = request.getHeaderNames()   ;   
        while(e.hasMoreElements()){   
            String name = (String) e.nextElement();   
            String value = request.getHeader(name);
            //cookie = JSESSIONID=B926F6024438D4C693A5E5881595160C; SESSION=458e80dc-e354-4af3-a501-74504a873e70
            if("cookie".equals(name)) {
                jsessionid = value.split(";")[0].split("=")[1];
            }
            System.out.println(name+"="+value);
        }
       // System.out.println("======jsessionid========>"+jsessionid);
        return jsessionid;
    }
    @Override
    public BufferedReader getReader() throws IOException { 
        return new BufferedReader(new InputStreamReader(getInputStream())); 
    
    @Override
    public ServletInputStream getInputStream() throws IOException { 
         ------》從緩存的數據中讀取數據《------
        final ByteArrayInputStream bais = new ByteArrayInputStream(body); 
        return new ServletInputStream() { 
            public int read() throws IOException { 
                return bais.read(); 
            }
            @Override
            public boolean isFinished() {
                // TODO Auto-generated method stub
                return false;
            }
            @Override
            public boolean isReady() {
                // TODO Auto-generated method stub
                return false;
            }
            @Override
            public void setReadListener(ReadListener listener) {
                // TODO Auto-generated method stub
            
        }; 
    
    @Override
    public String getHeader(String name) { 
        return super.getHeader(name); 
    
    @Override
    public Enumeration<String> getHeaderNames() { 
        return super.getHeaderNames(); 
    
   /* @Override 
    public Enumeration<String> getHeaders(String name) { 
        return super.getHeaders(name); 
    }  */
    /**
     * content-type=text/plain;charset=UTF-8
     * 重寫getHeaders方法,實現自定義Content-Type;
     */
    @Override 
    public Enumeration<String> getHeaders(String name) { 
        if ((null != name && name.equals("Content-Type"))||(null != name && name.equals("content-type"))) { 
            return new Enumeration<String>() { 
                private boolean hasGetted = false; 
                @Override 
                public String nextElement() { 
                    if (hasGetted) { 
                        throw new NoSuchElementException(); 
                    } else { 
                        hasGetted = true;
                        return "application/json;charset=utf-8"; 
                    
                
                @Override 
                public boolean hasMoreElements() { 
                    return !hasGetted; 
                
            }; 
        
        return super.getHeaders(name); 
    
    /**
     * 添加自定義信息到請求體;
     * @param customMsg:自定義的添加到請求體中的信息;
     */
    public void appendCustomMsgToReqBody(String customMsg) {
        String oldBodyString = HttpHelper.getBodyString(this);//oldBodyString一定是通過當前對象的輸入流解析得來的,否則接收時會報EOFException;
        String appendMsg = HttpHelper.appendCustomMsgToReqBody(customMsg);
        String requestBodyAfterAppend = appendMsg + "," +oldBodyString;
        //this.body = HttpHelper.appendCustomMsgToReqBody(HttpHelper.appendCustomMsgToReqBody(customMsg)+(HttpHelper.getBodyString(this))).getBytes(Charset.forName("UTF-8"));
        //this.body = HttpHelper.appendCustomMsgToReqBody((HttpHelper.getBodyString(this))).getBytes(Charset.forName("UTF-8"));
        this.body = HttpHelper.appendCustomMsgToReqBody(requestBodyAfterAppend).getBytes(Charset.forName("UTF-8"));
    }

HttpHelper.java

?
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
package com.neo.authUtils;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import javax.servlet.ServletRequest;
public class HttpHelper {
    /**
     * 獲取post請求中的Body
     *
     * @param request
     * @return
     */
    public static String getBodyString(ServletRequest request) {
        StringBuilder sb = new StringBuilder();
        InputStream inputStream = null;
        BufferedReader reader = null;
        try {
            inputStream = request.getInputStream();
            //讀取流并將流寫出去,避免數據流中斷;
            reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
            String line = "";
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return sb.toString();
    }
    //添加自定義的信息到請求體中;
    public static String appendCustomMsgToReqBody(String newReqBodyStr) {
        StringBuilder sb = new StringBuilder();
        InputStream inputStream = null;
        BufferedReader reader = null;
        String newReqBody = null;
        try {
            //通過字符串構造輸入流;
            inputStream = String2InputStream(newReqBodyStr);
            reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
            String line = "";
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        //返回字符串;
        newReqBody = sb.toString();
        return newReqBody;
    }
    //將字符串轉化為輸入流;
    public static InputStream String2InputStream(String str) {
        ByteArrayInputStream stream = null;
        try {
            stream = new ByteArrayInputStream(str.getBytes("UTF-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return stream;
    }
}

上述方案解決了

使用request.getInpuStream()方法讀取流中的數據只能讀取一次的問題,其實當我們在使用第三方接口時,如果請求頭信息和我們的服務所需不一致,例如第三方接口中頭部信息為:content-type=text/plain;charset=UTF-8

而我們需要的是:”application/json;charset=utf-8”時,我們也是可以通過重寫對應的方法對請求的頭部信息進行修改的,代碼如下:

?
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
/**
     * content-type=text/plain;charset=UTF-8
     * 重寫getHeaders方法,實現自定義Content-Type;
     */
    @Override
    public Enumeration<String> getHeaders(String name) { 
        if ((null != name && name.equals("Content-Type"))||(null != name && name.equals("content-type"))) { 
            return new Enumeration<String>() { 
                private boolean hasGetted = false
                @Override
                public String nextElement() { 
                    if (hasGetted) { 
                        throw new NoSuchElementException(); 
                    } else
                        hasGetted = true;
                        return "application/json;charset=utf-8"
                    
                
                @Override
                public boolean hasMoreElements() { 
                    return !hasGetted; 
                
            }; 
        
        return super.getHeaders(name); 
    

當我們在后端設置了頭部信息后,如果不出意外,前端發送的請求將變為簡單請求,這樣,服務器的處理機制將簡單很多。

HttpServletRequestWrapper和HttpServletResponseWrapper使用時的坑

WrapperRequest和WrapperResponse的使用

在做JavaWeb開發過程中如果想拿到請求參數和返回數據的話我們就會使用到這兩個類,從類名上就可以看出是包裝類,通過這兩個類的包裝我們可以使用移花接木的方式獲取到對應的參數數據。

這里涉及到的坑

坑1

如果請求參數在Body內時取出參數后,后端程序就無法再次取出數據

這個和InputStream不能重復讀有關 ,這里需要將Request中的數據自己保存一份然后在使用的時候給出新的InputStream,這樣就可以避免使用同一個InputStream讀取完數據后無法重新讀取數據

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Override
    public ServletInputStream getInputStream() throws IOException {
        //這里每次都重新創建了一個InputStream
        final ByteArrayInputStream inputStream = new ByteArrayInputStream(bodyData);
        return new ServletInputStream() {
            @Override
            public int read() throws IOException {
                return inputStream.read();
            }
            @Override
            public boolean isFinished() {
                return false;
            }
            @Override
            public boolean isReady() {
                return false;
            }
            @Override
            public void setReadListener(ReadListener readListener) {
            }
        };
    }

坑2

使用HttpServletResponseWrapper包裝Response后無法返回數據或者無法返回html,css等數據

這個跟網上的教程有關,大多網上的教程是這樣的如下代碼:

?
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
//先定義一個Filter類包裝對應的request和response
public class WrapperFilter extends OncePerRequestFilter {
    private static Logger logger = LoggerFactory.getLogger(WrapperFilter.class);
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
            logger.debug(" request mapping {} {}", request.getRequestURL(), request.getRequestURI());
            RequestWrapper requestWrapper = new RequestWrapper(request);
            ResponseWrapper responseWrapper = new ResponseWrapper(response);
            filterChain.doFilter(requestWrapper, responseWrapper);
    }
//response的包裝類
public class ResponseWrapper extends HttpServletResponseWrapper {
    private static Logger logger = LoggerFactory.getLogger(ResponseWrapper.class);
    private final ByteArrayOutputStream buffer;
    private final ServletOutputStream out;
    private final PrintWriter writer;
    
    public ResponseWrapper(HttpServletResponse response) throws IOException {
        super(response);
        buffer = new ByteArrayOutputStream(2048);
        out = new WrapperOutputStream(buffer);
        writer = new PrintWriter(buffer);
    }
    @Override
    public ServletOutputStream getOutputStream() throws IOException {
        return out;
    }
    /**
     *     當獲取字符輸出流時,實際獲取的是我們自己包裝的字符輸出流
     */
    @Override
    public PrintWriter getWriter() {
        return writer;
    }
   /**
     *     獲取返回的數據內容,這個是截獲的數據
     */
    public byte[] getResponseData() throws IOException {
        flushBuffer();
        return buffer.toByteArray();
    }
    public String getContent() throws IOException {
        flushBuffer();
        return buffer.toString();
    }
}

上面的代碼雖然是可以獲取到數據的但是,數據就無法返回到前端頁面了,那么為什么會出現這樣的問題呢,咱們來分析一下。

1、包裝類對response進行了包裝,并且重寫了getWriter和getOutputStream 這樣就可以保證后端使用response向前端寫數據時寫到我們定義的buffer中

2、這個包裝類是不范圍的,也就是只在WrapperFilter 之后有效,但是瀏覽器從response讀取數據明顯是在WrapperFilter的范圍之外的

也就是說瀏覽器從reponse讀取數據時無法使用ResponseWrapper而只能使用response 這個默認是ResponseFacade

3、那么問題來了咱們上面有往response中寫入數據嗎,顯然是沒有的也就是寫數據只在ResponseWrapper中有而ResponseFacade 是沒有數據的所以瀏覽器了就無法讀取到返回的數據啦。

清楚以上問題后問題就變得簡單得多了,那么我們只需要往ResponseFacade 中也定入一份數據就可以了

解決問題

Filter的內容不變

ResponseWrapper中的代碼如下修改

?
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
public class ResponseWrapper extends HttpServletResponseWrapper {
    private static Logger logger = LoggerFactory.getLogger(ResponseWrapper.class);
    private final ByteArrayOutputStream buffer;
    private final ServletOutputStream out;
    private final PrintWriter writer;
   
    public ResponseWrapper(HttpServletResponse response) throws IOException {
        super(response);
        buffer = new ByteArrayOutputStream(2048);
        //這里將response也傳入了WrapperOutputStream 和 WrapperWriter
        out = new WrapperOutputStream(buffer,  response);
        writer = new WrapperWriter(buffer, response);
    }
    @Override
    public ServletOutputStream getOutputStream() throws IOException {
        return out;
    }
    /**
     *     當獲取字符輸出流時,實際獲取的是我們自己包裝的字符輸出流
     */
    @Override
    public PrintWriter getWriter() {
        return writer;
    }
    public byte[] getResponseData() throws IOException {
        flushBuffer();
        return buffer.toByteArray();
    }
    public String getContent() throws IOException {
        flushBuffer();
        return buffer.toString();
    }
}

這里將response也傳入了WrapperOutputStream 和 WrapperWriter 這樣我們在做數據寫入的時候就可以同時向reponse中寫入數據了

這兩個類的實現如下:

?
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
public class WrapperOutputStream extends ServletOutputStream {
    private OutputStream innerOut;
    private HttpServletResponse response;
    public WrapperOutputStream(OutputStream innerOut, HttpServletResponse response) {
        super();
        this.response = response;
        this.innerOut = innerOut;
    }
    @Override
    public boolean isReady() {
        if(response == null){
            return false;
        }
        try {
            return response.getOutputStream().isReady();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }
    @Override
    public void setWriteListener(WriteListener listener) {
        if(response != null){
            try {
                ((CoyoteOutputStream)response.getOutputStream()).setWriteListener(listener);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    //關鍵在這里
    @Override
    public void write(int b) throws IOException {
        if(response != null){
            response.getOutputStream().write(b);
        }
        innerOut.write(b);
    }
}
//另一個類
public class WrapperWriter extends PrintWriter {
    private HttpServletResponse response;
    ByteArrayOutputStream output;
    public WrapperWriter(ByteArrayOutputStream out, HttpServletResponse response) {
        super(out);
        this.response = response;
        this.output = out;
    }
    //關鍵在這里
    @Override
    public void write(int b){
        super.write(b);
        try {
            response.getWriter().write(b);
        } catch (IOException e) {
            e.printStackTrace();
            this.setError();
        }
    }
    //關鍵在這里
    @Override
    public void write(String s, int off, int len) {
        super.write(s,off,len);
        try {
            response.getWriter().write(s,off,len);
        } catch (IOException e) {
            e.printStackTrace();
            this.setError();
        }
    }
}

以上可以看到數據的寫入變成了寫兩份一份寫到了自定義的對象中一份寫到了response中這樣返回到前端的responnse就不會沒有數據了

到此問題完全解決,這里還需要注意的就是PrintWriter 有多個writer重載需要都進行重寫才行

問題延伸

有人會問能不能直接將response中的OutputStream和Writer獲取到分配給對應的WrapperOutputStream 和WrapperWriter而不是直接傳入response本身,答案是不可以的,response是不能同時獲取OutputStream和Writer的因為他們操作的是同一個數據,所以ResponseFacade 實現時對其進行了保護——同時只能獲取OutputStream和Writer中的一個。

以上為個人經驗,希望能給大家一個參考,也希望大家多多支持服務器之家。

原文鏈接:https://blog.csdn.net/zj20142213/article/details/80012221

延伸 · 閱讀

精彩推薦
  • Java教程升級IDEA后Lombok不能使用的解決方法

    升級IDEA后Lombok不能使用的解決方法

    最近看到提示IDEA提示升級,尋思已經有好久沒有升過級了。升級完畢重啟之后,突然發現好多錯誤,本文就來介紹一下如何解決,感興趣的可以了解一下...

    程序猿DD9332021-10-08
  • Java教程xml與Java對象的轉換詳解

    xml與Java對象的轉換詳解

    這篇文章主要介紹了xml與Java對象的轉換詳解的相關資料,需要的朋友可以參考下...

    Java教程網2942020-09-17
  • Java教程Java8中Stream使用的一個注意事項

    Java8中Stream使用的一個注意事項

    最近在工作中發現了對于集合操作轉換的神器,java8新特性 stream,但在使用中遇到了一個非常重要的注意點,所以這篇文章主要給大家介紹了關于Java8中S...

    阿杜7482021-02-04
  • Java教程20個非常實用的Java程序代碼片段

    20個非常實用的Java程序代碼片段

    這篇文章主要為大家分享了20個非常實用的Java程序片段,對java開發項目有所幫助,感興趣的小伙伴們可以參考一下 ...

    lijiao5352020-04-06
  • Java教程小米推送Java代碼

    小米推送Java代碼

    今天小編就為大家分享一篇關于小米推送Java代碼,小編覺得內容挺不錯的,現在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧...

    富貴穩中求8032021-07-12
  • Java教程Java BufferWriter寫文件寫不進去或缺失數據的解決

    Java BufferWriter寫文件寫不進去或缺失數據的解決

    這篇文章主要介紹了Java BufferWriter寫文件寫不進去或缺失數據的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望...

    spcoder14552021-10-18
  • Java教程Java使用SAX解析xml的示例

    Java使用SAX解析xml的示例

    這篇文章主要介紹了Java使用SAX解析xml的示例,幫助大家更好的理解和學習使用Java,感興趣的朋友可以了解下...

    大行者10067412021-08-30
  • Java教程Java實現搶紅包功能

    Java實現搶紅包功能

    這篇文章主要為大家詳細介紹了Java實現搶紅包功能,采用多線程模擬多人同時搶紅包,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙...

    littleschemer13532021-05-16
主站蜘蛛池模板: 美国女孩毛片 | 久久久精品免费免费直播 | 国产自产2023最新麻豆 | 日本熟hdx | 国产精品免费视频一区一 | 麻豆视频免费在线观看 | 湿好紧太硬了我太爽了 | 午夜国产福利视频一区 | 色在线看 | 亚洲卡一卡2卡三卡4麻豆 | 动漫女性扒开尿口羞羞漫画 | h黑寡妇一级毛片 | 免费看打屁股视频的软件 | 男女刺激高清视频在线观看 | 亚洲免费视频在线观看 | 亚洲 日韩经典 中文字幕 | 60岁妇女毛片免费观看 | 日本加勒比无码av | 亚洲国产资源 | 国产一区二区三区在线看片 | 美女张开腿让我了一夜 | 成年人在线观看免费视频 | 视频免费在线 | 日本乱中文字幕系列在线观看 | 黄瓜视频黄版 | 香蕉tv国产在线永久播放 | 久久久精品3d动漫一区二区三区 | 国产精品毛片无码 | 亚洲乱码一区二区三区国产精品 | 青草视频在线观看视频 | 男人jj视频 | 亚洲美色综合天天久久综合精品 | 2018天天拍拍拍免费视频 | 久久久无码精品亚洲A片软件 | 99视频精品全部免费观看 | 国产精品久久久久久久人人看 | 国产码一区二区三区 | 欧美性xxxxxx爱 | 亚洲 欧美 国产 综合久久 | 欧美一区二区三区四区五区六区 | v视界影院成片 |