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 IPrincipalconforme 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 Userdiretamente 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 IPrincipaleIIdentity
public interface IPrincipal
{
IIdentity Identity { get; }
bool IsInRole(string role);
}
public interface IIdentity
{
string AuthenticationType { get; }
bool IsAuthenticated { get; }
string Name { get; }
}
IPrincipale IIdentityrepresenta 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.Userou ControllerBase.HttpContext.Uservocê está recebendo um objeto que está garantido para ser um ClaimsPrincipalobjeto que implementaIPrincipal .
Não há outro tipo de usuário para o qual o ASP.NET use Useragora (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 IPrincipale definitivamente não IHttpContextAccessor.
Importante: Não perca tempo injetando IPrincipaldiretamente no seu controlador ou método de ação - é inútil, pois já Userestá 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 IPrincipalpara 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, ClaimsPrincipalprecisará converter IPrincipalpara 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 ClaimsPrincipaldiretamente e passá-lo para o que for preciso IPrincipal.
Exatamente por que não há interface, IClaimsPrincipalnão tenho certeza. Presumo que a Microsoft tenha decidido que ClaimsPrincipalera apenas uma "coleção" especializada que não justificava uma interface.