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

解決Java項(xiàng)目中request流只能獲取一次的問(wèn)題

 更新時(shí)間:2024年02月23日 11:11:59   作者:嗑嗑嗑瓜子的貓  
Java項(xiàng)目開(kāi)發(fā)中可能存在以下幾種情況,你需要在攔截器中統(tǒng)一攔截請(qǐng)求和你項(xiàng)目里可能需要搞一個(gè)統(tǒng)一的異常處理器,這兩種情況是比較常見(jiàn)的,本文將給大家介紹如何解決Java項(xiàng)目中request流只能獲取一次的問(wèn)題,需要的朋友可以參考下

問(wèn)題描述

Java項(xiàng)目開(kāi)發(fā)中可能存在以下幾種情況:

1、你需要在攔截器中統(tǒng)一攔截請(qǐng)求,拿到請(qǐng)求中的參數(shù),來(lái)做統(tǒng)一的判斷處理或者其他操作。

那問(wèn)題就來(lái)了,由于request輸入流的數(shù)據(jù)只能讀取一次,所以你在攔截器中你讀取了輸入流的數(shù)據(jù),當(dāng)請(qǐng)求進(jìn)入后邊的Controller時(shí),輸入流中已經(jīng)沒(méi)有數(shù)據(jù)了,導(dǎo)致獲取不到數(shù)據(jù)。

2、你項(xiàng)目里可能需要搞一個(gè)統(tǒng)一的異常處理器,然后想在異常處理器中把發(fā)生異常的接口地址,方法名,以及請(qǐng)求的參數(shù)記錄到日志里或者直接發(fā)送給你配置的告警系統(tǒng),比如發(fā)送給釘釘群通知。這種情況下,因?yàn)槟闱斑卌ontroller已經(jīng)獲取過(guò)一次request輸入流了,在后邊的異常處理器里你還想再?gòu)膔equest輸入流中拿到請(qǐng)求參數(shù)等信息,所以也會(huì)出現(xiàn)request流只能讀取一次的錯(cuò)誤。

以上兩種情況是開(kāi)發(fā)中比較常見(jiàn)的,當(dāng)然除此之外,別的場(chǎng)景下你可能也會(huì)遇到request流只能讀取一次的錯(cuò)誤,所以今天就來(lái)講一下如果遇到這種情況該怎么解決。

產(chǎn)生原因

1、一個(gè)InputStream對(duì)象在被讀取完成后,將無(wú)法被再次讀取,始終返回-1;

2、InputStream并沒(méi)有實(shí)現(xiàn)reset方法(可以重置首次讀取的位置),無(wú)法實(shí)現(xiàn)重置操作;

解決方法

一、引入依賴

<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.7</version>
</dependency>

二、自定義Wrapper類來(lái)包裝HttpServletRequest

我們需要寫一個(gè)自定義包裝類,并繼承HttpServletRequestWrapper

import org.apache.commons.io.IOUtils;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
 
/**
 * @描述 包裝HttpServletRequest
 *          MyServletRequestWrapper + RequestReplaceFilter 的作用是:
 *              解決異常處理器中拿post請(qǐng)求的json參數(shù)時(shí),報(bào)request流只能讀一次的錯(cuò)
 *              原因是 request.getReader() 和 request.getInputStream() 都是只能調(diào)用一次
 *              所以我這里要使用 HttpServletRequestWrapper 來(lái)實(shí)現(xiàn)自定義的 MyServletRequestWrapper包裝類
 *              把request里的 body 保存在 MyServletRequestWrapper中, 并且重寫 getInputStream()方法
 *              然后所有的request都在RequestReplaceFilter中被轉(zhuǎn)換成了我自定義的HttpServletRequestWrapper
 *              然后獲取 body時(shí)就都是調(diào)用 MyServletRequestWrapper中的 getBody()方法了
 * @創(chuàng)建人 caoju
 */
public class MyServletRequestWrapper extends HttpServletRequestWrapper {
 
    private final byte[] body;
 
    public MyServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        body = IOUtils.toByteArray(super.getInputStream());
    }
 
    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }
 
    @Override
    public ServletInputStream getInputStream() throws IOException {
        return new RequestBodyCachingInputStream(body);
    }
 
    private class RequestBodyCachingInputStream extends ServletInputStream {
        private byte[] body;
        private int lastIndexRetrieved = -1;
        private ReadListener listener;
 
        public RequestBodyCachingInputStream(byte[] body) {
            this.body = body;
        }
 
        @Override
        public int read() throws IOException {
            if (isFinished()) {
                return -1;
            }
            int i = body[lastIndexRetrieved + 1];
            lastIndexRetrieved++;
            if (isFinished() && listener != null) {
                try {
                    listener.onAllDataRead();
                } catch (IOException e) {
                    listener.onError(e);
                    throw e;
                }
            }
            return i;
        }
 
        @Override
        public boolean isFinished() {
            return lastIndexRetrieved == body.length - 1;
        }
 
        @Override
        public boolean isReady() {
            return isFinished();
        }
 
        @Override
        public void setReadListener(ReadListener listener) {
            if (listener == null) {
                throw new IllegalArgumentException("listener cann not be null");
            }
            if (this.listener != null) {
                throw new IllegalArgumentException("listener has been set");
            }
            this.listener = listener;
            if (!isFinished()) {
                try {
                    listener.onAllDataRead();
                } catch (IOException e) {
                    listener.onError(e);
                }
            } else {
                try {
                    listener.onAllDataRead();
                } catch (IOException e) {
                    listener.onError(e);
                }
            }
        }
 
        @Override
        public int available() throws IOException {
            return body.length - lastIndexRetrieved - 1;
        }
 
        @Override
        public void close() throws IOException {
            lastIndexRetrieved = body.length - 1;
            body = null;
        }
    }
}

三、創(chuàng)建過(guò)濾器,通過(guò)過(guò)濾器包裝原有的request對(duì)象

import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
 
/**
 * @描述
 * @創(chuàng)建人 caoju
 */
@Component
public class RequestReplaceFilter extends OncePerRequestFilter {
 
    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {
        if (!(request instanceof MyServletRequestWrapper)) {
            request = new MyServletRequestWrapper(request);
        }
        filterChain.doFilter(request, response);
    
        /*//如果有文件上傳的業(yè)務(wù)場(chǎng)景,需要用下面的代碼進(jìn)行處理,不然文件上傳的流會(huì)有問(wèn)題
        String contentType = request.getContentType();
        //如果contentType是空
        //或者contentType是多媒體的上傳類型則忽略,不進(jìn)行包裝,直接return
        if (contentType == null) {
            filterChain.doFilter(request, response);
            return;
        }else if(request.getContentType().startsWith("multipart/")){
            filterChain.doFilter(request, response);
            return;
        }else if (!(request instanceof MyServletRequestWrapper)) {
            request = new MyServletRequestWrapper(request);
        }
        filterChain.doFilter(request, response);
        */
    }
}

通過(guò)以上幾步,我們就實(shí)現(xiàn)了把request里的 body 保存在 MyServletRequestWrapper中的效果

就可以在整個(gè)請(qǐng)求鏈路中任何地方去重復(fù)的獲取request流了

四、使用案例

配置好之后,就可以在整個(gè)請(qǐng)求鏈路中任何地方去重復(fù)的獲取request流了。

比如,你可以在請(qǐng)求剛進(jìn)來(lái)時(shí),在過(guò)濾器或者攔截器里拿到request對(duì)象,再拿到request對(duì)象的流數(shù)據(jù),去做一些事情,或者你也可以在請(qǐng)求即將結(jié)束時(shí),在統(tǒng)一的異常處理器中拿到request對(duì)象,拿到request對(duì)象流數(shù)據(jù)里請(qǐng)求的json參數(shù);等等等等,還有其他很多你想使用的場(chǎng)景,都可以這么做。

下面是在代碼中利用RequestContextHolder獲取request對(duì)象,拿到request對(duì)象后就可以獲取請(qǐng)求方式、請(qǐng)求url、以及請(qǐng)求參數(shù)這些數(shù)據(jù)了。如果你在某些地方也有需要打印記錄請(qǐng)求方式、請(qǐng)求url、請(qǐng)求參數(shù)的這些需求,那可以直接復(fù)制粘貼我下邊的代碼就ok了

        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        String mode = "";
        String methodUrl = "";
        String param = "";
        if (requestAttributes != null) {
            ServletRequestAttributes attributes = (ServletRequestAttributes) requestAttributes;
            HttpServletRequest request = attributes.getRequest();
            //請(qǐng)求方式
            mode = request.getMethod();
            //方法URL
            methodUrl = request.getRequestURI();
            if(mode.equals(HttpMethod.GET.name())){
                param = request.getQueryString();
            }
            if(mode.equals(HttpMethod.POST.name())){
                param = getJsonRequest(request);
            }
        }
    /**
     * 獲取Request中的JSON字符串
     * @param request
     * @return
     * @throws IOException
     */
    public static String getJsonRequest(HttpServletRequest request) {
        StringBuilder sb = new StringBuilder();
        try (BufferedReader reader = request.getReader();) {
            char[] buff = new char[1024];
            int len;
            while ((len = reader.read(buff)) != -1) {
                sb.append(buff, 0, len);
            }
        } catch (IOException e) {
            log.error("POST請(qǐng)求參數(shù)獲取異常", e);
        }
        return sb.toString();
    }

ok,到這里解決request流只能獲取一次的問(wèn)題就搞定了

希望對(duì)你有所幫助

以上就是解決Java項(xiàng)目中request流只能獲取一次的問(wèn)題的詳細(xì)內(nèi)容,更多關(guān)于Java request流獲取一次的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java中String字符串常量池和intern方法源碼分析

    Java中String字符串常量池和intern方法源碼分析

    在之前的文章中,小編給大家介紹了String字符串的不可變性及其實(shí)現(xiàn)原理,其中給大家提到了字符串常量池的概念,那么什么是常量池,String字符串與常量池有什么關(guān)系,本文給大家嘮嘮字符串常量池及String#intern()方法的作用,需要的朋友可以參考下
    2023-05-05
  • Java番外雜談之每天掃的二維碼你了解它內(nèi)含的信息嗎

    Java番外雜談之每天掃的二維碼你了解它內(nèi)含的信息嗎

    二維碼已經(jīng)成為我們?nèi)粘I钪斜夭豢缮俚慕M成部分了,登錄需要掃一掃二維碼、買東西付錢需要掃一掃二維碼、開(kāi)會(huì)簽到也需要掃一掃二維碼,那么如此使用的二維碼技術(shù),背后的原理是怎樣的呢?本文將結(jié)合二維碼的發(fā)展歷程以及典型應(yīng)用場(chǎng)景,分析二維碼背后的技術(shù)原理
    2022-02-02
  • springboot導(dǎo)入多個(gè)配置文件的方法實(shí)踐

    springboot導(dǎo)入多個(gè)配置文件的方法實(shí)踐

    Spring Boot 2.4.x引入了新的配置導(dǎo)入方式,通過(guò)spring.config.import屬性可以靈活地導(dǎo)入多種類型的配置文件,本文就來(lái)介紹一下,感興趣的可以了解一下
    2024-11-11
  • SpringBoot利用validation實(shí)現(xiàn)數(shù)據(jù)校驗(yàn)完整指南

    SpringBoot利用validation實(shí)現(xiàn)數(shù)據(jù)校驗(yàn)完整指南

    Spring Boot 提供的 Validation(基于 JSR 303/380 規(guī)范)讓我們能通過(guò)注解的方式優(yōu)雅地完成參數(shù)校驗(yàn),極大地提升了開(kāi)發(fā)效率和代碼可讀性,下面我們就來(lái)看看具體實(shí)現(xiàn)吧
    2025-09-09
  • Mac安裝多個(gè)JDK并實(shí)現(xiàn)動(dòng)態(tài)切換

    Mac安裝多個(gè)JDK并實(shí)現(xiàn)動(dòng)態(tài)切換

    有時(shí)候我們有多個(gè)項(xiàng)目需要使用多個(gè)版本JDK,本文主要介紹了Mac安裝多個(gè)JDK并實(shí)現(xiàn)動(dòng)態(tài)切換,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07
  • jsoup?框架的使用小結(jié)

    jsoup?框架的使用小結(jié)

    jsoup 是一款基于Java的HTML解析器,它提供了一套非常省力的API,不但能直接解析某個(gè)URL地址、HTML文本內(nèi)容,而且還能通過(guò)類似于 DOM、CSS 或者jQuery的方法來(lái)操作數(shù)據(jù),所以jsoup也可以被當(dāng)做爬蟲工具使用,這篇文章主要介紹了jsoup使用,需要的朋友可以參考下
    2023-04-04
  • Java使用位運(yùn)算實(shí)現(xiàn)權(quán)限管理的示例詳解

    Java使用位運(yùn)算實(shí)現(xiàn)權(quán)限管理的示例詳解

    在開(kāi)發(fā)中,權(quán)限管理是一個(gè)非常常見(jiàn)的需求,本文將詳細(xì)講解如何使用 Java 中的 位運(yùn)算 實(shí)現(xiàn)一個(gè)輕量級(jí)、高效的權(quán)限管理系統(tǒng),并提供完整的代碼示例和解釋,感興趣的小伙伴可以了解下
    2025-06-06
  • Spring Boot 中密碼加密的兩種方法

    Spring Boot 中密碼加密的兩種方法

    這篇文章主要介紹了Spring Boot 中密碼加密的兩種方法,幫助大家更好的理解和使用Spring Boot框架,感興趣的朋友可以了解下
    2020-09-09
  • 使用Javacsv.jar的jar包操作csv文件的方法

    使用Javacsv.jar的jar包操作csv文件的方法

    使用javacsv的jar包很方便的可以操作csv文件。下面通過(guò)本教程給大家介紹使用javacsv.jar操作csv文件的方法,感興趣的朋友一起看下吧
    2016-08-08
  • SpringBoot實(shí)現(xiàn)異步消息處理的代碼示例

    SpringBoot實(shí)現(xiàn)異步消息處理的代碼示例

    在現(xiàn)代應(yīng)用程序中,異步消息處理是一項(xiàng)至關(guān)重要的任務(wù)。它可以提高應(yīng)用程序的性能、可伸縮性和可靠性,同時(shí)也可以提供更好的用戶體驗(yàn),本文將介紹如何使用Spring Boot實(shí)現(xiàn)異步消息處理,并提供相應(yīng)的代碼示例
    2023-06-06

最新評(píng)論

探索| 白河县| 兰州市| 离岛区| 哈巴河县| 嘉义市| 芜湖市| 外汇| 太保市| 郑州市| 宝兴县| 白朗县| 穆棱市| 湘乡市| 岳西县| 汉川市| 监利县| 襄樊市| 通州区| 九龙坡区| 竹山县| 封丘县| 交城县| 石泉县| 上高县| 黄浦区| 宜阳县| 晋江市| 伊宁县| 奉贤区| 乌鲁木齐县| 永川市| 静乐县| 定安县| 沛县| 汉沽区| 昆明市| 即墨市| 扬中市| 拉孜县| 田阳县|