Čitanje HttpServletRequest više puta u proljeće

1. Uvod

U ovom uputstvu naučit ćemo kako čitati tijelo iz HttpServletRequest više puta koristeći Spring.

HttpServletRequest je sučelje koje izlaže getInputStream () metoda za čitanje tijela. Prema zadanim postavkama, podaci iz ovoga InputStream može se pročitati samo jednom.

2. Ovisnosti Mavena

Prvo što će nam trebati je odgovarajuće proljeće-webmvc i javax.servlet ovisnosti:

 org.springframework spring-webmvc 5.2.0.Opusti javax.servlet javax.servlet-api 4.0.1 

Također, budući da koristimo aplikacija / json vrsta sadržaja, jackson-databind ovisnost je potrebna:

 com.fasterxml.jackson.core jackson-databind 2.10.0 

Spring koristi ovu knjižnicu za pretvaranje u i iz JSON-a.

3. Proljetni ContentCachingRequestWrapper

Proljeće pruža a ContentCachingRequestWrapper razred. Ova klasa pruža metodu, getContentAsByteArray () čitati tijelo više puta.

Ova klasa ipak ima ograničenja: Ne možemo čitati tijelo više puta koristeći getInputStream () i getReader () metode.

Ova klasa pohranjuje tijelo zahtjeva trošenjem datoteke InputStream. Ako čitamo InputStream u jednom od filtara, a zatim ga drugi sljedeći filtri u lancu filtara više ne mogu čitati. Zbog ovog ograničenja, ova klasa nije prikladna u svim situacijama.

Da bismo prevladali ovo ograničenje, pogledajmo sada općenitije rješenje.

4. Proširivanje HttpServletRequest

Stvorimo a nova klasa - CachedBodyHttpServletRequest - koja se proteže HttpServletRequestWrapper. Na taj način, ne trebamo nadjačati sve apstraktne metode HttpServletRequest sučelje.

HttpServletRequestWrapper razred ima dvije apstraktne metode getInputStream () i getReader (). Nadjačat ćemo obje ove metode i stvoriti novi konstruktor.

4.1. Konstruktor

Prvo, kreirajmo konstruktor. Unutar njega čitat ćemo tijelo iz stvarnog InputStream i spremite ga u bajt[] objekt:

javna klasa CachedBodyHttpServletRequest proširuje HttpServletRequestWrapper {privatni bajt [] cachedBody; javni CachedBodyHttpServletRequest (zahtjev HttpServletRequest) baca IOException {super (zahtjev); InputStream requestInputStream = request.getInputStream (); this.cachedBody = StreamUtils.copyToByteArray (requestInputStream); }}

Kao rezultat toga, moći ćemo čitati tijelo više puta.

4.2. getInputStream ()

Dalje, poništimo getInputStream () metoda. Ovu ćemo metodu koristiti za čitanje sirovog tijela i pretvaranje u objekt.

Ovom metodom ćemo stvoriti i vratiti novi objekt CachedBodyServletInputStream razred (provedba ServletInputStream):

@Override public ServletInputStream getInputStream () baca IOException {return new CachedBodyServletInputStream (this.cachedBody); }

4.3. getReader ()

Zatim ćemo poništiti getReader () metoda. Ova metoda vraća a BufferedReader objekt:

@Override public BufferedReader getReader () baca IOException {ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream (this.cachedBody); vrati novi BufferedReader (novi InputStreamReader (byteArrayInputStream)); }

5. Provedba ServletInputStream

Stvorimo a razred - CachedBodyServletInputStream - koji će provesti ServletInputStream. U ovoj ćemo klasi stvoriti novi konstruktor, kao i nadjačati gotovo je(), isReady () i čitati() metode.

5.1. Konstruktor

Prvo, kreirajmo novi konstruktor koji uzima bajtni niz.

Unutar nje stvorit ćemo novi ByteArrayInputStream instance koja koristi taj bajtni niz. Nakon toga dodijelit ćemo ga globalnoj varijabli cachedBodyInputStream:

javna klasa CachedBodyServletInputStream proširuje ServletInputStream {private InputStream cachedBodyInputStream; javni CachedBodyServletInputStream (bajt [] cachedBody) {this.cachedBodyInputStream = novi ByteArrayInputStream (cachedBody); }}

5.2. čitati()

Zatim ćemo poništiti čitati() metoda. U ovoj ćemo metodi nazvati ByteArrayInputStream # čitanje:

@Override public int read () baca IOException {return cachedBodyInputStream.read (); }

5.3. gotovo je()

Zatim ćemo poništiti gotovo je() metoda. Ova metoda pokazuje da li InputStream ima više podataka za čitanje ili ne. Vraća se pravi kada je za čitanje dostupno nula bajtova:

@Override public boolean isFinished () {return cachedBody.available () == 0; }

5.4. isReady ()

Slično tome, poništit ćemo isReady () metoda. Ova metoda pokazuje da li InputStream je li spreman za čitanje ili ne.

Budući da smo već kopirali InputStream u bajt polju, vratit ćemo se pravi da naznačite da je uvijek dostupan:

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

6. Filtar

Na kraju, stvorimo novi filtar koji će koristiti CachedBodyHttpServletRequest razred. Ovdje ćemo produžiti Proljeće OncePerRequestFilter razred. Ovaj razred ima apstraktnu metodu doFilterInternal ().

Ovom metodom ćemo stvoriti objekt CachedBodyHttpServletRequest klase iz stvarnog objekta zahtjeva:

CachedBodyHttpServletRequest cachedBodyHttpServletRequest = novo CachedBodyHttpServletRequest (zahtjev);

Onda ćemo proslijedite ovaj novi objekt omotača zahtjeva lancu filtra. Dakle, svi sljedeći pozivi na getInputStream() metoda će pozvati nadjačanu metodu:

filterChain.doFilter (cachedContentHttpServletRequest, odgovor);

7. Zaključak

U ovom uputstvu brzo smo prošli kroz ContentCachingRequestWrapper razred. Vidjeli smo i njegova ograničenja.

Zatim smo stvorili novu implementaciju HttpServletRequestWrapper razred. Nadjačali smo getInputStream () metoda za vraćanje objekta od ServletInputStream razred.

Konačno, stvorili smo novi filtar za prosljeđivanje objekta omotača zahtjeva lancu filtara. Dakle, mogli smo pročitati zahtjev više puta.

Potpuni izvorni kod primjera može se naći na GitHubu.


$config[zx-auto] not found$config[zx-overlay] not found