我问了一个相关的问题,但是搞砸了标题,没有人会理解它。由于我现在能够更准确地提出这个问题,我决定在一个新问题中重新制定它并关闭旧问题。对不起。
所以我想做的是将数据(我的自定义用户的昵称存储在数据库中)传递给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 ;-)而且我在这里错过了一个简单而重要的观点。任何人都可以给我一些提示吗?我真的很感激。
答案 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
就是这样。
答案 9 :(得分:-2)
Select Project -> press F4 -> anonymous login -> false | windows authentication - > True