web-dev-qa-db-fra.com

Lire le corps de la demande deux fois

J'essaie de lire le corps dans un middleware à des fins d'authentification, mais lorsque la demande parvient au contrôleur api, l'objet est vide car le corps a déjà été lu. Y at-il de toute façon autour de cela. Je lis le corps comme ça dans mon middleware.

var buffer = new byte[ Convert.ToInt32( context.Request.ContentLength ) ];
await context.Request.Body.ReadAsync( buffer, 0, buffer.Length );
var body = Encoding.UTF8.GetString( buffer );
20
Jake Rote

Si vous utilisez application/x-www-form-urlencoded Ou multipart/form-data, Vous pouvez appeler en toute sécurité context.Request.ReadFormAsync() plusieurs fois car il renvoie une instance mise en cache lors des appels suivants.

Si vous utilisez un type de contenu différent, vous devrez tamponner manuellement la demande et remplacer le corps de la demande par un flux rembobinable comme MemoryStream. Voici comment vous pourriez faire en utilisant un middleware en ligne (vous devez l'enregistrer bientôt dans votre pipeline):

app.Use(next => async context => {
    // Keep the original stream in a separate
    // variable to restore it later if necessary.
    var stream = context.Request.Body;

    // Optimization: don't buffer the request if
    // there was no stream or if it is rewindable.
    if (stream == Stream.Null || stream.CanSeek) {
        await next(context);

        return;
    }

    try {
        using (var buffer = new MemoryStream()) {
            // Copy the request stream to the memory stream.
            await stream.CopyToAsync(buffer);

            // Rewind the memory stream.
            buffer.Position = 0L;

            // Replace the request stream by the memory stream.
            context.Request.Body = buffer;

            // Invoke the rest of the pipeline.
            await next(context);
        }
    }

    finally {
        // Restore the original stream.
        context.Request.Body = stream;
    }
});

Vous pouvez également utiliser l'extension BufferingHelper.EnableRewind(), qui fait partie du package Microsoft.AspNet.Http: Elle est basée sur une approche similaire mais s'appuie sur un flux spécial qui commence la mise en mémoire tampon des données en mémoire et spoule tout en un fichier temporaire sur le disque lorsque le seuil est atteint:

app.Use(next => context => {
    context.Request.EnableRewind();

    return next(context);
});

Pour info: un middleware tampon sera probablement ajouté à vNext à l'avenir.

31
Pinpoint

Utilisation de la mention par PinPoint de EnableRewind

Startup.cs
using Microsoft.AspNetCore.Http.Internal;

Startup.Configure(...){
...
//Its important the rewind us added before UseMvc
app.Use(next => context => { context.Request.EnableRewind(); return next(context); });
app.UseMvc()
...
}

Ensuite, dans votre middleware, il vous suffit de rembobiner et de relire

private async Task GenerateToken(HttpContext context)
    {
     context.Request.EnableRewind();
     string jsonData = new StreamReader(context.Request.Body).ReadToEnd();
    ...
    }
8
Nathan Zaetta

Cela fonctionne avec .Net Core 2.1 et supérieur.

Aujourd'hui, je lance dans un problème similaire. En bref, ce qui fonctionnait avec

Body.Seek(0, SeekOrigin.Begin);

a abouti à aujourd'hui en exception, du moins dans mon cas. Cela s'est produit après la migration du code vers la dernière version de .NET Core.

La solution de contournement pour moi était d'ajouter ceci:

app.Use(next => context => { context.Request.EnableBuffering(); return next(context);

Ajoutez ceci avant de configurer des contrôleurs ou MVC. Cela semble être ajouté dans le cadre de la version .NET Core 2.1.

J'espère que cela aide quelqu'un!

Acclamations et codage heureux.

0
DasSoftware