14

I'm trying to setup an action filter that only does something if the StatusCode of the HttpContext.Response is 302.

I would expect to be able to do this in the OnActionExecuting method, but the StatusCode is always 200.

ActionFilter code:

public class CustomFilter : IActionFilter
{
   public void OnActionExecuting(ActionExecutingContext context)
    {
        // do some setup
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        if (context.HttpContext.Response.StatusCode == StatusCodes.Status302Found)
        {
            // never get here
        }
    }
}

My Action method:

public IActionResult Redirect()
{
    return RedirectToAction("Index", "Home");
}

And registering the ActionFilter in startup:

public void ConfigureServices(
    IServiceCollection services)
{
    services.AddMvc(
        options =>
        {
            options.Filters.Add(new CustomFilter());
        });
}

I have checked in the browser and it is correctly returning 302 and doing the redirect. I have also tried using the IAsyncActionFilter interface but had the same problem.

How can I apply my ActionFilter to (only) a redirected response?

And why is this not working as is?

EDIT: Whoops I had them the wrong way round. Actually I am still getting this issue though...

jag
  • 517
  • 6
  • 16

4 Answers4

14

You are looking at the status code of the response before response is actually generated. OnActionExecuting is called before the action is executed, so no status code is set yet. Default value for status code is 200, and that's what you see.

To be able to see the actual status code other actions have assigned to the response, you need to look at OnActionExecuted, which runs after the action.

Update.

Another issue might be the fact that in Core framework action filter runs before and after the action is executed. So the response is not processed yet, and status code is not set in the http response object.

The proper method for your use case seems to be IResultFilter.OnResultExecuted

Andrei
  • 55,890
  • 9
  • 87
  • 108
  • Ahh dammit, I had them the wrong way round. Thank you. – jag May 22 '18 at 09:07
  • Actually I am still getting the same issue. – jag May 22 '18 at 09:21
  • @jag, are your types correct? you have `ActionExecut_ing_Context` for `OnActionExecut_ed_` and vice versa – Andrei May 22 '18 at 09:24
  • Ahh sorry yes, no compiler to complain at me in stackoverflow editor! – jag May 22 '18 at 09:25
  • @jag, that's weird. Works for me, and worked [here](https://stackoverflow.com/q/42444170/728795) too. Are you applying the filter correctly? – Andrei May 22 '18 at 09:32
  • I've added where I register the `ActionFilter` in startup. It looks like that question isn't using aspnet-core, could that explain the discrepancy? – jag May 22 '18 at 10:30
  • How is this even an answer to this question????!!!!! The status code is always 200 even in `OnActionExecuted`. This answers NOTHING. – Arad Alvand May 01 '23 at 20:49
  • @AradAlvand it was the update that fixed it for me - specifically using a `ResultFilter` instead of `ActionFilter`. see https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-7.0#filter-types – jag Aug 09 '23 at 13:38
5

You can attempt to convert your ActionExecutedContext.Result object to ObjectResult and retrieve StatusCode from it.

public void OnActionExecuted(ActionExecutedContext context)
{
    var statusCode = (context.Result as ObjectResult)?.StatusCode
}
Peter Csala
  • 17,736
  • 16
  • 35
  • 75
Antony Thomas
  • 3,576
  • 2
  • 34
  • 40
  • 1
    This actually worked for me in ASP.NET MVC Core 2.1 (in my OnActionExecuted method): `var statusCode = (context.Result as StatusCodeResult)?.StatusCode ?? 200;` – Bob Tabor Oct 31 '18 at 15:13
  • 1
    @BobTabor Careful with this - return BadRequest(), return Forbid(), and exceptions might cause this to not work as you expect. – bugnuker Dec 14 '19 at 17:12
  • 4
    Better would be to try and cast it to an `IStatusCodeResult`. – ThomasDC Feb 13 '20 at 14:10
1
int? statusCode = null;
ObjectResult result = context.Result as ObjectResult;
statusCode = result?.StatusCode;

if (statusCode == null)
{
    StatusCodeResult statusResult = context.Result as StatusCodeResult;
    statusCode = statusResult?.StatusCode;
}
Ravi
  • 398
  • 3
  • 11
0

Based on previous answers, in order to avoid as or cast, you can just use reflection

PropertyInfo pi = context.Result.GetType().GetProperty("StatusCode");
int httpCode = -1;
if (pi != null && pi.CanRead && pi.PropertyType == typeof(int)) httpCode = (int)pi.GetValue(context.Result);

So if StatusCode property is present, it will be read no matter what's the underlying result class type.