Asp.net-Core

如何檢索目前響應正文長度?

  • June 20, 2021

我需要檢索響應正文長度。

當我查看https://github.com/aspnet/HttpAbstractions/wiki/Rolling-Notes-Response-Stream-Contract時,據說:

Stream.Position {get} 和 Stream.Length {get} 返回寫入的累積字節數

這正是我所需要httpContext.Response.Body.Length的,但會引發NotSupportedException並說“流不可搜尋”。

我應該使用委託流來計算每次寫入的字節數嗎?

這是在流寫入期間跟踪內容長度的程式碼。該類ContentLengthTracker旨在在其他類之間共享內容長度值。程式碼發佈在https://github.com/ycrumeyrolle/Throttling/blob/master/src/Throttling/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();
       }
   }
}

我假設您正在嘗試從中間件中獲取 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);
       }
   }
}

由於在返回靜態文件(png、js 等)時超出我理解的原因,響應正文將為空,但是設置了 ContentLength,這就是我使用response.ContentLength ?? buffer.Length.

(mod注意:對於兩個問題的重複答案感到抱歉。另一個答案發布不正確,打開的標籤太多。我將其刪除並在此處重新發布答案)。

引用自:https://stackoverflow.com/questions/30652138