ServletRequest中的getReader() 和getInputStream()只能调用一次的问题探究

起因:

在项目中做对外接口API防重设计的时候,发现项目有个类 XXXRequestWrapper ,这个类继承 HttpServletRequestWrapper 并重写了 getInputStream()getReader()方法,代码示例如下:

public class XXXRequestWrapper extends HttpServletRequestWrapper{

    //省略constrcutor以及自定义filed

    @Override
    public BufferedReader getReader() {
        return new BufferedReader(new InputStreamReader(getInputStream(), StandardCharsets.UTF_8));
  }

   @Override
    public ServletInputStream getInputStream() {
        //省略实现代码

}

项目中自定义的Filter中都会把 servletRequest转化为 该warpper,让我十分不解,一度认为这是脱裤子放屁,多此一举的行为。然鹅,最后被打脸的确是我自己……

一探:

经过搜索后得知,因为ServletRequest中的getInputStream()/getReader()只能被调用一次 而一个项目中肯定存在多处需要调用该方法获取流中数据的场景,所以需要自定义一个XXXWrapper类 并重写getReader()getInputStream()方法,将流中数据保存并向下传递(其他Filter).伪代码如下:

public class CustomizeFilter implements Filter{
    @Override  
    public void doFilter(ServletRequest request, ServletResponse response,  
            FilterChain chain) throws IOException, ServletException {  

        ServletRequest requestWrapper = null;    
        if(request instanceof HttpServletRequest) {  
            HttpServletRequest httpServletRequest = (HttpServletRequest) request;
            requestWrapper = new XXXWrapper(request)
        }

        if(requestWrapper == null) {    
            chain.doFilter(request, response);    
        } else {    
            chain.doFilter(requestWrapper, response);  //将requet 替换requestWrapper向下传递
        }    
    }  
}

此种做法可以避免之前提到的流只能被读取一次的问题。但是仍然有个问题困扰着我,为什么只能被读取一次?这个问题,我搜了下,其实也有不少人研究过,有些给的答案很形象,你可以把流中的数据当作一瓢水,你调用getInputStream方法就相当于从瓢中取水,取了一次以后,自然就没有了。坦白说,看到这个解释的时候,我恍然大悟好像明白了,又好像什么都没明白。因为,这个答案没有从根本上说明答案。

二探:

ServletRequest中的getInputStream()为什么只能调用一次? 这个问题还是让我有些疑惑,我继续寻找答案,终于,一番努力(百度)下 ,找到了想要的答案。

其实这个问题最终归于I/O的读取,我们可以先看下ServletRequest的getInputStream()是怎样的,源码如下:

    /**
     * 省略
     */
    public ServletInputStream getInputStream() throws IOException;

其返回值为ServletInputStreamServletInputStream又是怎样的呢?

public abstract class ServletInputStream extends InputStream {

    /**
     * Does nothing, because this is an abstract class.
     */
    protected ServletInputStream() {
        // NOOP
    }
    //省略其余部分
}

由此我们可以看到 ServletInputStream其实是 InputStream的子类,而且该方法的实现上来看,

getInputStream() 方法 最终是由RequestFacade类实现的,其实现源码为:

    @Override
    public ServletInputStream getInputStream() throws IOException {

        if (request == null) {
            throw new IllegalStateException(
                            sm.getString("requestFacade.nullRequest"));
        }

        return request.getInputStream();
    }

其中的 request.getInputStream() 则是由 Request实现的:

    @Override
    public ServletInputStream getInputStream() throws IOException {

        if (usingReader) {
            throw new IllegalStateException(sm.getString("coyoteRequest.getInputStream.ise"));
        }

        usingInputStream = true;
        if (inputStream == null) {
            inputStream = new CoyoteInputStream(inputBuffer);
        }
        return inputStream;

    }

再往下探究其实已经没必要了,因为ServletRequestgetInputStream()返回的为InputStream的子类,而我们在读取该流的时候(即在Filter中获取request中的参数时),其使用的是InputStreamread方法.

对于InputStreamread方法:

  /**
     * Reads up to <code>len</code> bytes of data from the input stream into
     * an array of bytes.  An attempt is made to read as many as
     * <code>len</code> bytes, but a smaller number may be read.
     * The number of bytes actually read is returned as an integer.
     *
     * <p> This method blocks until input data is available, end of file is
     * detected, or an exception is thrown.
     *
     * <p> If <code>len</code> is zero, then no bytes are read and
     * <code>0</code> is returned; otherwise, there is an attempt to read at
     * least one byte. If no byte is available because the stream is at end of
     * file, the value <code>-1</code> is returned; otherwise, at least one
     * byte is read and stored into <code>b</code>.
     **/
public int read(byte b[], int off, int len) throws IOException {
    //省略实现
}

关于read()方法,通俗的讲,就是每次读取都会记录该位置pos,下次读取,则从该pos+1的位置读取流中数据,直到读取到文件或者流的结束位置返回-1,而我们在Filter中调用getInputStream()则是相当于将流中数据全部读取了,使得pos 指向文件/流末尾,再次

读取自然不能获取到流中数据,聊到这,我估计有些人会有跟我一样的想法,既然是pos指针问题,那重置该pos的值不就可以解决问题了嘛,是的 , InputStream提供了reset()方法用来重置pos指针,然鹅,然鹅,然鹅,我们还是看下源码吧。

    public synchronized void reset() throws IOException {
        throw new IOException("mark/reset not supported");
    }

直接抛出IO异常,查了下资料,有解释说,Java期望有需要重写该方法的子类,自己去重写该方法,只需要遵循方法合约(可自行查看该方法说明)即可。

在其子类里翻了 下,确实有重写了该方法的,如 BufferedInputStream

    public synchronized void reset() throws IOException {
        getBufIfOpen(); // Cause exception if closed
        if (markpos < 0)
            throw new IOException("Resetting to invalid mark");
        pos = markpos;
    }

那么我们上文提到的 ServletInputStream呢 ,很不巧,其并未重写该方法。

所以,这也就导致我们想要在读取完之后重置pos的想法落空,只能另辟蹊径,采用文章开头介绍的方式了。

三探:

关于I/O的流读取问题,其实也做一些测试,但是发掘不如网上的文章说的详细,这里也懒得赘述了,链接在此,有兴趣的可以去看下。

关于 “ServletRequest中的getReader() 和getInputStream()只能调用一次的问题探究” 的 31,795 个意见

  1. MariaNus649

    Xevil5.0自动解决大多数类型的captchas,
    包括这类验证码: ReCaptcha v.2, ReCaptcha v.3, Hotmail (Microsoft), Google captcha, SolveMedia, BitcoinFaucet, Steam, +12000
    + hCaptcha 支持新的Xevil6.0! 只需在YouTube中搜索XEvil6.0

    有兴趣吗? 只是谷歌XEvil 5.0
    P.S. 免费XEvil演示可用 !!!

    此外,还有一个巨大的折扣可供购买,直到6月10日(最后一天!!!): -30%!

    XEvil.Net

  2. MariaNus263

    Xevil5.0自动解决大多数类型的captchas,
    包括这类验证码: ReCaptcha-2, ReCaptcha v.3, Hotmail (Microsoft), Google captcha, SolveMedia, BitcoinFaucet, Steam, +12k
    + hCaptcha 支持新的Xevil6.0! 只需在YouTube中搜索XEvil6.0

    有兴趣吗? 只是谷歌XEvil 5.0.15
    P.S. 免费XEvil演示可用 !

    此外,还有一个巨大的折扣可供购买,直到6月10日(最后一天!!!): -30%!

    XEvil.Net

  3. MariaNus074

    Xevil5.0自动解决大多数类型的captchas,
    包括这类验证码: ReCaptcha-2, ReCaptcha-3, Hotmail, Google captcha, SolveMedia, BitcoinFaucet, Steam, +12000
    + hCaptcha 支持新的Xevil6.0! 只需在YouTube中搜索XEvil6.0

    有兴趣吗? 只是谷歌XEvil 6.0
    P.S. 免费XEvil演示可用 !!!

    此外,还有一个巨大的折扣可供购买,直到6月10日(最后一天!!!): -30%!

    XEvil.Net

  4. RitakigH139

    Xevil5.0自动解决大多数类型的captchas,
    包括这类验证码: ReCaptcha v.2, ReCaptcha-3, Hotmail, Google, Solve Media, BitcoinFaucet, Steam, +12000
    + hCaptcha 支持新的Xevil6.0! 只需在YouTube中搜索XEvil6.0

    有兴趣吗? 只是谷歌XEvil 5.0
    P.S. 免费XEvil演示可用 !!!

    此外,还有一个巨大的折扣可供购买,直到6月20日: -30%!

    XEvil.Net

    查看YouTube中的新视频:
    “XEvil 6.0 1] + XRumer multhithreading hCaptcha test”

  5. ElenaZepe

    Нellо аll, guуѕǃ Ι know, my mеsѕage maу bе tоо sрecіfіс,
    But mу ѕіѕter found nісе mаn here аnd they marriеd, sо hоw аbоut mе?! 🙂
    Ι аm 25 уearѕ old, Εlеna, frоm Rоmanіa, I knоw Еnglіѕh and German lаnguagеѕ аlѕo
    And… I hаve sреcifіc diseаѕе, namеd nymрhomania. Ԝhо knоw whаt іs this, can undеrѕtаnd me (bеttеr tо ѕaу it іmmеdіаtelу)
    Аh уes, Ι сoоk verу taѕtу! аnd I love nоt only сook ;))
    Ιm real girl, nоt prоstіtutе, and lookіng for sеriouѕ аnd hоt relаtіоnshiр…
    Anywау, уou сan fіnd my profilе hеre: http://oommorheartdidetpadd.tk/user/16747/

  6. whimbgem

    Хорошего дня.
    Посоветуйте путнюю онлайн-типографию для изготовления журналов
    Могу посоветовать хорошую типографию, качество, цены и скорость у них хорошее,
    но они находятся в Красноярске, а мне хотелось бы в Казани.
    Вот тут печать и изготовление календарей https://4uprint.ru/kalendari

  7. AntonioAcali

    Auction.ru располагает этот уютный проецирование что-что тоже все сверх вывода условия раз-два мишенью продуктивной деятельность, эквивалентно яко отдельным лавочникам, этим иконой а также ярыга интернет-торговым фокусам хором кот механической загрузкой продуктов в течение веб-сайт. При нас действует энергообслуживание «Auction шерстепоставка, плата в веб-сайте что-что также гарантийное обеспечение возврата денежных лекарственное средство», беспрерывная экономотдел подмоги юзеров, что-что сверх того немало практических настроек не без; мишенью торговель а тоже покупок.
    Подробне о аукционы спецтехники – https://prom.auction.ru/

  8. WilliamVuh

    SOME DETAILS ON PREMIUM ACCOUNT: http://www.0dayvinyls.org/premium.php
    * Reseller payment method: Bitcoin, Neteller, Skrill, Webmoney, Perfect Money
    * Server’s capacity: 230 TB for MP3, FLAC, Music Clips.
    * Support: FTP, FTPS (File Transfer Protocol Secure), SFTP and HTTP, HTTPS.
    * Overal server’s speed: 1GB/s.
    * Easy to use: Most of genres are sorted by days.

    Best regards, Scene Releases Team.

  9. doPStuts

    Здравствуйте.
    Посоветуйте хорошую типографию для изготовления листовок
    Мы работали с одной типографией, качество, цены и скорость у них хорошее,
    но они размещаются в Красноярске, а мне хотелось бы в Казани.
    Вот например печать плакатов https://4uprint.ru/plakaty

  10. Terrychoom

    Also known as carbon scrubbers for their ability to get contaminants out of the air, these employ activated and highly ionized carbon to attract particulates responsible for carrying odor, such as dust, hair, mold spores, and volatile organic compounds, and traps them in a filter. Our guide also includes a list of the best CBD oil in Utah for 2021. So when you are ready to get going and start to sell our CBD products just let us know and we will be happy to help you. https://marijuanasaveslives.org/xxx-420-seeds/

  11. StephenGok

    Charger, e Power Bank For Smartphones, Light Power Extender, Mobile Phone Charger, Travel Gifts Mobile Phone Charger, Travel Gifts, AA USB Battery Pack,Portable Power Bank For Smartphones, Light Power Extender, Mobile Phone Charger, Travel Gifts

  12. Lampallric

    Доброго вечера, форумчане!
    Курю больше 9 лет и вот принял решение убавить потребление сигарет. Денег слишком много тратится. Знакомый дал совет самокруток из натурального табака. Он и стоит дешевле, и по качеству, говорят, лучше, по накуриваемости и количеству штук в день тоже меньше уходит.
    Нашел вот такой сайт поставщика, который предлагает https://lampa-lounge.ru – табак для сигарет>>> и хотел узнать, может кто то из вас уже сотрудничал здесь товарищи? Как сроки? Какой табак?
    Боюсь просто брать неизвестно у кого по предоплате и еще неизвестно который вид табака.

  13. Bruceger

    Binary options exude a confess traders profit from price fluctuations in multiple pandemic markets, but it’s prominent to discern the risks and rewards of these controversial and often-misunderstood fiscal instruments. Binary options influence confirm teeny-weeny coincidence to usual options, featuring different payouts, fees, and risks, as effectively as a unmatched liquidity order and investment process https://corretoras-opcoes-binarias.com/

  14. EddieCic

    Кто ваши конкуренты ? Есть ли такие, о которых вы не знаете или знаете но не владеете информацией о том на какую сумму они рекламируются и какие именно объявления размещают?
    Все это можно узнать за пару минут вот по этой ссылке
    Эта инструкция поможет не только прояснить ситуацию в платной контекстной рекламе, но и то, по каким запросам ваши конкуренты успешно продвигаются в SEO.
    Вы сможете получить список этих запросов по каждому из своих конкурентов https://www.youtube.com/watch?v=TnIsoSSKkwE
    Этот метод можно применить абсолютно к любому сайту, а на проверку уйдет несколько секунд, даже если вы не дружите с интернетом!

  15. Thomasunulp

    Жителю США (штат Кентуки) возместили 450 000 долларов за то, что его компания устроила ему внезапный праздник по случаю дня рождения, хотя он предупреждал всех о том, что такое вызовет у него всплеск тревоги.
    Истец, Кевин Берлинг, утверждает, что данная вечеринка в 2019 году в корпорации Gravity Diagnostics стала причиной возникновения серии приступов панической атаки у него.
    Благодаря иску, поданному в Кентуки, г-н Берлинг, страдающий тревожными расстройствами, неоднократно просил своего начальника не не устраивать никакого празднования в офисе, как это всегда проводится для коллег, так как это приведет к расстройствам психики и вызвать неприятные детские воспоминания.
    Но несмотря на это, компания, проводящая тесты Covid-19, все таки сделала внезапное празднование в августе 2019 года, что послужило поводом для приступа панической атаки. Он быстро покинул вечеринку и закончил свой обед в машине.
    В августе Gravity Diagnostics уволила Берлинга, сославшись на то, что они встревожены и обеспокоены безопасностью сотрудников, работающих с ним. В его иске утверждалось, что компания дискриминировала его из-за инвалидности и несправедливо отомстила ему за то, что он попросил удовлетворить его просьбу.
    После двухдневного судебного разбирательства в конце марта суд присудил ему $450 000, включая $300 000 за расстройство и $150 000 в виде потерянной заработной платы.
    Статистики альянса по психическим заболеваниям говорят о том, что более 40 млн Американцев – а это почти 20% всей америки – мучаются от таких расстройств.
    Новость опубликовал медиахолдинг rctorg.top

  16. StephenGok

    The world’s first dimensionless bluetooth rings https://project-br.com/ With our rings, you can forget about the feeling of discomfort or fear that you will not guess the right size! Also,our rings can be worn on the glove and as a clip on the ears without any problems – gadgets, technology