Soru İstek durumunu değiştirmeden http.Request'in gövdesini okumak?


Uygulayan bir türüm var http.Handler arayüzü, içinde ServeHTTP yönteminde, gelen HTTP istekleri denetlenir, bazı eylemler alınır ve istekler ters proxy işleyicisine iletilir (httputil.NewSingleHostReverseProxy).

Bu sadece, URL veya üstbilgiler gibi temel istek özelliklerini incelediğim sürece iyi çalışır.

Gelen bir POST isteğinin gövdesini incelemek istediğimde, ör. arayarak req.ParseForm() ve sonra req.Form Özellik, istek ters proxy'ye iletildikten sonra bir hatayla karşılaşıyorum:

http: proxy error: http: Request.ContentLength=687 with Body length 0

Bunun olduğunu düşünüyorum çünkü HTTP isteğinin gövdesine bakmak req.Body.Reader Akıtılacak akış, proxy işleyicisi tarafından tekrar okunamayacağı anlamına gelir.

Gibi şeyler ile oynuyordum io.Copy() ve bufio.Peek()ama ben hiçbir yere gitmiyorum.

HTTP istek gövdesine göz atmanın bir yolu var mı (ve req.ParseForm vb), orijinal istek nesnesini orijinal durumunda bırakırken, ters proxy'ye iletilebilsin mi?


32
2018-04-14 21:34


Menşei


Değiştirmeyi denediniz mi Body gibi aranabilir bir okuyucu ile bytes.Reader? Sen ayarlayabilirsiniz Body uygulayan her değere ReadCloser. - nemo
Test edilmemiş (ama denemeye değer): İstenilenin, yani Gövde değil, hepsi hariç, Talebin derin bir kopyasını oluşturun. * Form. Ardından Request.Click'i klonlamak için io.TeeReader'ı kullanın, bir NopCloser'a sarın ve isteğin derinliklerine doldurun. Derin kopyayı ters proxy'ye aktarın. - Volker


Cevaplar:


Bir arabelleği okumaya çalışın ve sonra iki tane yeni okuyucuyu kullanmak için arabellek kullanmayı deneyin. Örneğin, aşağıdaki kodu değiştirmek istediğimizi hayal edin:

doStuff(r.Body) // r is an http.Request

Yapabilirdik:

buf, _ := ioutil.ReadAll(r.Body)
rdr1 := ioutil.NopCloser(bytes.NewBuffer(buf))
rdr2 := ioutil.NopCloser(bytes.NewBuffer(buf))

doStuff(rdr1)
r.Body = rdr2 // OK since rdr2 implements the io.ReadCloser interface

// Now the program can continue oblivious to the fact that
// r.Body was ever touched.

Bunu not et *bytes.Buffer sahip değil Close() error yöntem, bu yüzden uygulamıyor io.ReadCloser arayüz. Böylece, bizim sarmak zorundayız *bytes.Buffer çağrılarda değerler ioutil.NopCloser.


55
2018-04-15 07:44



Evet, np! Ve Close imzasında iyi yakalayın. Sanırım belgeleri okumak önemlidir;) - joshlf
Alabilirsiniz io.ReadCloser kullanarak bir tampondan io.NopCloser işlev, ör. io.NopCloser(buf). Görmek golang.org/src/pkg/io/ioutil/ioutil.go?s=3623:3664#L111 - Lars Wiegman
belki bir TeeReader yararlı olurdu - danmux
Bu güzel çalışır, ancak sadece bellekte veya diskte tutabileceğiniz sınırlı bedenler için. - Joppe
drainBody içinde golang.org/src/net/http/httputil/dump.go#L26 benzer bir çözüm sunuyor ama http.NoBody hesaba katmak - mattes


Son zamanlarda daha farklı bir yaklaşım kullandım. Var GetBody Bu yöntemde, istek gövdesinin yeni bir kopyasını edinebilirsiniz.

doStuff(r.Body)

Bunun yerine yapabilirsiniz:

body, _ := r.GetBody()
doStuff(body)
// r.Body is unmodified

Bu, daha sonra işlemek için daha fazla işlem yapmaya devam ederken, istek gövdesini incelemenize olanak tanır.


0
2018-02-14 10:46



GetBody() sadece müşteri istekleri için kullanılır. Sunucu istekleri için kullanılmaz. - mattes