【问题标题】:Why is User (as in User.Identity.Name) null in my abstract base controller?为什么我的抽象基本控制器中的用户(如 User.Identity.Name)为空?
【发布时间】:2009-01-10 07:27:26
【问题描述】:

我在问一个相关的问题,但把标题弄乱了,没有人能理解。由于我现在能够更准确地提出问题,因此我决定将其重新表述为一个新问题并关闭旧问题。很抱歉。

所以我想要做的是将数据(存储在数据库中的我的自定义用户昵称)传递给 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 可以为空,但不是用户本身。

在我看来 HttpContext 不可用(因为也是 null ;-),我在这里遗漏了一个简单但重要的点。谁能给我一些提示?我真的很感激。

【问题讨论】:

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


    【解决方案1】:

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

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

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

    【讨论】:

      【解决方案2】:

      直到 Controller 被实例化后才分配 User 属性,但您可以通过以下方式从构造函数获得早期访问权限:

      System.Web.HttpContext.Current.User
      

      【讨论】:

        【解决方案3】:

        我的猜测是Controller的基本构造函数没有填充User,而是稍后为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 在构造时为空。

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

        【讨论】:

        • 奇怪,因为User 为空,但System.Web.HttpContext.Current.User 不是。 MVC 1.0
        【解决方案4】:

        你能用类似的东西抓住这个吗:

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

        还是HttpContext总是空的??

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

        【讨论】:

          【解决方案5】:

          谢谢雷蒙德。我太累了,看不到明显的东西。 @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) 或钩子可能会有所帮助。

          为此干杯。

          【讨论】:

          • 我建议覆盖您的自定义 Controller 类的 Initialize 方法,并将您的代码从构造函数移到那里。
          【解决方案6】:

          我在基本控制器实现中执行此操作,它按预期工作。

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

          这对我来说总是返回真或假,所以User != null

          【讨论】:

          • 这是因为 User 对象在调用 LoggedOn 属性时已被填充。
          • 因为属性的getter不是控制器构造函数
          【解决方案7】:

          致大师: 我在您的帮助下做了类似的事情,希望对后来的访客有所帮助。 就我而言,我需要为不同的用户创建控制器的存储库,但在控制器的构造函数中,(主要)用户尚未准备好。所以我为控制器创建了一个属性:

          [CreateRepositoryByUser]
          public class MFCController : Controller
          {
              protected MFCRepository _repository
              {
                  get { return ViewData["repository"] as MFCRepository; }
              }
          ...
          

          _repository 确实不是控制器的私有变量,而是由属性创建的一些东西:

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

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

          【讨论】:

            【解决方案8】:

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

            【讨论】:

              【解决方案9】:

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

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

              然后在你的构造函数中添加IPrincipal。请注意,对于 ASPNET,它保证为 ClaimsPrincipal - 因为这就是 HttpContext.User

              Similar question

              【讨论】:

                【解决方案10】:
                Select Project -> press F4 -> anonymous login -> false  | windows authentication - > True
                

                【讨论】:

                • 这会启用 Windows 身份验证,这绝对不是解决 OP 问题的方法
                猜你喜欢
                • 2018-03-30
                • 1970-01-01
                • 1970-01-01
                • 2011-11-28
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多