为啥我的抽象基本控制器中的用户(如 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 如何为用户-地图-属性一次在链接表中保存多条记录?