I have to say I was quite surprised that HttpContext is null inside the constructor. I'm sure it's for performance reasons. Have confirmed that using IPrincipal
as described below does get it injected into the constructor. Its essentially doing the same as the accepted answer, but in a more interfacey-way.
For anyone finding this question looking for an answer to the generic "How to get current user?" you can just access User
directly from Controller.User
. But you can only do this inside action methods (I assume because controllers don't only run with HttpContexts and for performance reasons).
However - if you need it in the constructor (as OP did) or need to create other injectable objects that need the current user then the below is a better approach:
Inject IPrincipal to get user
First meet IPrincipal
and IIdentity
public interface IPrincipal
{
IIdentity Identity { get; }
bool IsInRole(string role);
}
public interface IIdentity
{
string AuthenticationType { get; }
bool IsAuthenticated { get; }
string Name { get; }
}
IPrincipal
and IIdentity
represents the user and username. Wikipedia will comfort you if 'Principal' sounds odd.
Important to realize that whether you get it from IHttpContextAccessor.HttpContext.User
, ControllerBase.User
or ControllerBase.HttpContext.User
you're getting an object that is guaranteed to be a ClaimsPrincipal
object which implements IPrincipal
.
There's no other type of User that ASP.NET uses for User
right now, (but that's not to say other something else couldn't implement IPrincipal
).
So if you have something which has a dependency of 'the current user name' that you want injected you should be injecting IPrincipal
and definitely not IHttpContextAccessor
.
Important: Don't waste time injecting IPrincipal
directly to your controller, or action method - it's pointless since User
is available to you there already.
In startup.cs
:
services.AddHttpContextAccessor();
services.AddTransient<IPrincipal>(provider => provider.GetService<IHttpContextAccessor>().HttpContext.User);
Then in your DI object that needs the user you just inject IPrincipal
to get the current user.
The most important thing here is if you're doing unit tests you don't need to send in an HttpContext
, but only need to mock something that represents IPrincipal
which can just be ClaimsPrincipal
.
One extra important thing that I'm not 100% sure about. If you need to access the actual claims from ClaimsPrincipal
you need to cast IPrincipal
to ClaimsPrincipal
. This is fine since we know 100% that at runtime it's of that type (since that's what HttpContext.User
is). I actually like to just do this in the constructor since I already know for sure any IPrincipal
will be a ClaimsPrincipal
.
If you're doing mocking, just create a ClaimsPrincipal
directly and pass it to whatever takes IPrincipal
.
Exactly why there is no interface for IClaimsPrincipal
I'm not sure. I assume MS decided that ClaimsPrincipal
was just a specialized 'collection' that didn't warrant an interface.