86 votes

Golang lire du corps de la requête

Je suis en train d'écrire mon propre logginMiddleware. En gros, j'ai besoin de vous connecter corps de la requête et la réponse. Le problème que j'ai rencontré est que quand j'ai lu corps, il devient vide et je ne peux pas le lire deux fois. Je comprends qu'il se passe, parce qu'il est de type ReadCloser. Est-il possible de rembobiner corps pour le début?

144voto

icza Points 3857

L'inspection et les moqueries du corps de la requête

Lorsque vous lisez d'abord le corps, vous devez le stocker une fois que vous avez fini avec cela, vous pouvez définir un nouveau io.ReadCloser que le corps de la requête construite à partir des données d'origine. Alors, quand on avance dans la chaîne, le gestionnaire suivant peut lire le même corps.

Une option est de lire l'ensemble du corps à l'aide d' ioutil.ReadAll(), ce qui vous donne le corps comme un octet tranche.

Vous pouvez utiliser bytes.NewBuffer() pour obtenir un io.Reader d'un octet tranche.

La dernière pièce manquante est de faire de l' io.Reader un io.ReadClosercar bytes.Buffer n'ont pas d' Close() méthode. Pour cela, vous pouvez utiliser ioutil.NopCloser() qui encapsule une io.Reader, et renvoie un io.ReadCloser, dont ajoutés Close() méthode sera un no-op (ne fait rien).

Notez que vous pouvez même modifier le contenu de l'octet tranche que vous utilisez pour créer le "nouveau" corps. Vous avez le plein contrôle sur elle.

Les soins doivent être pris même si, comme il pourrait y avoir d'autres champs HTTP comme content-length et les sommes de contrôle qui peut devenir non valide si vous modifiez uniquement les données. Si par la suite des gestionnaires de vérifier ces, vous aussi, vous avez besoin de modifier ces trop!

L'inspection / modifier le corps de la réponse

Si vous aussi vous voulez lire le corps de la réponse, alors vous devez envelopper la http.ResponseWriter que vous obtenez, et passer le wrapper sur la chaîne. Ce wrapper peut mettre en cache les données envoyées, que vous pouvez inspecter après, sur la volée (par la suite à des gestionnaires d'écrire).

Voici un simple ResponseWriter wrapper, qui met en cache les données, de sorte qu'il sera disponible après que la réparation gestionnaire retourne:

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

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

Notez que MyResponseWriter.Write() seulement écrit les données dans un tampon. Vous pouvez également choisir de l'inspecter à la volée (en Write() méthode) et écrire les données directement sur le enveloppés / embedded ResponseWriter. Vous pouvez même modifier les données. Vous avez le plein contrôle.

Les soins doivent être prises à nouveau si, par la suite les gestionnaires peuvent aussi envoyer des en-têtes de réponse HTTP liée à la réponse de données –telles que la longueur, ou sommes– qui peut également devenir invalide si vous modifiez les données de réponse.

Exemple complet

Mettre les morceaux ensemble, voici un exemple:

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

0voto

Yuri Giovani Points 21

Je pourrais utiliser l' GetBody de trousse de Demande.

Regardez ce commentaire dans le code source de la demande.allez net/http:

GetBody définit une option de func, une nouvelle copie de Corps. Il est utilisé pour les demandes des clients quand une redirection exige la lecture du corps plus d'une fois. L'utilisation de GetBody encore nécessite la mise en Corps. Pour les demandes de serveur qu'il est utilisé."

GetBody func() (io.ReadCloser, error)

De cette façon, vous pouvez obtenir le corps demande sans le rendre vide.

Exemple:

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X