为什么User(在User.Identity.Name中)在我的抽象基本控制器中为null?

时间:2009-01-10 07:27:26

标签: c# asp.net-mvc controller user-controls httpcontext

我问了一个相关的问题,但是搞砸了标题,没有人会理解它。由于我现在能够更准确地提出这个问题,我决定在一个新问题中重新制定它并关闭旧问题。对不起。

所以我想做的是将数据(我的自定义用户的昵称存储在数据库中)传递给LoginUserControl。此登录通过Html.RenderPartial()从主页面呈现,因此我真正需要做的是确保每次调用时都出现ViewData [“UserNickname”]。但是我不想在每个控制器的每个动作中填充ViewData [“UserNickname”],所以我决定使用this approach并创建一个抽象的基本控制器,它将为我完成工作,如下所示:

public abstract class ApplicationController : Controller
    {
        private IUserRepository _repUser;

        public ApplicationController()
        {
            _repUser = RepositoryFactory.getUserRepository();
            var loggedInUser = _repUser.FindById(User.Identity.Name); //Problem!
            ViewData["LoggedInUser"] = loggedInUser;
        }
    }

这样,无论我的推导控制器做什么,用户信息都已经存在。

到目前为止,这么好。现在出现问题:

我无法调用User.Identity.Name,因为User已经为空。在我的所有派生控制器中都不是这种情况,因此这是抽象基本控制器特有的。

我在代码中的另一个地方通过FormsAuthentication设置User.Identity.Name,但我认为这不是问题 - afaik User.Identity.Name可以为null,但不是User本身。

在我看来HttpContext不可用(因为也是null ;-)而且我在这里错过了一个简单而重要的观点。任何人都可以给我一些提示吗?我真的很感激。

10 个答案:

答案 0 :(得分:23)

这个问题的答案实际上非常简单。由于Raimond指出的原因,我无法在构造函数中执行代码,但我可以在构造函数之外执行。

所以我所做的是覆盖基本控制器类中的onActionExecuting()(我为它创建了一个自定义属性,但只是重写该方法也应该有效),然后从那里进行用户查找。

现在它按预期工作,我没有重复的代码。

答案 1 :(得分:17)

在实例化Controller之后才会分配User属性,但您可以通过以下方式获得构造函数的早期访问:

System.Web.HttpContext.Current.User

答案 2 :(得分:14)

我的猜测是Controller的基本构造函数没有填写用户,但只有在为Controller设置ControllerContext时才知道它。你应该在关于MVC应用程序生命周期的文档中检查这个(here可能会做的,虽然它可能有点过时,因为它是预览版本),或者只是查看源代码MVC。

来自我的MVC代码(也是预览版,但应该没问题): (在控制器中)

 public IPrincipal User {
            get {
                return HttpContext == null ? null : HttpContext.User;
            }
        }

...

public HttpContextBase HttpContext {
        get {
            return ControllerContext == null ? null : ControllerContext.HttpContext;
        }
    }

我没有看到代码中的默认构造函数的实现。 这将证明ControllerContext在构造时为空。

所以你应该在其他地方执行你的代码。

答案 3 :(得分:4)

你能用以下的东西抓住这个:

HttpContext currentContext = HttpContext.Current;
string userName = currentContext.User.Identity.Name;

或者HttpContext总是空的?

你能通过抽象类的构造函数设置httpContext吗?并以这种方式使用它?

答案 4 :(得分:4)

谢谢雷蒙德。我太累了,看不清楚。 @Keeney:是的,上下文总是为空。雷蒙德指出了原因。无论如何,谢谢,我也不明白为什么: - )

我当前的工作解决方案(虽然不是我想要的)是一个属性,我用它来装饰我的所有控制器动作。以下是实施:

public class MasterPageDataAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            base.OnActionExecuting(filterContext);
            IUserRepository _repUser = RepositoryFactory.getUserRepository();
            IPrincipal siteUser = filterContext.Controller.ControllerContext.HttpContext.User;
            User loggedInUser = null;

            if (siteUser == null || siteUser.Identity.Name == null)
            {
                //do nothing
            }
            else
            {
                loggedInUser = _repUser.findUserById(siteUser.Identity.Name);
            }
            filterContext.Controller.ViewData["LoggedInUser"] = loggedInUser ?? new User { Nickname = "Guest" };
        }
    }

我将研究如何以遵循DRY原则的方式执行代码,因为使用属性肯定意味着重复自己。也许某种拦截器(interesting idea)或钩子可能会有所帮助。

为此欢呼。

答案 5 :(得分:0)

我在basecontroller实现中这样做,它按预期工作。

public abstract class BaseController : Controller
{
    public bool LoggedOn
    {
        get { return User.Identity.IsAuthenticated; }
    }
}

这对我总是返回true或false,User != null

答案 6 :(得分:0)

到Masterfu: 我做了一些类似于你的帮助,希望能帮助后来的访客。 在我的情况下,我需要为不同的用户创建控制器的reposiotry,但在控制器的构造函数中,(principal)用户还没准备好。所以我为控制器创建了一个属性:

[CreateRepositoryByUser]
public class MFCController : Controller
{
    protected MFCRepository _repository
    {
        get { return ViewData["repository"] as MFCRepository; }
    }
...
实际上,_repository不是控制器的私有变量,而是由属性创建的somethign:

public class CreateRepositoryByUser : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        CreateRepository(filterContext);
    }

    public static void CreateRepository(ActionExecutingContext filterContext)
    {
        if (filterContext.Controller.ViewData["repository"] == null)
        {
            filterContext.Controller.ViewData["repository"] =
                MFCRepository.CreateMFCRepository(filterContext.Controller.ControllerContext.HttpContext.User);
        }
    }
}

我将创建存储库的代码放在一个单独的方法中,以防其他属性在触发此属性之前可能想要使用(主要)用户。

答案 7 :(得分:0)

在MVC管道中从构造函数调用太快了。

将代码移动到OnAuthorization,您将获得参数中的授权用户。为我工作!

从你的例子我会做这样的事情:

public abstract class ApplicationController : Controller {
    private IUserRepository _repUser;

    protected override void OnAuthorization(AuthorizationContext filterContext)
    {
        _repUser = RepositoryFactory.getUserRepository();
        var loggedInUser = _repUser.FindById(filterContext.HttpContext.User.Identity.Name); //Problem!
        ViewData["LoggedInUser"] = loggedInUser;
    }


}

答案 8 :(得分:0)

如果在构造函数中需要IPrincipal,请注入User

 // startup.cs
 // Inject IPrincipal
 services.AddTransient<IPrincipal>(provider => provider.GetService<IHttpContextAccessor>().HttpContext.User);

然后在您的构造函数中添加为IPrincipal。请注意,使用ASPNET可以保证ClaimsPrincipal是因为HttpContext.User就是这样。

Similar question

答案 9 :(得分:-2)

Select Project -> press F4 -> anonymous login -> false  | windows authentication - > True