No1Lives4Ever
No1Lives4Ever

Reputation: 6893

Reading the Response.Body stream in Filter

I writing my filter that running after a sever method call and print its content to Console. The code is written in 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);
    }
}

The result - exception:

Stream was not readable.

Further investigation leads me to the point that the stream (context.HttpContext.Response) has those values:

  1. CanRead = false
  2. CanSeek = false

That can explain why it cant read the body...

How to solve?

Upvotes: 9

Views: 15161

Answers (3)

May Alvarado
May Alvarado

Reputation: 131

Use Actionfilter instead of middleware:

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

            

Upvotes: 4

itminus
itminus

Reputation: 25360

Not sure why you need to do this . context.Result is an instance of IActionResult , you can manipulate it as you like . If you do want to read the Response.Body , there's something hacky can be done.

Since the default Response.Body is not a readable Stream , in order to make the body readable , we need to hijack the response , namely replace the Body with our own instance of Stream :

  1. We can create a brand new memory stream dynamically before action is executing , and hijack the default Response.Body stream .
  2. When action executed , read the stream using a StreamReader, do some work, and set the Response.Body=your new stream .

It's safe to hijack the Response.Body with a plain memory stream because the type of Body is plain 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);
    }
}

For a testing purpose , I create an action method :

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

enter image description here

Upvotes: 4

Erez.L
Erez.L

Reputation: 85

It all depends on what do you want to achieve. If you want to get the response values or just to see the result you can use

context.Result or context.Result.Value

If you want to modify the response or just to log the entire response you should use a middleware.

here is a good example https://exceptionnotfound.net/using-middleware-to-log-requests-and-responses-in-asp-net-core/

hope it helps

Upvotes: 0

Related Questions