Comment inspecter le flux de réponse MVC à l'aide du composant middleware OWIN?


Jim W dit de réintégrer Monica

Cette question a déjà été posée sous plusieurs formes, mais je ne peux obtenir aucune des réponses, je perds mes cheveux et je ne sais pas si le problème est simplement que les solutions datent d'il y a 2 ans et que les choses ont changé.

Comment puis-je intercepter en toute sécurité le flux de réponse dans un middleware Owin personnalisé - j'ai basé mon code sur cela, il semble que cela devrait fonctionner, mais cela ne fonctionne pas

OWIN OnSendingHeaders Callback - Reading Response Body - semble être une version OWIN différente, car la signature de méthode ne fonctionne pas

Ce que je veux faire, c'est écrire un OMC qui peut inspecter le flux de réponse de MVC.

Ce que j'ai fait (parmi plusieurs autres tentatives), c'est d'ajouter un OMC qui définit context.Response.Body à un MemoryStream, afin que je puisse le rembobiner et inspecter ce qui a été écrit par les composants en aval:

    public async Task Invoke(IDictionary<string, object> env)
    {

        IOwinContext context = new OwinContext(env);

        // Buffer the response
        var stream = context.Response.Body;
        var buffer = new MemoryStream();
        context.Response.Body = buffer;
        .......

Ce que je trouve, c'est que le MemoryStream est toujours vide, sauf si je lui écris depuis un autre OMC. Il semble donc que les OMC en aval utilisent mon MemoryStream, mais les réponses MVC ne le sont pas, comme si le pipeline OWIN se terminait avant que la demande ne soit envoyée à MVC, mais ce n'est pas vrai, n'est-ce pas?

Code complet:

public partial class Startup
{
    public void Configuration(IAppBuilder app)
    {
        ConfigureAuth(app);
        app.Use(new ResponseExaminerMiddleware());

        // Specify the stage for the OMC
        //app.UseStageMarker(PipelineStage.Authenticate);
    }


}




public class ResponseExaminerMiddleware
{
    private AppFunc next;

    public void Initialize(AppFunc next)
    {
        this.next = next;
    }

    public async Task Invoke(IDictionary<string, object> env)
    {

        IOwinContext context = new OwinContext(env);

        // Buffer the response
        var stream = context.Response.Body;
        var buffer = new MemoryStream();
        context.Response.Body = buffer;

        await this.next(env);

        buffer.Seek(0, SeekOrigin.Begin);
        var reader = new StreamReader(buffer);
        string responseBody = await reader.ReadToEndAsync();

        // Now, you can access response body.
        System.Diagnostics.Debug.WriteLine(responseBody);

        // You need to do this so that the response we buffered
        // is flushed out to the client application.
        buffer.Seek(0, SeekOrigin.Begin);
        await buffer.CopyToAsync(stream);

    }

}

Pour ce que cela vaut, j'ai également essayé une suggestion où le flux response.Body est défini sur une sous-classe Stream, juste pour que je puisse surveiller ce qui est écrit dans le flux et bizarrement la méthode Stream.Write est appelée, mais avec un tableau d'octets vide, jamais de contenu réel ...

MadBender

MVC ne transmet pas sa demande via le pipeline OWIN. Pour capturer la réponse MVC, nous devons créer un filtre de réponse personnalisé qui capture les données de réponse

/// <summary>
/// Stream capturing the data going to another stream
/// </summary>
internal class OutputCaptureStream : Stream
{
    private Stream InnerStream;
    public MemoryStream CapturedData { get; private set; }

    public OutputCaptureStream(Stream inner)
    {
        InnerStream = inner;
        CapturedData = new MemoryStream();
    }

    public override bool CanRead
    {
        get { return InnerStream.CanRead; }
    }

    public override bool CanSeek
    {
        get { return InnerStream.CanSeek; }
    }

    public override bool CanWrite
    {
        get { return InnerStream.CanWrite; }
    }

    public override void Flush()
    {
        InnerStream.Flush();
    }

    public override long Length
    {
        get { return InnerStream.Length; }
    }

    public override long Position
    {
        get { return InnerStream.Position; }
        set { CapturedData.Position = InnerStream.Position = value; }
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        return InnerStream.Read(buffer, offset, count);
    }

    public override long Seek(long offset, SeekOrigin origin)
    {
        CapturedData.Seek(offset, origin);
        return InnerStream.Seek(offset, origin);
    }

    public override void SetLength(long value)
    {
        CapturedData.SetLength(value);
        InnerStream.SetLength(value);
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        CapturedData.Write(buffer, offset, count);
        InnerStream.Write(buffer, offset, count);
    }
}

Et puis nous créons un middleware de journalisation qui peut enregistrer correctement les deux types de réponses

public class LoggerMiddleware : OwinMiddleware
{
    public LoggerMiddleware(OwinMiddleware next): base(next)
    {
    }

    public async override Task Invoke(IOwinContext context)
    {
        //to intercept MVC responses, because they don't go through OWIN
        HttpResponse httpResponse = HttpContext.Current.Response;
        OutputCaptureStream outputCapture = new OutputCaptureStream(httpResponse.Filter);
        httpResponse.Filter = outputCapture;

        IOwinResponse owinResponse = context.Response;
        //buffer the response stream in order to intercept downstream writes
        Stream owinResponseStream = owinResponse.Body;
        owinResponse.Body = new MemoryStream();

        await Next.Invoke(context);

        if (outputCapture.CapturedData.Length == 0) {
            //response is formed by OWIN
            //make sure the response we buffered is flushed to the client
            owinResponse.Body.Position = 0;
            await owinResponse.Body.CopyToAsync(owinResponseStream);
        } else {   
            //response by MVC
            //write captured data to response body as if it was written by OWIN         
            outputCapture.CapturedData.Position = 0;
            outputCapture.CapturedData.CopyTo(owinResponse.Body);
        }

        LogResponse(owinResponse);
    }
}

Articles connexes


Inspecter le certificat du serveur à l'aide de HttpClient

LordChariot Je réécris du code de gestion Web dans WinForms et je passe de HttpWebRequest à HttpClient. Il y a une dernière chose dont j'ai besoin que je n'arrive pas à trouver comment l'accomplir. Dans HttpWebRequest, je peux capturer le certificat du serveur

Comment intercepter 404 à l'aide du middleware Owin

moribvndvs Contexte Laissez-moi d'abord vous expliquer le contexte. Je travaille sur un projet qui tente de marier un serveur d'arrière-plan qui utilise l'API Web configurée via OWIN - hébergé sur IIS maintenant, mais potentiellement d'autres hôtes pris en cha