How to inject HttpContext or ActionContext ?
HttpContext
and ActionContext
are two classes that describe how the framework understands the current request, and how it is going to handle it.
HttpContext may bring you information about the Request (path, headers, cookies, etc), the user identity, the current set of services, etc. Microsoft website defines it as 'Encapsulates all HTTP-specific information about an individual HTTP request.'
Although it is already registered on several base MVC classes and accessible via Context
or HttpContext
property, in a custom class, you usually cannot just inject the HttpContext
class.
To inject the HttpContext
or ActionContext
classes, instead the .Net team has set up accessors dedicated to that requirement: IHttpContextAccessor
and IActionContextAccessor
.
A reason behind? Probably that you would be able to access these context into singleton classes, such as middlewares.
IHttpContextAccessor
and IActionContextAccessor
are not registered by default, and should be registered as singleton. Therefore
- if you want to properly inject
IActionContextAccessor
, you need to call at startup:
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
services.TryAddSingleton<IActionContextAccessor, ActionContextAccessor>();
- regarding the
HttpContextAccessor
, if you want to properly injectIHttpContextAccessor
, the team has added an extension method that you should to call during the initialization:
using Microsoft.Extensions.DependencyInjection;
services.AddHttpContextAccessor();
And then you are properly set to access HttpContext
or ActionContext
.
Example of use
IHttpContextAccessor: implementing a base Controller class
In a project, we often have code that may be shared among our controllers.
Implementing a base class for our project controllers may be a good way to share some common behaviors and add some shortcuts which may be useful in many methods in different controllers. The extra bonus would be that it simplifies the controller code and gives more visibility to the actual core business code.
public class BaseController : Controller // extending Controller is optional
{
protected Service1 CommonService1 { get; }
protected Service2 CommonService2 { get; }
public BaseController(CommonService1 commonService1
, CommonService2 commonService2)
{
this.CommonService1 = commonService1;
this.CommonService2 = commonService2;
}
}
However this base class may require several services which would make the constructor of the implementation classes look pretty ugly with a large amount of dependencies. This would go against our will of simplification of the end-user controllers.
public class MyController : BaseController
{
protected Service3 CommonService3 { get; }
protected Service4 CommonService4 { get; }
public MyController(CommonService1 commonService1
, CommonService2 commonService2
, CommonService3 commonService3
, CommonService4 commonService4)
: base(CommonService1 commonService1
, CommonService2 commonService2)
{
this.CommonService3 = commonService3;
this.CommonService4 = commonService4;
}
private void Method1()
{
CommonService1.Call1();
}
}
One possibility would be to take advantage of the Controller.HttpContext.RequestServices
to request services silently in the constructor, however you may notice that the HttpContext
is null in the constructor and is populated after.
So we can make use of IHttpContextAccessor
to access the Services in the constructor, it allows having a single dependency to pass to the base controller :
public class BaseController : Controller // extending Controller is optional
{
private IServiceProvider _Services;
protected Service1 CommonService1 { get; }
protected Service2 CommonService2 { get; }
public BaseController(IHttpContextAccessor httpContextAccessor)
{
this._Services = httpContextAccessor.HttpContext.RequestServices;
this.CommonService1 = services.GetRequiredService<Service1>();
this.CommonService2 = services.GetRequiredService<Service2>();
}
}
Note that this does not exactly follow the Explicit Dependencies Principle, but you may notice the
GetRequiredService
which will throw a comprehensible exception if the service is not well registered. This will avoid silent fails by assigning a null value to the variables and later surprises.
From there you can implement any controller, by passing a single dependency to the base class:
public class MyController : BaseController
{
public MyController(IHttpContextAccessor httpContextAccessor)
: base(httpContextAccessor)
{
}
private void Method1()
{
CommonService1.Call1();
}
}
We have seen in this post how to inject HttpContext and ActionContext in a custom class, done by registering and injecting their respective accessors.