如何检索当前响应体长?

ycr*_*lle 7 asp.net-core

我需要检索响应体长度.

当我查看https://github.com/aspnet/HttpAbstractions/wiki/Rolling-Notes-Response-Stream-Contract时,据说:

Stream.Position {get}和Stream.Length {get}返回写入的累积字节数

这正是我所需要的,但httpContext.Response.Body.Length提出NotSupportedException并说"流不可寻找".

我应该使用委托流来计算每次写入的字节数吗?

Ger*_*vis 5

我假设您正在尝试从中间件中获取 ContentLength ?

这是一个中间件示例。应在任何响应生成中间件(例如 useMVC 或 useStaticFiles)之前将其添加到管道 (startup.cs)。

public class ContentLengthMiddleware
{
    RequestDelegate _next;

    public ContentLengthMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        using (var buffer = new MemoryStream())
        {
            var request = context.Request;
            var response = context.Response;

            var bodyStream = response.Body;
            response.Body = buffer;

            await _next(context);
            Debug.WriteLine($"{request.Path} ({response.ContentType}) Content-Length: {response.ContentLength ?? buffer.Length}");
            buffer.Position = 0;
            await buffer.CopyToAsync(bodyStream);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

由于超出我理解的原因,当返回静态文件(png、js 等)时,响应正文将为空,但是设置了 ContentLength,这就是我使用response.ContentLength ?? buffer.Length.

(mod注意:对于两个问题的重复答案感到抱歉。另一个答案被错误地发布,打开了太多选项卡。我删除了它并在此处重新发布了答案)。

  • 该解决方案的缺点是使用了“MemoryStream”。它无用地消耗内存。 (2认同)
  • 我已经重写了“Stream”,并且只在每次写入时进行计数,如下所示: public override void Write(byte[] buffer, int offset, int count) { _tracker.ContentLength += count - offset; _inner.Write(缓冲区、偏移量、计数); } (2认同)

ycr*_*lle 1

这是在流写入期间跟踪内容长度的代码。该类ContentLengthTracker旨在在其他类之间共享内容长度值。代码发布在https://github.com/ycrumeyrolle/Throtdling/blob/master/src/Throtdling/Internal/ContentLengthTrackingStream.cs

using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

public class ContentLengthTracker
{
    public long ContentLength { get; set; }
}

public class ContentLengthTrackingStream : Stream
{
    private readonly Stream _inner;
    private readonly ContentLengthTracker _tracker;

    public ContentLengthTrackingStream(Stream inner, ContentLengthTracker tracker)
    {
        if (inner == null)
        {
            throw new ArgumentNullException(nameof(inner));
        }

        if (tracker == null)
        {
            throw new ArgumentNullException(nameof(tracker));
        }

        _inner = inner;
        _tracker = tracker;
    }

    public override bool CanRead
        => _inner.CanRead;

    public override bool CanSeek
        => _inner.CanSeek;

    public override bool CanWrite
        => _inner.CanWrite;

    public override long Length
        => _inner.Length;

    public override long Position
    {
        get => _inner.Position;
        set => _inner.Position = value;
    }

    public override bool CanTimeout
        => _inner.CanTimeout;

    public override int ReadTimeout
    {
        get => _inner.ReadTimeout;
        set => _inner.ReadTimeout = value;
    }

    public override int WriteTimeout
    {
        get => _inner.WriteTimeout;
        set => _inner.WriteTimeout = value;
    }

    public ContentLengthTracker Tracker
        => _tracker;

    public override void Flush()
        => _inner.Flush();

    public override Task FlushAsync(CancellationToken cancellationToken)
        => _inner.FlushAsync(cancellationToken);

    public override int Read(byte[] buffer, int offset, int count)
        => _inner.Read(buffer, offset, count);

    public async override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
        => await _inner.ReadAsync(buffer, offset, count, cancellationToken);

    public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
        => _inner.BeginRead(buffer, offset, count, callback, state);

    public override int EndRead(IAsyncResult asyncResult)
    {
        Task<int> task = asyncResult as Task<int>;
        if (task != null)
        {
            return task.GetAwaiter().GetResult();
        }

        return _inner.EndRead(asyncResult);
    }

    public override long Seek(long offset, SeekOrigin origin)
        => _inner.Seek(offset, origin);

    public override void SetLength(long value)
       => _inner.SetLength(value);

    public override void Write(byte[] buffer, int offset, int count)
    {
        _tracker.ContentLength += count - offset;
        _inner.Write(buffer, offset, count);
    }

    public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
    {
        // _tracker.ContentLength += count - offset; // This is incorrect
        _tracker.ContentLength += count;
        return _inner.BeginWrite(buffer, offset, count, callback, state);
    }

    public override void EndWrite(IAsyncResult asyncResult)
        => _inner.EndWrite(asyncResult);

    public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
    {
        // _tracker.ContentLength += count - offset; // This is incorrect
        _tracker.ContentLength += count;
        return _inner.WriteAsync(buffer, offset, count, cancellationToken);
    }

    public override void WriteByte(byte value)
    {
        _tracker.ContentLength++;
        _inner.WriteByte(value);
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            _inner.Dispose();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)