Golang read request body
Piszę własne logginMiddleware. Zasadniczo, muszę zapisać treść prośby i odpowiedzi. Problem, który napotkałem jest to, że kiedy czytam ciało, staje się puste i nie mogę przeczytać go dwa razy. Rozumiem, że dzieje się tak, ponieważ jest typu ReadCloser. Czy istnieje sposób na przewinięcie ciała do początku?
2 answers
Kontrola i wyśmiewanie organu wnioskującego
Kiedy po raz pierwszy przeczytasz ciało, musisz je przechowywać, aby po jego zakończeniu można było ustawić nowy io.ReadCloser
jako organ żądania zbudowany z oryginalnych danych. Więc po przejściu w łańcuchu, następny opiekun może odczytać to samo ciało.
Jedną z opcji jest odczytanie całego ciała za pomocą ioutil.ReadAll()
, co daje Ci ciało jako kawałek bajtu.
Możesz użyć bytes.NewBuffer()
aby uzyskać io.Reader
z kawałka bajtów.
Ostatnim brakującym elementem jest zrobienie io.Reader
io.ReadCloser
, ponieważ bytes.Buffer
nie ma metody Close()
. W tym celu możesz użyć ioutil.NopCloser()
która zawija io.Reader
i zwraca io.ReadCloser
, której dodana metoda Close()
będzie no-op (nie robi nic).
Zauważ, że możesz nawet zmodyfikować zawartość plasterka bajtów, którego używasz do utworzenia" nowego " ciała. Masz nad tym pełną kontrolę.
należy jednak zachować ostrożność, ponieważ mogą istnieć inne pola HTTP, takie jak content-długość i sumy kontrolne, które mogą stać się nieważne, jeśli zmodyfikujesz tylko dane. Jeśli kolejne manipulatory sprawdzają te, będziesz musiał je również zmodyfikować!
Kontrola / modyfikacja organu odpowiedzi
Jeśli chcesz również przeczytać treść odpowiedzi, musisz zawinąć http.ResponseWriter
dostajesz i podajesz opakowanie na łańcuchu. Wrapper ten może buforować wysyłane dane, które można sprawdzić albo po, w locie (jak kolejne programy obsługi piszą do it).
Oto prosty wrapper ResponseWriter
, który po prostu buforuje dane, więc będzie dostępny po następnym zakończeniu obsługi:
type MyResponseWriter struct {
http.ResponseWriter
buf *bytes.Buffer
}
func (mrw *MyResponseWriter) Write(p []byte) (int, error) {
return mrw.buf.Write(p)
}
Zauważ, że MyResponseWriter.Write()
po prostu zapisuje dane do bufora. Możesz również sprawdzić go w locie (w metodzie Write()
) i natychmiast zapisać dane do owiniętego / osadzonego ResponseWriter
. Możesz nawet modyfikować dane. Masz pełną kontrolę.
należy jednak ponownie zachować ostrożność, ponieważ kolejne manipulatory mogą również wysyłać HTTP nagłówki odpowiedzi związane z danymi odpowiedzi - takimi jak długość lub sumy kontrolne-które również mogą stać się nieważne, jeśli zmienisz dane odpowiedzi.
Pełny przykład
Po złożeniu elementów do kupy, oto pełny przykład pracy:]}func loginmw(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Printf("Error reading body: %v", err)
http.Error(w, "can't read body", http.StatusBadRequest)
return
}
// Work / inspect body. You may even modify it!
// And now set a new body, which will simulate the same data we read:
r.Body = ioutil.NopCloser(bytes.NewBuffer(body))
// Create a response wrapper:
mrw := &MyResponseWriter{
ResponseWriter: w,
buf: &bytes.Buffer{},
}
// Call next handler, passing the response wrapper:
handler.ServeHTTP(mrw, r)
// Now inspect response, and finally send it out:
// (You can also modify it before sending it out!)
if _, err := io.Copy(w, mrw.buf); err != nil {
log.Printf("Failed to send out response: %v", err)
}
})
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-20 16:28:33
Mógłbym użyć GetBody
z pakietu Request.
Spójrz na ten komentarz w kodzie źródłowym z request.przejdź do net / http
GetBody definiuje opcjonalny func, aby zwrócić nową kopię Ciało. Jest on używany do żądań klientów, gdy przekierowanie wymaga czytanie ciała więcej niż raz. Korzystanie z GetBody still wymaga ustawienia ciała. Dla żądań serwera jest on nieużywany."
GetBody func() (io.ReadCloser, error)
W ten sposób można uzyskać żądanie ciała bez make him pusty.
Próbka:
getBody := request.GetBody
copyBody, _ := getBody()
http.DefaultClient.Do(request)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-29 14:22:26