• 解决ASP.NET Core的中间件无法读取Response.Body的问题


    概要

    本文主要介绍如何在ASP.NET Core的中间件中,读取Response.Body的方法,以便于我们实现更多的定制化开发。本文介绍的方法适用于.Net 3.1 和 .Net 6。

    代码和实现

    现象解释

    首先我们尝试在自定义中间件中直接读取Response.Body,代码如下:

    public class GlobalRequestManagementMiddleware : IMiddleware
        {
            public async Task InvokeAsync(HttpContext context, RequestDelegate next)
            {
                try
                {
                    await next(context);
                    var reader = new StreamReader(context.Response.Body, Encoding.UTF8);
                    var bodyText = await reader.ReadToEndAsync();
                }
                catch (Exception)
                {
                    throw;
                }
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    我们会得到一个异常消息,表示Response.Body是一个不可读的Stream流
    在这里插入图片描述
    我们添加更多的调试信息,查看Response.Body的具体属性:

     public async Task InvokeAsync(HttpContext context, RequestDelegate next)
    {
       try
       {
           await next(context);
           Console.WriteLine("CanRead is " + context.Response.Body.CanRead);
           Console.WriteLine("CanSeek is " + context.Response.Body.CanSeek);
           Console.WriteLine("CanWrite is " + context.Response.Body.CanWrite);
           var reader = new StreamReader(context.Response.Body, Encoding.UTF8);
           var bodyText = await reader.ReadToEndAsync();
    
       }
       catch (Exception)
       {
           throw;
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    输出结果如下:

    在这里插入图片描述
    Response.Body是一个不可读,不可查找,但是可写的Stream,CanRead,CanSeek和CanWrite全部是只读属性,不可修改。

    解决方案

    从Response.Body本身来解决这个问题,已经基本不可能了。因为该Stream已经被标记为不可读,并且不可修改。

    我们变换解决思路,既然这个Stream无法使用,那我们就在其进入其它中间件,过滤器和Action之前,将其替换为可读和可写的普通内存流。代码如下:

    public async Task InvokeAsync(HttpContext context, RequestDelegate next)
     {
         using ( var bodyStream = new MemoryStream())
         {
             Stream originalBody = context.Response.Body;
             context.Response.Body = bodyStream ;
             await next(context);
             bodyStream.Position = 0;
             var reader = new StreamReader(context.Response.Body, Encoding.UTF8);
             var bodyText = await reader.ReadToEndAsync();
             Console.WriteLine("bodyText is " + bodyText);
             bodyStream.Position = 0;
             await bodyStream.CopyToAsync(originalBody);
             context.Response.Body = originalBody;
         }
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    1. 用普通的MemoryStream替代原有Response.Body中的Stream;
    2. 使用MemoryStream 去接收中间件后面操作产生的操作结果;
    3. 读取MemoryStream中的操作结果;
    4. 重置MemoryStream,以方便后面的操作读取;
    5. Response.Body虽然是不可读的,但是可写,我们可以将中间件后续操作中的操作结果写入最初的Response.Body中;
    6. 将context.Response.Body替换为最初的Stream流。

    用上述方法,我们就可以读取甚至修改Response.Body中的内容。

    我们调用一个Post请求,查看我们自定义的Middleware和后面的操作是否可以正常完成:

    [HttpPost("{id}")]
    public Student Post([FromBody] Student student)
    {
        return student;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    执行结果如下:

    在这里插入图片描述

    在这里插入图片描述
    Body的内容在中间件中被成功读出,Post请求成功的将Student对象返回。

    附录

      public class Student
        {
            public int Id { get; set; }
            public string Name { get; set; }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
  • 相关阅读:
    这一次,称重软件查询功能完整了
    async/await详解
    Innodb如何实现表--上篇
    软件设计师2012上午题基础知识(易错整理)
    QtC++与QCheckBox详解
    负数取余问题
    数字智能化软件改变企业进销存销售管理结构
    CMake中的变量: 改变构建行为的变量
    PyTorch深度学习实战(17)——多任务学习
    文心一言 VS 讯飞星火 VS chatgpt (103)-- 算法导论10.1 1题
  • 原文地址:https://blog.csdn.net/weixin_43263355/article/details/133547573