Tengo que decir que me sorprendió bastante que HttpContext sea nulo dentro del constructor. Estoy seguro de que es por razones de rendimiento. Han confirmado que el uso IPrincipal
como se describe a continuación lo inyecta en el constructor. Básicamente está haciendo lo mismo que la respuesta aceptada, pero de una manera más interactiva.
Para cualquiera que encuentre esta pregunta y busque una respuesta al genérico "¿Cómo obtener un usuario actual?" puedes acceder User
directamente desde Controller.User
. Pero solo puede hacer esto dentro de los métodos de acción (supongo que los controladores no solo se ejecutan con HttpContexts y por razones de rendimiento).
Sin embargo, si lo necesita en el constructor (como lo hizo OP) o necesita crear otros objetos inyectables que necesitan el usuario actual, entonces el siguiente es un mejor enfoque:
Inyecte IPrincipal para obtener el usuario
Primer encuentro IPrincipal
yIIdentity
public interface IPrincipal
{
IIdentity Identity { get; }
bool IsInRole(string role);
}
public interface IIdentity
{
string AuthenticationType { get; }
bool IsAuthenticated { get; }
string Name { get; }
}
IPrincipal
y IIdentity
representa al usuario y nombre de usuario. Wikipedia te consolará si 'Principal' suena extraño .
Es importante darse cuenta de que si la recibe de IHttpContextAccessor.HttpContext.User
, ControllerBase.User
o ControllerBase.HttpContext.User
que está recibiendo un objeto que se garantiza que sea un ClaimsPrincipal
objeto que implementaIPrincipal
.
No hay otro tipo de usuario que ASP.NET use en User
este momento (pero eso no quiere decir que otra cosa no pueda implementar IPrincipal
).
Entonces, si tiene algo que depende del 'nombre de usuario actual' que desea inyectar, debería inyectar IPrincipal
y definitivamente no IHttpContextAccessor
.
Importante: No pierda el tiempo inyectando IPrincipal
directamente en su controlador o método de acción, no tiene sentido ya User
que ya está disponible para usted.
En startup.cs
:
// Inject IPrincipal
services.AddTransient<IPrincipal>(provider => provider.GetService<IHttpContextAccessor>().HttpContext.User);
Luego, en su objeto DI que necesita el usuario que acaba de inyectar IPrincipal
para obtener el usuario actual.
Lo más importante aquí es que si está haciendo pruebas unitarias, no necesita enviar una HttpContext
, pero solo necesita burlarse de algo que representa lo IPrincipal
que puede ser ClaimsPrincipal
.
Una cosa extra importante de la que no estoy 100% seguro. Si necesita acceder a los reclamos reales ClaimsPrincipal
, debe enviarlos IPrincipal
a ClaimsPrincipal
. Esto está bien ya que sabemos al 100% que en tiempo de ejecución es de ese tipo (ya que eso HttpContext.User
es lo que es). De hecho, me gusta hacer esto en el constructor, ya que sé con certeza que cualquiera IPrincipal
será a ClaimsPrincipal
.
Si te estás burlando, solo crea uno ClaimsPrincipal
directamente y pásalo a lo que sea necesario IPrincipal
.
Exactamente por qué no hay interfaz porque IClaimsPrincipal
no estoy seguro. Supongo que MS decidió que ClaimsPrincipal
era solo una 'colección' especializada que no garantizaba una interfaz.