User.GetUserId() fails inside controller's constructor

谁说胖子不能爱 提交于 2020-01-02 03:17:56

问题


I am getting the following error: Value cannot be null. Parameter name: principal

How can I access Identity (userId) inside the controller's constructor? I can only get it working by wrapping the failed call in a function (both highlighted below).

Is there anything I need to inject?

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
    }
}

回答1:


As @Henk mentioned, the controller constructor will be executed before the ActionContext has been set, so you won't have access to properties like Context, Request or User. You need to retrieve the userId within the context of a request.

You could use the old-fashioned approach of the action filters which are still part of the MVC6 pipeline (Which also supports async action filters via IAsyncActionFilter).

Since you want to set a property in your controller, the most simple way you could implement this is by overriding the OnActionExecuting method in your controller class. This works because you are inheriting from Controller, which already implements IActionFilter.

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

Edit

If you check the DefaultControllerFactory you will see that:

  1. first the controller is created
  2. then the ActionContext is set (Via the DefaultControllerPropertyActivator which is one of the property activators):

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



回答2:


When the controller is instantiated there is no guarantee that the request information is avaiable in the HttpContext yet. There might not be a request at all. Is there reason why you need that information in the constructor?

Edit
I understand your issue. What I usually do in a scenario like this, is create a property with a backing field which only queries once per controller:

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


来源:https://stackoverflow.com/questions/33169060/user-getuserid-fails-inside-controllers-constructor

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!