值不能为空。 (参数'principal')[身份][ASP.net核心]

Value cannot be null. (Parameter 'principal') [ Identity ] [ ASP.net core ]

我正在做一个 Asp.Net core 项目,我使用 Identity 框架。

问题:

在Controller中我尝试根据User获取当前用户信息(他的类型是ClaimsPrincipal)但是我得到一个运行时错误。

我的尝试:

那是我的 Controller 代码

[Authorize]
public class ServiceController : Controller
{

    private readonly UserManager<AppUser>      _userManager;
    private          RoleManager<IdentityRole> _roleManager;
    private          AppUser                   _currentUser;
    private          AppRoles                  _currentUserRole;

    public ServiceController(UserManager<AppUser> userManager, RoleManager<IdentityRole> roleManager)
    {
        _userManager = userManager;
        _roleManager = roleManager;

        SetCurrentUser();
        SetCurrentUserRole();
    }



    #region Private Methods

    private async void SetCurrentUser()
    {
        var us = User;
        _currentUser = await _userManager.GetUserAsync(User);
    }

    private async void SetCurrentUserRole()
    {
        string currentUserRoleName = _userManager.GetRolesAsync(_currentUser).Result.FirstOrDefault();

        if (User.IsInRole(AppRoles.Admin.ToString()))
        {
            _currentUserRole = AppRoles.Admin;
        }
        else if (User.IsInRole(AppRoles.User.ToString())) 
        {
            _currentUserRole = AppRoles.User;
        }
    }

    #endregion


    public ActionResult Get()
    {
        return _currentUserRole switch
               {
                   AppRoles.User  => RedirectToAction("Services", "User"),
                   AppRoles.Admin => RedirectToAction("Services", "Admin"),
                   _              => RedirectToPage("Login")
               };
    }


}

哪里出错了?

错误发生在SetCurrentUser方法中,Visual Studio抛出了这个异常:

System.ArgumentNullException: 'Value cannot be null. (Parameter 'principal')'

错误截图:

附加信息:

我的项目依赖于.Net 5

请帮忙解决这个问题?

UserHttpContext 尚未在控制器的构造函数中设置(仍然是 null)。如果您想访问它,请尝试将 IHttpContextAccessor 注入构造函数,如下所示:

public ServiceController(UserManager<AppUser> userManager, 
                         RoleManager<IdentityRole> roleManager,
                         IHttpContextAccessor httpContextAccessor){
    var user = httpContextAccessor.HttpContext.User;
    //now pass the user above around to use it instead of basing on the property User
    //...
}

然而,这种方式并不是每个标准,尤其是当您需要在构造函数中调用 async 方法时,根本不应该这样做。您的方法甚至使用 async void 声明,这应该绝对避免。实际上,您想要的是在执行操作之前自动提供一些信息。这非常适合 IActionFilterIAsyncActionFilter(这里使用异步过滤器更好,因为您将在其中调用异步方法)。因为信息在控制器中可用(如 properties/fields),所以覆盖控制器的 OnActionExecutionAsync 方法而不是使用我提到的过滤器会更方便。在这种情况下使用过滤器可能需要您共享信息(例如通过 HttpContext.Items 或请求功能...),这有点不方便。

以下是您应该如何操作:

public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
   await SetCurrentUserAsync();
   await SetCurrentUserRoleAsync();
   await base.OnActionExecutionAsync(context, next);
}

//adjust your methods
private async Task SetCurrentUserAsync() { ... }
private async Task SetCurrentUserRoleAsync() { ... }

就是这样,记得删除构造函数中的等效代码。