在过滤器中读取Response.Body流

时间:2018-10-17 07:19:54

标签: c# asp.net asp.net-web-api asp.net-core

我编写了在服务器方法调用后运行的过滤器,并将其内容打印到控制台。该代码是用ASP.NET core v2.1编写的:

public class MyCustomFilter : ActionFilterAttribute
{
    public override void OnResultExecuted(ResultExecutedContext context)
    {

        // ERROR on the next line!
        using (StreamReader sr = new StreamReader(context.HttpContext.Response.Body))
        {
            Console.WriteLine(sr.ReadToEnd());
        }

        base.OnResultExecuted(context);
    }
}

结果-异常:

  

流不可读。

进一步的研究使我发现流(context.HttpContext.Response)具有以下值:

  1. CanRead = false
  2. CanSeek =假

这可以解释为什么它无法读取身体...

如何解决?

3 个答案:

答案 0 :(得分:1)

不确定为什么要这样做。 context.ResultIActionResult的实例,您可以根据需要进行操作。如果您确实想阅读Response.Body,则可以做些骇人听闻的事情。

由于默认的Response.Body是不可读的Stream,为了使主体可读,我们需要劫持响应,即将Body替换为我们自己的Stream 的实例:

  1. 我们可以在执行动作之前动态创建一个全新的内存流,并劫持默认的Response.Body流。
  2. 执行动作后,使用StreamReader读取流,做一些工作,然后设置Response.Body=your new stream

用普通的内存流劫持Response.Body是安全的,因为Body的类型是普通的Stream

public class MyCustomFilter : ActionFilterAttribute
{
    private MemoryStream responseBody ;

    public override void OnActionExecuting(ActionExecutingContext context){
        this.responseBody=new MemoryStream();
        // hijack the real stream with our own memory stream 
        context.HttpContext.Response.Body = responseBody;
    }

    public override void OnResultExecuted(ResultExecutedContext context)
    {

        responseBody.Seek(0, SeekOrigin.Begin);

        // read our own memory stream 
        using (StreamReader sr = new StreamReader(responseBody))
        {
            var actionResult= sr.ReadToEnd();
            Console.WriteLine(actionResult);
            // create new stream and assign it to body 
            // context.HttpContext.Response.Body = ;
        }

        // no ERROR on the next line!

        base.OnResultExecuted(context);
    }
}

出于测试目的,我创建了一个操作方法:

[MyCustomFilter]
public IActionResult Index()
{
    return Ok("it wooooooooorks");
}

enter image description here

答案 1 :(得分:1)

使用 Actionfilter 而不是中间件:

var resultnext = await next();
var objectResult = resultnext.Result as ObjectResult;
var resultValue = objectResult.Value;

            

答案 2 :(得分:0)

这完全取决于您要实现的目标。 如果您想获取响应值或仅查看结果,可以使用

context.Result or context.Result.Value

如果您要修改响应或仅记录整个响应,则应使用中间件。

这是一个很好的例子https://exceptionnotfound.net/using-middleware-to-log-requests-and-responses-in-asp-net-core/

希望有帮助