为啥我的抽象基本控制器中的用户(如 User.Identity.Name)为空?

Posted

技术标签:

【中文标题】为啥我的抽象基本控制器中的用户(如 User.Identity.Name)为空?【英文标题】: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 ;-),我在这里遗漏了一个简单但重要的点。谁能给我一些提示?我真的很感激。

【问题讨论】:

【参考方案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 问题的方法

以上是关于为啥我的抽象基本控制器中的用户(如 User.Identity.Name)为空?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我应该只用护照序列化用户存储 user.id? [复制]

CakePHP 如何为用户-地图-属性一次在链接表中保存多条记录?

为啥我的用户数据没有使用资源控制器中的 show 方法传递到页面视图?

link_to嵌套资源路由找不到ID

如何仅为休息控制器更改基本网址?

为啥我的 UIPageViewController 中的页面没有改变?