Devo dizer que fiquei bastante surpreso que o HttpContext seja nulo dentro do construtor. Tenho certeza que é por razões de desempenho. Confirmaram que o uso IPrincipal
conforme descrito abaixo injeta no construtor. É essencialmente fazer o mesmo que a resposta aceita, mas de uma maneira mais interativa.
Para quem encontrar esta pergunta, procurando uma resposta para o genérico "Como obter usuário atual?" você pode simplesmente acessar User
diretamente de Controller.User
. Mas você só pode fazer isso dentro dos métodos de ação (presumo que os controladores não sejam executados apenas com HttpContexts e por razões de desempenho).
No entanto - se você precisar dele no construtor (como o OP fez) ou precisar criar outros objetos injetáveis que precisam do usuário atual, a seguir é apresentada uma abordagem melhor:
Injete o IPrincipal para obter o usuário
Primeiro encontro IPrincipal
eIIdentity
public interface IPrincipal
{
IIdentity Identity { get; }
bool IsInRole(string role);
}
public interface IIdentity
{
string AuthenticationType { get; }
bool IsAuthenticated { get; }
string Name { get; }
}
IPrincipal
e IIdentity
representa o usuário e o nome de usuário. A Wikipedia o confortará se 'Principal' parecer estranho .
Importante perceber que se você começar a partir de IHttpContextAccessor.HttpContext.User
, ControllerBase.User
ou ControllerBase.HttpContext.User
você está recebendo um objeto que está garantido para ser um ClaimsPrincipal
objeto que implementaIPrincipal
.
Não há outro tipo de usuário para o qual o ASP.NET use User
agora (mas isso não quer dizer que outra coisa não possa ser implementada IPrincipal
).
Portanto, se você tem algo que depende do 'nome do usuário atual' que deseja injetar, deve injetar IPrincipal
e definitivamente não IHttpContextAccessor
.
Importante: Não perca tempo injetando IPrincipal
diretamente no seu controlador ou método de ação - é inútil, pois já User
está disponível para você.
Em startup.cs
:
// Inject IPrincipal
services.AddTransient<IPrincipal>(provider => provider.GetService<IHttpContextAccessor>().HttpContext.User);
Em seguida, no seu objeto DI que precisa do usuário, você apenas injeta IPrincipal
para obter o usuário atual.
A coisa mais importante aqui é que, se você estiver fazendo testes de unidade, não precisará enviar um HttpContext
, mas precisará simular algo que represente o IPrincipal
que pode ser ClaimsPrincipal
.
Uma coisa extra importante que eu não tenho 100% de certeza. Se você precisar acessar as reivindicações reais, ClaimsPrincipal
precisará converter IPrincipal
para ClaimsPrincipal
. Isso é bom, pois sabemos 100% que, em tempo de execução, é desse tipo (já que é isso que HttpContext.User
é). Na verdade, gosto de fazer isso no construtor, pois já tenho certeza de que IPrincipal
será um ClaimsPrincipal
.
Se você estiver fazendo zombaria, basta criar um ClaimsPrincipal
diretamente e passá-lo para o que for preciso IPrincipal
.
Exatamente por que não há interface, IClaimsPrincipal
não tenho certeza. Presumo que a Microsoft tenha decidido que ClaimsPrincipal
era apenas uma "coleção" especializada que não justificava uma interface.