从ASP.NET MVC控制器构造函数访问时,IPrincipal属性为null

时间:2017-01-10 15:31:05

标签: c# asp.net asp.net-mvc iprincipal

以下示例代码抛出NullReferenceException消息Object reference not set to an instance of an object.。该错误指的是User属性null

基本控制器

public class BaseController : Controller
{
    public string UserName
    {
        get
        {
            return User.Identity.Name;
        }
    }
}

家庭控制器

[Authorize]
public class HomeController : BaseController
{
    private string username { get; set; }

    public HomeController()
    {
        username = UserName;
    }
    // GET: Home
    public ActionResult Index()
    {
        ViewBag.UserName = username;
        return View();
    }
}

另一方面,如果直接在操作中访问UserName属性,那么它可以正常工作。因此,如果代码更改为:

,则代码可以正常工作

家庭控制器

[Authorize]
public class HomeController : BaseController
{
    // GET: Home
    public ActionResult Index()
    {
        ViewBag.UserName = this.UserName;
        return View();
    }
}

任何想法为什么?

更新

因此,真正的问题是在构造控制器期间HttpContext不可用。我真的好奇为什么?任何解释?另外,如果我需要使用构造函数DI来实例化依赖类,该依赖类又需要知道当前登录的用户,例如:

[Authorize]
public class HomeController : BaseController
{
    private IMyService service;

    public HomeController(IMyService service)
    {
        this.service = service;
        this.service.UserName = UserName;
    }

    // GET: Home
    public ActionResult Index()
    {
        // use service here ...
        return View();
    }
}

2 个答案:

答案 0 :(得分:4)

您无权访问构造函数中的HttpContext。如果您想要一个地方添加这样的检索逻辑,您可以通过以下方式之一来完成。

直接呼叫基地控制器

从基本控制器访问UserName属性,无需将其重置为派生控制器上的属性或字段。

[Authorize]
public class HomeController : BaseController
{
    // GET: Home
    public ActionResult Index()
    {
        ViewBag.UserName = UserName;
        return View();
    }
}

挂钩进入OnActionExecuting而不是ctor

Controller.OnActionExecuting而不是构造函数中设置值,因为此时您可以访问HttpContext。在方法中添加override即可。

扩展方法

添加方法扩展名。我更喜欢这种方法,因为我不喜欢控制器继承,只是继承标准的Mvc控制器。

public static class ControllerExtension {
  public static string UserName(this Controller controller) {
    return controller.User.Identity.Name;
  }
}

您的代码变为

[Authorize]
public class HomeController : Controller
{
    // GET: Home
    public ActionResult Index()
    {
        ViewBag.UserName = this.UserName();
        return View();
    }
}

修改

  

...其中构造函数DI用于实例化一个参数,该参数又具有需要设置的UserName属性...

您可能希望将HttpContextBase实例注入您的服务,并让它以这种方式检索经过身份验证的用户名。你不想要的是Controller实例需要为服务提供用户名,因为这会创建紧密耦合的代码并破坏SoC(关注点分离)。

class MyService : IMyService
{
    public string UserName {get;}
    // inject HttpContextBase
    public MyService(HttpContextBase context){
        this.UserName = context.User.Identity.Name;
    }
}

答案 1 :(得分:0)

这是因为你在构造函数中没有HttpContext。在实例化Controller之后才会分配User属性。

尝试在那里使用System.Web.HttpContext.Current.User