it-swarm.com.de

Golang las Anfragekörper

Ich schreibe meine eigene logginMiddleware. Grundsätzlich muss ich den Hauptteil der Anfrage und der Antwort protokollieren. Das Problem, dem ich gegenüberstand, ist, dass ich, wenn ich Körper lese, leer wird und ich es nicht zweimal lesen kann. Ich verstehe, dass es passiert, weil es vom Typ ReadCloser ist. Gibt es eine Möglichkeit, den Körper an den Anfang zurückzuspulen? 

34

Antragskörper prüfen und verspotten

Wenn Sie den Body zum ersten Mal lesen, müssen Sie ihn speichern. Wenn Sie damit fertig sind, können Sie einen neuen io.ReadCloser als Anfragetext festlegen, der aus den ursprünglichen Daten erstellt wird. Wenn Sie also in der Kette vorrücken, kann der nächste Handler denselben Körper lesen.

Eine Möglichkeit ist, den gesamten Körper mit ioutil.ReadAll() zu lesen, wodurch der Körper als Byte-Slice angezeigt wird.

Sie können bytes.NewBuffer() verwenden, um einen io.Reader aus einem Byte-Slice zu erhalten.

Das letzte fehlende Stück besteht darin, aus io.Reader einen io.ReadCloser zu machen, da bytes.Buffer keine Close()-Methode hat. Dazu können Sie ioutil.NopCloser() verwenden, das einen io.Reader umschließt und einen io.ReadCloser zurückgibt, dessen hinzugefügte Close()-Methode eine no-op ist (nichts tut).

Beachten Sie, dass Sie möglicherweise sogar den Inhalt des Byte-Slice ändern, mit dem Sie den "neuen" Body erstellen. Sie haben die volle Kontrolle darüber.

Es ist jedoch Vorsicht geboten, da es andere HTTP-Felder wie Inhaltslänge und Prüfsummen geben kann, die ungültig werden können, wenn Sie nur die Daten ändern. Wenn nachfolgende Handler diese überprüfen, müssen Sie auch diese ändern!

Antwortkörper prüfen/ändern

Wenn Sie auch den Antworttext lesen möchten, müssen Sie den http.ResponseWriter einschließen, den Sie erhalten, und den Wrapper an die Kette übergeben. Dieser Wrapper kann die gesendeten Daten zwischenspeichern, die Sie nach dem On-the-Fly (während die nachfolgenden Handler darauf schreiben) entweder überprüfen können.

Hier ist ein einfacher ResponseWriter-Wrapper, der die Daten nur zwischenspeichert, so dass sie nach der Rückkehr des nachfolgenden Handlers verfügbar sind:

type MyResponseWriter struct {
    http.ResponseWriter
    buf *bytes.Buffer
}

func (mrw *MyResponseWriter) Write(p []byte) (int, error) {
    return mrw.buf.Write(p)
}

Beachten Sie, dass MyResponseWriter.Write() die Daten nur in einen Puffer schreibt. Sie können sich auch dafür entscheiden, die Datei "on-the-fly" (in der Write()-Methode) zu untersuchen und die Daten sofort in die umschlossene/eingebettete ResponseWriter zu schreiben. Sie können die Daten sogar ändern. Sie haben die volle Kontrolle.

Es ist jedoch erneut Vorsicht geboten, da die nachfolgenden Handler auch HTTP-Antwortheader senden können, die sich auf die Antwortdaten beziehen (wie Länge oder Prüfsummen), die ebenfalls ungültig werden können, wenn Sie die Antwortdaten ändern.

Vollständiges Beispiel

Wenn Sie die Teile zusammenfügen, finden Sie hier ein vollständiges Arbeitsbeispiel:

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)
        }
    })
}
58
icza

Ich könnte das Paket "GetBody from Request" verwenden.

Schauen Sie sich diesen Kommentar im Quellcode von request.go in net/http an:

GetBody definiert eine optionale Funktion, um eine neue Kopie von Body zurückzugeben. Es wird für Clientanforderungen verwendet, wenn für eine Umleitung der Text mehr als einmal gelesen werden muss. Die Verwendung von GetBody erfordert weiterhin das Festlegen von Body. Für Serveranforderungen wird es nicht verwendet. "

GetBody func() (io.ReadCloser, error)

Auf diese Weise können Sie die Textanforderung abrufen, ohne sie leer zu machen.

Stichprobe:

getBody := request.GetBody
copyBody, err := getBody()
if err != nil {
    // Do something return err
}
http.DefaultClient.Do(request)
1
Yuri Giovani