欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Request的包裝類HttpServletRequestWrapper的使用說(shuō)明

 更新時(shí)間:2021年08月23日 11:39:53   作者:SuperPurse  
這篇文章主要介紹了Request的包裝類HttpServletRequestWrapper的使用說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

Request的包裝類HttpServletRequestWrapper使用

在使用zuul進(jìn)行鑒權(quán)的時(shí)候,我們希望從請(qǐng)求Request中獲取輸入流,解析里面的內(nèi)容,奈何InputStream只能被讀取一次。為啥呢?源碼里是這樣說(shuō)的:

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讀取的時(shí)候,會(huì)有一個(gè)pos指針,它指示每次讀取之后下一次要讀取的起始位置。在每次讀取后會(huì)更新pos的值,當(dāng)你下次再來(lái)讀取的時(shí)候是從pos的位置開始的,而不是從頭開始,所以第二次獲取String中的值的時(shí)候是不全的,API中提供了一個(gè)解決辦法:reset()。但我發(fā)現(xiàn)在inputStream和servlet中根本不起作用。提示 mark/reset not supported 。意思是只有重寫過(guò)markSupported()方法的IO流才可以用。所以一般我們使用inputStream,最好在一次內(nèi)處理完所有邏輯。

那么就沒(méi)法在中途獲取請(qǐng)求流中的數(shù)據(jù)么?當(dāng)然有辦法了,我可是PPZ,只需要重寫Request緩存一下流中的數(shù)據(jù)就好了,實(shí)現(xiàn)代碼如下:

BodyReaderHttpServletRequestWrapper.java

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("-------------------打印請(qǐng)求的頭信息------------------------------");    
        Enumeration<?> e = request.getHeaderNames()   ;    
         while(e.hasMoreElements()){    
             String name = (String) e.nextElement();    
             String value = request.getHeader(name);    
            // System.out.println(name+" = "+value);    
         }
         -----》獲取流中的數(shù)據(jù)緩存到字節(jié)數(shù)組中,以后要讀數(shù)據(jù)就用這里的《------
        body = HttpHelper.getBodyString(request).getBytes(Charset.forName("UTF-8"));  
    }
    /**
     * 從請(qǐng)求的頭部獲取用戶的身份識(shí)別id;
     * @param request
     * @return
     */
    public String getJsessionidFromHeader(HttpServletRequest request) {
        String jsessionid = null;//識(shí)別用戶身份的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 {  
         ------》從緩存的數(shù)據(jù)中讀取數(shù)據(jù)《------
        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方法,實(shí)現(xiàn)自定義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);  
    }  
    /**
     * 添加自定義信息到請(qǐng)求體;
     * @param customMsg:自定義的添加到請(qǐng)求體中的信息;
     */
    public void appendCustomMsgToReqBody(String customMsg) {
        String oldBodyString = HttpHelper.getBodyString(this);//oldBodyString一定是通過(guò)當(dāng)前對(duì)象的輸入流解析得來(lái)的,否則接收時(shí)會(huì)報(bào)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

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請(qǐng)求中的Body
     *
     * @param request
     * @return
     */
    public static String getBodyString(ServletRequest request) {
        StringBuilder sb = new StringBuilder();
        InputStream inputStream = null;
        BufferedReader reader = null;
        try {
            inputStream = request.getInputStream();
            //讀取流并將流寫出去,避免數(shù)據(jù)流中斷;
            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();
    }
    //添加自定義的信息到請(qǐng)求體中;
    public static String appendCustomMsgToReqBody(String newReqBodyStr) {
        StringBuilder sb = new StringBuilder();
        InputStream inputStream = null;
        BufferedReader reader = null;
        String newReqBody = null;
        try {
            //通過(guò)字符串構(gòu)造輸入流;
            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;
    }
    //將字符串轉(zhuǎn)化為輸入流;
    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()方法讀取流中的數(shù)據(jù)只能讀取一次的問(wèn)題,其實(shí)當(dāng)我們?cè)谑褂玫谌浇涌跁r(shí),如果請(qǐng)求頭信息和我們的服務(wù)所需不一致,例如第三方接口中頭部信息為:content-type=text/plain;charset=UTF-8

而我們需要的是:”application/json;charset=utf-8”時(shí),我們也是可以通過(guò)重寫對(duì)應(yīng)的方法對(duì)請(qǐng)求的頭部信息進(jìn)行修改的,代碼如下:

/**
     * content-type=text/plain;charset=UTF-8
     * 重寫getHeaders方法,實(shí)現(xiàn)自定義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);  
    }  

當(dāng)我們?cè)诤蠖嗽O(shè)置了頭部信息后,如果不出意外,前端發(fā)送的請(qǐng)求將變?yōu)楹?jiǎn)單請(qǐng)求,這樣,服務(wù)器的處理機(jī)制將簡(jiǎn)單很多。

HttpServletRequestWrapper和HttpServletResponseWrapper使用時(shí)的坑

WrapperRequest和WrapperResponse的使用

在做JavaWeb開發(fā)過(guò)程中如果想拿到請(qǐng)求參數(shù)和返回?cái)?shù)據(jù)的話我們就會(huì)使用到這兩個(gè)類,從類名上就可以看出是包裝類,通過(guò)這兩個(gè)類的包裝我們可以使用移花接木的方式獲取到對(duì)應(yīng)的參數(shù)數(shù)據(jù)。

這里涉及到的坑

坑1

如果請(qǐng)求參數(shù)在Body內(nèi)時(shí)取出參數(shù)后,后端程序就無(wú)法再次取出數(shù)據(jù)

這個(gè)和InputStream不能重復(fù)讀有關(guān) ,這里需要將Request中的數(shù)據(jù)自己保存一份然后在使用的時(shí)候給出新的InputStream,這樣就可以避免使用同一個(gè)InputStream讀取完數(shù)據(jù)后無(wú)法重新讀取數(shù)據(jù)

@Override
    public ServletInputStream getInputStream() throws IOException {
        //這里每次都重新創(chuàng)建了一個(gè)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后無(wú)法返回?cái)?shù)據(jù)或者無(wú)法返回html,css等數(shù)據(jù)

這個(gè)跟網(wǎng)上的教程有關(guān),大多網(wǎng)上的教程是這樣的如下代碼:

//先定義一個(gè)Filter類包裝對(duì)應(yīng)的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;
    }
    /**
     *     當(dāng)獲取字符輸出流時(shí),實(shí)際獲取的是我們自己包裝的字符輸出流
     */
    @Override
    public PrintWriter getWriter() {
        return writer;
    }
   /**
     *     獲取返回的數(shù)據(jù)內(nèi)容,這個(gè)是截獲的數(shù)據(jù)
     */
    public byte[] getResponseData() throws IOException {
        flushBuffer();
        return buffer.toByteArray();
    }
    public String getContent() throws IOException {
        flushBuffer();
        return buffer.toString();
    }
}

上面的代碼雖然是可以獲取到數(shù)據(jù)的但是,數(shù)據(jù)就無(wú)法返回到前端頁(yè)面了,那么為什么會(huì)出現(xiàn)這樣的問(wèn)題呢,咱們來(lái)分析一下。

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

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

也就是說(shuō)瀏覽器從reponse讀取數(shù)據(jù)時(shí)無(wú)法使用ResponseWrapper而只能使用response 這個(gè)默認(rèn)是ResponseFacade

3、那么問(wèn)題來(lái)了咱們上面有往response中寫入數(shù)據(jù)嗎,顯然是沒(méi)有的也就是寫數(shù)據(jù)只在ResponseWrapper中有而ResponseFacade 是沒(méi)有數(shù)據(jù)的所以瀏覽器了就無(wú)法讀取到返回的數(shù)據(jù)啦。

清楚以上問(wèn)題后問(wèn)題就變得簡(jiǎn)單得多了,那么我們只需要往ResponseFacade 中也定入一份數(shù)據(jù)就可以了

解決問(wèn)題

Filter的內(nèi)容不變

ResponseWrapper中的代碼如下修改

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;
    }
    /**
     *     當(dāng)獲取字符輸出流時(shí),實(shí)際獲取的是我們自己包裝的字符輸出流
     */
    @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 這樣我們?cè)谧鰯?shù)據(jù)寫入的時(shí)候就可以同時(shí)向reponse中寫入數(shù)據(jù)了

這兩個(gè)類的實(shí)現(xiàn)如下:

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();
            }
        }
    }
    //關(guān)鍵在這里
    @Override
    public void write(int b) throws IOException {
        if(response != null){
            response.getOutputStream().write(b);
        }
        innerOut.write(b);
    }
}
//另一個(gè)類
public class WrapperWriter extends PrintWriter {
    private HttpServletResponse response;
    ByteArrayOutputStream output;
    public WrapperWriter(ByteArrayOutputStream out, HttpServletResponse response) {
        super(out);
        this.response = response;
        this.output = out;
    }
    //關(guān)鍵在這里
    @Override
    public void write(int b){
        super.write(b);
        try {
            response.getWriter().write(b);
        } catch (IOException e) {
            e.printStackTrace();
            this.setError();
        }
    }
    //關(guān)鍵在這里
    @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();
        }
    }
}

以上可以看到數(shù)據(jù)的寫入變成了寫兩份一份寫到了自定義的對(duì)象中一份寫到了response中這樣返回到前端的responnse就不會(huì)沒(méi)有數(shù)據(jù)了

到此問(wèn)題完全解決,這里還需要注意的就是PrintWriter 有多個(gè)writer重載需要都進(jìn)行重寫才行

問(wèn)題延伸

有人會(huì)問(wèn)能不能直接將response中的OutputStream和Writer獲取到分配給對(duì)應(yīng)的WrapperOutputStream 和WrapperWriter而不是直接傳入response本身,答案是不可以的,response是不能同時(shí)獲取OutputStream和Writer的因?yàn)樗麄儾僮鞯氖峭粋€(gè)數(shù)據(jù),所以ResponseFacade 實(shí)現(xiàn)時(shí)對(duì)其進(jìn)行了保護(hù)——同時(shí)只能獲取OutputStream和Writer中的一個(gè)。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Spring Hystrix熔斷報(bào)警原理圖例解析

    Spring Hystrix熔斷報(bào)警原理圖例解析

    這篇文章主要介紹了Spring Hystrix熔斷報(bào)警原理圖例解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-03-03
  • Idea中使用Git的流程

    Idea中使用Git的流程

    這篇文章主要介紹了Idea中使用Git的流程,git是目前流行的分布式版本管理系統(tǒng)。本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2020-09-09
  • 淺析 ArrayList 和 LinkedList 有什么區(qū)別

    淺析 ArrayList 和 LinkedList 有什么區(qū)別

    ArrayList 和 LinkedList 有什么區(qū)別,是面試官非常喜歡問(wèn)的一個(gè)問(wèn)題。今天通過(guò)本文給大家詳細(xì)介紹下,感興趣的朋友跟隨小編一起看看吧
    2020-10-10
  • Java servlet 使用 PrintWriter 時(shí)的編碼與亂碼的示例代碼

    Java servlet 使用 PrintWriter 時(shí)的編碼與亂碼的示例代碼

    本篇文章主要介紹了Java servlet 使用 PrintWriter 時(shí)的編碼與亂碼的示例代碼,探討了 PrintWriter 的缺省編碼與普通字符流的缺省編碼的差異,具有一定的參考價(jià)值,有興趣的可以了解一下
    2017-11-11
  • 解決Java中new?BigDecimal()的坑

    解決Java中new?BigDecimal()的坑

    這篇文章主要介紹了解決Java中new?BigDecimal()的坑,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • 深入理解java中的null“類型”

    深入理解java中的null“類型”

    這篇文章主要介紹了深入理解java中的null“類型”,分享了相關(guān)代碼示例,小編覺(jué)得還是挺不錯(cuò)的,具有一定借鑒價(jià)值,需要的朋友可以參考下
    2018-01-01
  • java通過(guò)poi解析word入門的操作方法

    java通過(guò)poi解析word入門的操作方法

    Apache POI 是一個(gè)處理Miscrosoft Office各種文件格式的開源項(xiàng)目,可以使用POI在Java程序中對(duì)Miscrosoft Office各種文件進(jìn)行讀寫操作,本文給大家介紹java通過(guò)poi解析word入門的操作方法,感興趣的朋友一起看看吧
    2024-08-08
  • java生成圖片進(jìn)行套打功能

    java生成圖片進(jìn)行套打功能

    這篇文章主要為大家詳細(xì)介紹了java生成圖片進(jìn)行套打功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-08-08
  • java8使用Stream API方法總結(jié)

    java8使用Stream API方法總結(jié)

    在本篇文章里小編給大家分享了關(guān)于java8使用Stream API方法相關(guān)知識(shí)點(diǎn),需要的朋友們學(xué)習(xí)下。
    2019-04-04
  • IDEA之啟動(dòng)參數(shù),配置文件默認(rèn)參數(shù)的操作

    IDEA之啟動(dòng)參數(shù),配置文件默認(rèn)參數(shù)的操作

    這篇文章主要介紹了IDEA之啟動(dòng)參數(shù),配置文件默認(rèn)參數(shù)的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-01-01

最新評(píng)論