I am running into an issue with Action Filters and Autofac. I have an action filter which uses a dependency to determine if the request should be redirected or not. I am applying this filter explicitly to only a sub set of controller actions. The redirect will direct to an action does not have this filter applied. However, the filter still triggers and I end up in an endless loop.
Please note that the code below is a skeleton of some of the logic going on. I just want to capture the problem I'm having.
In my case the controller that has this atribute is the build in accound controller. Namely the Login action. I have set up the autorization filter to apply to all actions in the FitlerConfig.cs:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new AuthorizeAttribute());
filters.Add(new HandleErrorAttribute());
}
My Filter Looks like this:
public class CustomFilter : ActionFilterAttribute
{
public SomeDependancy MyDependancy { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (MyDependancy.ShouldRedirect)
{
filterContext.Result = new RedirectToRouteResult("Default",
new RouteValueDictionary
{
{ "controller", "Error" }, { "action", "CustomError" }
}
);
}
base.OnActionExecuting(filterContext);
return;
}
}
My Account Controller:
public class AccountController : Controller
{
[AllowAnonymous]
[CustomFilter]
public ActionResult Login(string returnUrl)
{
ViewBag.ReturnUrl = returnUrl;
return View();
}
}
Error Controller:
public class ErrorController : Controller
{
[AllowAnonymous]
public ActionResult CustomError()
{
return View("CustomError");
}
}
And finally, my autofac registrations:
var builder = new ContainerBuilder();
builder.Register(con => new SomeDependancy()).AsSelf().InstancePerHttpRequest();
builder.RegisterType<CustomFilter>().As<IActionFilter>().PropertiesAutowired();
// property injection on filters
builder.RegisterFilterProvider();
// Needed to allow property injection in custom action filters.
builder.RegisterType<ExtensibleActionInvoker>().As<IActionInvoker>();
builder.RegisterControllers(typeof(MvcApplication).Assembly).InjectActionInvoker();
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
What ends up happening is if I set a break point in my CustomFilter, when I go to the login page, the breakpoint hits and the redirect occurs, then the break point hits again, and again, and again. What is baffling me is the fact that this action filter is not applied to that controller or action.
If I comment out the dependency and simply redirect in the filter and then comment out autofac registrations, things work as expected. Upon hitting the login screen the filter triggers and redirects to the error page.
I have found the issue to my problem.
I looked at the source code for Autofac's ExtensibleActionInvoker.
I read a comment in it's constructor which says:
/// The following types, if registered in the container, will be added to the
/// filters list:
/// <list>
/// <item><see cref="IActionFilter"/></item>
/// <item><see cref="IAuthorizationFilter"/></item>
/// <item><see cref="IExceptionFilter"/></item>
/// <item><see cref="IResultFilter"/></item>
/// </list>
To solve my issue I ended up with:
var builder = new ContainerBuilder();
builder.Register(con => new SomeDependancy()).AsSelf().InstancePerHttpRequest();
// builder.RegisterType<CustomFilter>().As<IActionFilter>().PropertiesAutowired();
// property injection on filters
builder.RegisterFilterProvider();
// Needed to allow property injection in custom action filters.
builder.RegisterType<ExtensibleActionInvoker>().As<IActionInvoker>();
builder.RegisterControllers(typeof(MvcApplication).Assembly).InjectActionInvoker();
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
It turns out that i dont need to register my CustomFilter. Doing so was causing the ExtensibleActionInvoker to automatically register as a global filter, it seems.