Correct way to Inject MVC and HTTP attributes and filters using Ninject
💻 coding

Correct way to Inject MVC and HTTP attributes and filters using Ninject

2 min read 399 words
2 min read
ShareWhatsAppPost on X
  • 1Ninject can cause null dependencies in custom attributes due to constructor injection issues at startup.
  • 2Binding an implementation to itself in Ninject can lead to duplicate execution of action filters and null reference exceptions.
  • 3Refactoring the attribute class and using proper Ninject bindings can resolve dependency issues and ensure correct execution.

AI-generated summary · May not capture all nuances

Key Insight
AskGif

"Ninject can cause null dependencies in custom attributes due to constructor injection issues at startup."

Correct way to Inject MVC and HTTP attributes and filters using Ninject

Ninject has some fun quirks. For instance, one "bug" I came across was that if you hit an ASP.NET controller with a custom attribute, the attribute’s constructor-injected dependencies would be null on startup. For example, take the following ActionFilterAttribute:

public class ForceHttpAttribute : ActionFilterAttribute
{
 private readonly IControllerContextHelper _controllerContextHelper;

 public ForceHttpAttribute(IControllerContextHelper controllerContextHelper)
 {
 _controllerContextHelper = controllerContextHelper;
 }

 /// <summary>
 /// Called before the controller action executes. Checks for HTTPS requsts
 /// and redirects to HTTP if so.
 /// </summary>
 /// <param name="filterContext">The filter context.</param>
 public override void OnActionExecuting(ActionExecutingContext filterContext)
 {
 var currentUri = _controllerContextHelper.GetRequestUri(filterContext);

 var isSecure = _controllerContextHelper.IsSecureConnection(filterContext);

 if (isSecure)
 {
 var secureUri = currentUri.AbsoluteUri.Replace("https://", "http://");
 filterContext.Result = new RedirectResult(secureUri);
 }
 }
}

You can use BindFilter to bind this to implementation or an interface, such as the following INCORRECT WAY (don’t do this!):

kernel.BindFilter<ForceHttpAttribute>(System.Web.Mvc.FilterScope.Controller, 0).WhenControllerHas<ForceHttpAttribute>();

This is wrong for a couple of reasons. For instance, you’re binding implementation to itself. This will lead to the attribute’s OnActionExecuting to run twice. So, don’t do this. For me, the first time OnActionExecuting ran, there were null dependencies (sometimes throwing null reference exceptions), but the second time around it would execute as expected. This lead to some fun debugging…

The solution

The solution I found was to refactor the above class into the following:

public class ForceHttpAttribute : Attribute
{
 public class Implementation : ActionFilterAttribute
 {
 private readonly IControllerContextHelper _controllerContextHelper;

 public Implementation(IControllerContextHelper controllerContextHelper)
 {
 _controllerContextHelper = controllerContextHelper;
 }

 /// <summary>
 /// Called before the controller action executes. Checks for HTTPS requsts
 /// and redirects to HTTP if so.
 /// </summary>
 /// <param name="filterContext">The filter context.</param>
 public override void OnActionExecuting(ActionExecutingContext filterContext)
 {
 var currentUri = _controllerContextHelper.GetRequestUri(filterContext);

 var isSecure = _controllerContextHelper.IsSecureConnection(filterContext);

 if (isSecure)
 {
 var secureUri = currentUri.AbsoluteUri.Replace("https://", "http://");
 filterContext.Result = new RedirectResult(secureUri);
 }
 }
 }
}

With the following Ninject binding:

kernel.BindFilter<ForceHttpAttribute.Implementation>(System.Web.Mvc.FilterScope.Controller, 0)
 .WhenControllerHas<ForceHttpAttribute>()
 .InRequestScope();

So what is this telling Ninject? It says whenever you find the filter [ForceHttpAttribute] (an implementation of Attribute), use the nested class ForceHttpAttribute.Implementation (an implementation of ActionFilterAttribute).

This binding gave expected results. The dependencies were no longer null and the OnActionExecuting only ran once.

If you’re using an HTTP filter’s, you will have to use the BindHttpFilter method:

kernel.BindHttpFilter<MyHttpAttribute.Implementation>(FilterScope.Controller)
 .WhenControllerHas<MyHttpAttribute>()
 .InRequestScope();

You can also pass the constructor arguments using the WithConstructorArgumentFromControllerAttribute method:

kernel.BindHttpFilter(FilterScope.Controller)
 .WhenControllerHas().InRequestScope()
 // "myParameter" is the constructor parameter name, MyParamater is the class property on MyHttpAttribute (i.e. public string MyParamater {get;set;})
 .WithConstructorArgumentFromControllerAttribute("myParameter", a => a.MyParameter);

Enjoyed this article?

Share it with someone who'd find it useful.

ShareWhatsAppPost on X

AskGif

Published on 21 March 2019 · 2 min read · 399 words

Part of AskGif Blog · coding

You might also like

Correct way to Inject MVC and HTTP attributes and filters using Ninject | AskGif Blog