You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET中间件单元测试:设置响应体后读取为空的问题

问题

我开发了一个用于修改响应的.NET中间件,正在对其进行单元测试。状态码和内容类型的断言都能成功,但断言响应体时,明明已经设置了响应体,读取到的却是空字符串。

我在另一项测试中修改和断言请求体时没有问题,想问能不能对响应体执行同样的操作?

中间件代码

using System.Net;
using System.Net.Mime;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;

namespace Web.API.Middleware
{
    public class SampleMiddleware
    {
        private readonly RequestDelegate _next;

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

        public async Task InvokeAsync(HttpContext context)
        {
            bool success = true;

            // 省略其他逻辑...

            context.Response.ContentType = MediaTypeNames.Text.Plain;
            context.Response.StatusCode = (int)HttpStatusCode.BadRequest;

            await context.Response.WriteAsync("Bad request"); // 此处设置响应体

            success = false;

            if (success)
            {
                await _next(context);
            }
        }
    }
}

单元测试代码

using System.IO;
using System.Net;
using System.Net.Mime;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Web.API.Middleware;

namespace Web.API.UnitTests
{
    [TestClass]
    public class SampleMiddlewareTest
    {
        [TestMethod]
        public async Task BadRequestTest()
        {
            // Arrange
            var expected = "Bad request";

            var httpContext = new DefaultHttpContext();
            var nextMock = new Mock<RequestDelegate>();
            var middleware = new SampleMiddleware(nextMock.Object);

            // Act
            await middleware.InvokeAsync(httpContext);

            // Assert
            Assert.AreEqual((int)HttpStatusCode.BadRequest, httpContext.Response.StatusCode); // 断言成功
            Assert.AreEqual(MediaTypeNames.Text.Plain, httpContext.Response.ContentType); // 断言成功

            StreamReader reader = new StreamReader(httpContext.Response.Body);
            var responseBody = reader.ReadToEnd();

            Assert.AreEqual(expected, responseBody); // 断言失败
        }
    }
}

测试结果

Assert.AreEqual 失败。预期:<Bad request>。实际:<>.
解决方案

问题根源有两个:

  1. 响应流指针位置:调用WriteAsync后,流的读写指针已经移动到了流的末尾,此时直接调用ReadToEnd会从末尾开始读取,自然返回空字符串。
  2. 默认流不可查找:DefaultHttpContext的Response.Body默认是不可查找的管道流(PipeStream),无法重置指针位置。

只需两步修改测试代码即可解决:

修改后的测试代码

using System.IO;
using System.Net;
using System.Net.Mime;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Web.API.Middleware;

namespace Web.API.UnitTests
{
    [TestClass]
    public class SampleMiddlewareTest
    {
        [TestMethod]
        public async Task BadRequestTest()
        {
            // Arrange
            var expected = "Bad request";
            var httpContext = new DefaultHttpContext();
            // 将默认流替换为可查找的MemoryStream
            httpContext.Response.Body = new MemoryStream();
            var nextMock = new Mock<RequestDelegate>();
            var middleware = new SampleMiddleware(nextMock.Object);

            // Act
            await middleware.InvokeAsync(httpContext);
            // 将流指针重置到起始位置
            httpContext.Response.Body.Seek(0, SeekOrigin.Begin);

            // Assert
            Assert.AreEqual((int)HttpStatusCode.BadRequest, httpContext.Response.StatusCode);
            Assert.AreEqual(MediaTypeNames.Text.Plain, httpContext.Response.ContentType);

            // 使用using确保流资源正确释放,同时用异步读取保持逻辑一致性
            using var reader = new StreamReader(httpContext.Response.Body);
            var responseBody = await reader.ReadToEndAsync();

            Assert.AreEqual(expected, responseBody);
        }
    }
}

额外说明

  • 用using包裹StreamReader是最佳实践,能自动释放底层流资源。
  • 优先使用异步方法ReadToEndAsync,和中间件的异步执行逻辑保持一致,避免同步阻塞带来的潜在问题。
  • 如果是更复杂的中间件场景(比如需要将流传递给后续中间件),可以考虑使用Stream包装类来保留原始流的功能,但这个简单场景下替换为MemoryStream足够满足测试需求。

内容的提问来源于stack exchange,提问作者tariq

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 13:47:03