User.GetUserId() 在控制器的构造函数中失败

User.GetUserId() fails inside controller's constructor

我收到以下错误:值不能为空。参数名称:principal

如何在控制器的构造函数中访问身份 (userId)?我只能通过将失败的调用包装在一个函数中来让它工作(都在下面突出显示)。

有什么需要注射的吗?

public class BaseController : Controller {
    protected readonly MylDbContext dbContext;
    protected readonly string userId;

    public BaseController(MylDbContext dbContext) {
        this.dbContext = dbContext;
        userId = User.GetUserId(); // fails here
    }

    public string GetUserId() {
        return User.GetUserId(); // works here
    }
}

实例化控制器时,无法保证请求信息在 HttpContext 中可用。可能根本没有请求。为什么在构造函数中需要该信息?

编辑
我明白你的问题。在这种情况下,我通常做的是创建一个 属性 和一个支持字段,每个控制器只查询一次:

private int? _userId;
public int UserId
{
    get
    {
        if (!_userId.HasValue)
        {
            // query from db.
            _userId = 42;
        }
        return _userId.Value;
    }
}

如@Henk 所述,控制器构造函数将在设置 ActionContext 之前执行,因此您将无法访问 ContextRequestUser 等属性.您需要在请求的上下文中检索 userId。

您可以使用 action filters 的老式方法,它仍然是 MVC6 管道的一部分(它还通过 IAsyncActionFilter 支持异步操作过滤器)。

由于您想在控制器中设置 属性,实现此操作的最简单方法是重写控制器 class 中的 OnActionExecuting 方法。这是可行的,因为您继承自 Controller,它已经实现了 IActionFilter.

public override void OnActionExecuting(ActionExecutingContext context)
{
    //Get user id   
    userId = User.GetUserId();
}

编辑

如果您检查 DefaultControllerFactory,您会看到:

  1. 首先创建控制器
  2. 然后设置 ActionContext(通过 DefaultControllerPropertyActivator,它是 属性 激活器之一):

    var controller = _controllerActivator.Create(actionContext, controllerType);
    foreach (var propertyActivator in _propertyActivators)
    {
        propertyActivator.Activate(actionContext, controller);
    }