2009-01-10 100 views
22

我在问一个相关的问题,但混淆了标题,没有人会理解它。由于我现在可以更精确地提出这个问题,因此我决定在一个新问题中重新提出并关闭旧问题。对不起。为什么User(如User.Identity.Name)在我的抽象基础控制器中为空?

所以我想要做的是将数据(我的自定义用户的昵称存储在数据库中)传递给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; 
     } 
    } 

这样,无论我派生Controller,用户信息已经存在。

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

我不能说User.Identity.Name因为User已经是空。在我的所有派生控制器中都不是这种情况,所以这是抽象基本控制器的特定情况。

我设置通过FormsAuthentication的User.Identity.Name在另一地点的代码,但我认为这不可能是问题 - 据我所知User.Identity.Name可以为空,但不是用户本身。

它看起来像我的HttpContext不可用(因为还空;-)和我缺少一个简单而重要的一点在这里。任何人都可以给我一些提示吗?我真的很感激。

回答

12

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

从我有MVC的代码(也预览版,但应该罚款): (在控制器)

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

...

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

我不请参阅代码中的默认构造函数的实现。 这将证明ControllerContext在构建时为null。

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

+7

奇怪,因为`User`为null,但`System.Web.HttpContext.Current.User`不是。 MVC 1.0 – 2009-10-22 18:57:15

4

你能抓住这个使用类似:

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

抑或是的HttpContext总是空的?

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

4

谢谢Raimond。我太累了,看不明显。 @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)或挂钩可能会有所帮助。

干杯。

+1

我建议覆盖您的自定义控制器类的Initialize方法,并将您的代码从构造函数移动到那里。 – jcmcbeth 2011-02-10 15:19:37

0

我在基本控制器实现中这样做,它按预期工作。

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

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

+0

这是因为在调用LoggedOn属性时填充了User对象。 – jcmcbeth 2011-02-10 15:17:07

21

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

所以我所做的是重写onActionExecuting()在基础控制器类(我创建了一个自定义的属性,但只是重写该方法也应该),然后从那里做我的用户查找。

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

+0

感谢发布您如何解决 – JeremyWeir 2009-03-25 07:42:22

0

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

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

我把创建的存储库的代码一个独立的方法,在其他属性可能希望在触发此属性之前使用(委托人)用户。

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


} 
11

用户属性不分配,直到控制器实例化后,但你可以从你的构造函数获得早期访问:

System.Web.HttpContext.Current.User 
相关问题