倒带请求正文流

时间:2014-02-15 23:53:27

标签: asp.net .net asp.net-web-api owin owin-middleware

我正在重新实现一个请求记录器作为Owin Middleware,它记录了所有传入请求的请求URL和正文。 我能够读取正文,但如果我在控制器中执行body参数为null。

我猜它是空的,因为流位置在末尾,因此在尝试反序列化主体时没有任何东西可以读取。我在以前版本的Web API中遇到了类似的问题,但是能够将Stream位置设置回0.这个特定的流会引发This stream does not support seek operations异常。

在最新版本的Web API 2.0中,我可以在我的请求记录器中调用Request.HttpContent.ReadAsStringAsync(),并且正文仍然可以直接到达控制器。

如何在阅读后回放信息流?

如何在不消费的情况下阅读请求正文?

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

    public override Task Invoke(IOwinContext context)
    {
        return Task.Run(() => {
            string body = new StreamReader(context.Request.Body).ReadToEnd();
            // log body

            context.Request.Body.Position = 0; // cannot set stream position back to 0
            Console.WriteLine(context.Request.Body.CanSeek); // prints false
            this.Next.Invoke(context);
        });
    }
}

public class SampleController : ApiController 
{
    public void Post(ModelClass body)
    {
        // body is now null if the middleware reads it
    }
}

3 个答案:

答案 0 :(得分:37)

刚刚找到一个解决方案。使用包含数据的新流替换原始流。

    public override Task Invoke(IOwinContext context)
    {
        return Task.Run(() => {
            string body = new StreamReader(context.Request.Body).ReadToEnd();
            // log body

            byte[] requestData = Encoding.UTF8.GetBytes(body);
            context.Request.Body = new MemoryStream(requestData);
            this.Next.Invoke(context);
        });
    }

如果您正在处理大量数据,我确信FileStream也可以替代。

答案 1 :(得分:10)

这里是Despertar对第一个答案的一个小改进,这对我有很大帮助,但在处理二进制数据时遇到了一个问题。将流提取到的中间步骤一个字符串然后使用Encoding.UTF8.GetBytes(body)将其放回到字节数组中会混淆二进制内容(除非是UTF8编码的字符串,否则内容将会更改)。这是我使用Stream.CopyTo()

的解决方法
    public override async Task Invoke(IOwinContext context)
    {
        // read out body (wait for all bytes)
        using (var streamCopy = new MemoryStream())
        {
            context.Request.Body.CopyTo(streamCopy);
            streamCopy.Position = 0; // rewind

            string body = new StreamReader(streamCopy).ReadToEnd();
            // log body

            streamCopy.Position = 0; // rewind again
            context.Request.Body = streamCopy; // put back in place for downstream handlers

            await this.Next.Invoke(context);
        }
    }

此外,MemoryStream很不错,因为您可以在记录完整正文之前检查流的长度(如果有人上传大文件,这是我不想做的事情)。

答案 2 :(得分:-3)

我知道这是旧的,但只是为了帮助遇到这种情况的人。 您需要在流媒体上寻找:context.Request.Body.Seek(0, SeekOrigin.Begin);