尝试从AccountController以外的其他控制器注册用户会出错

时间:2013-11-21 19:44:14

标签: asp.net-mvc asp.net-mvc-5 asp.net-identity

完全搞砸了我的上一个问题,所以发布了一个新问题。

MyTestController:

    [HttpPost]
    public async Task<ActionResult> Index(MyTestViewModel viewModel)
    {
        if (ModelState.IsValid)
        {
                AccountController ac = new AccountController();

                var user = new ApplicationUser()
                {
                    UserName = viewModel.Email
                };
                var result = await ac.UserManager.CreateAsync(user, viewModel.Password);
                if (result.Succeeded)
                {
                    await ac.SignInAsync(user, isPersistent: true);
                }
                else
                {
                    ac.AddErrors(result);
                }

SignInAsync中的AccountController方法(将其从private更改为public):

    public async Task SignInAsync(ApplicationUser user, bool isPersistent)
    {
        AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
        var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
        AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
    }

尝试注册用户时,它会出现以下错误:

Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.

Source Error: 


Line 411:            get
Line 412:            {
Line 413:                return HttpContext.GetOwinContext().Authentication;
Line 414:            }
Line 415:        }

AccountController中的那些行:

    private IAuthenticationManager AuthenticationManager
    {
        get
        {
            return HttpContext.GetOwinContext().Authentication;
        }
    }

AccountController中的所有内容都是默认的MVC 5 app。

无法从其他控制器调用这些方法,就像上面的例子一样?

为什么我在第413行得到NullReferenceException

2 个答案:

答案 0 :(得分:3)

从另一个Controller调用Controller方法很困难,因为HttpContext等属性需要正确初始化。这通常由MVC框架完成,该框架使用ControllerFactory创建控制器,并且在此过程中的某个时刻,在控制器上调用受保护的方法Initialize,以确保设置HttpContext属性。 这就是你在第413行得到例外的原因,因为在你使用new运算符创建的控制器上没有调用Initialize方法。

我认为重构您想要分享的功能会更容易。 例如。如果AccountController和MyTestController都拥有对此类

的引用
public class AccountManager
{
    public UserManager<ApplicationUser> UserManager { get; private set; }
    public HttpContextBase HttpContext { get; private set; }

    public AccountManager()
        : this(new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext())))
    {
    }

    public AccountManager(UserManager<ApplicationUser> userManager)
    {
        UserManager = userManager;
    }

    public void Initialize(HttpContextBase context)
    {
        HttpContext = context;
    }

    private IAuthenticationManager AuthenticationManager
    {
        get
        {
            return HttpContext.GetOwinContext().Authentication;
        }
    }

    public async Task SignInAsync(ApplicationUser user, bool isPersistent)
    {
        AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
        var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
        AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
    }
}

然后您可以像这样修改AccountController

public class AccountController : Controller
{
    public AccountController()
        : this(new AccountManager())
    {
    }

    public AccountController(AccountManager accountManager)
    {
        AccountManager = accountManager;
    }

    protected override void Initialize(System.Web.Routing.RequestContext requestContext)
    {
        base.Initialize(requestContext);
        AccountManager.Initialize(this.HttpContext);
    }

    public UserManager<ApplicationUser> UserManager 
    {
        get
        {
            return AccountManager.UserManager;
        }

    }

    public AccountManager AccountManager { get; private set; }

你的MyTestController就像这样:

public class MyTestController : Controller
{
    public MyTestController ()
        : this(new AccountManager())
    {
    }

    public MyTestController (AccountManager accountManager)
    {
        AccountManager = accountManager;
    }

    protected override void Initialize(System.Web.Routing.RequestContext requestContext)
    {
        base.Initialize(requestContext);
        AccountManager.Initialize(this.HttpContext);
    }

    public AccountManager AccountManager { get; private set; }

    [HttpPost]
    public async Task<ActionResult> Index(MyTestViewModel viewModel)
    {
       if (ModelState.IsValid)
       {  
            var user = new ApplicationUser()
            {
                UserName = viewModel.Email
            };
            var result = await AccountManager.UserManager.CreateAsync(user, viewModel.Password);
            if (result.Succeeded)
            {
                await AccountManager.SignInAsync(user, isPersistent: true);
            }
            else
            {
                AddErrors(result); //don't want to share this a it updates ModelState which belongs to the controller.
            }

<强>更新

不得不做出轻微的改变:

我必须更改UserManager属性,因为Dispose方法使用setter方法:

private UserManager<ApplicationUser> _userManager;
public UserManager<ApplicationUser> UserManager
{
    get { return AccountManager.UserManager; }

    private set { _userManager = value; }
}

protected override void Dispose(bool disposing)
{
    if (disposing && UserManager != null)
    {
        UserManager.Dispose();
        UserManager = null;
    }
    base.Dispose(disposing);
}

我必须将AddErrors方法添加到MyTestController(正如您指出我们不想共享该方法):

private void AddErrors(IdentityResult result)
{
    foreach (var error in result.Errors)
    {
        ModelState.AddModelError("", error);
    }
}

我将此行重新添加到AccountManager类的AccountManager属性中(与问题无关,但我在项目中已将其与此相关)

UserManager.UserValidator = new UserValidator(UserManager) { AllowOnlyAlphanumericUserNames = false };

答案 1 :(得分:0)

对我来说,在将当前的ControllerContext设置为AccountControllerContext之后,它就像一个魅力。不确定这种方法是否有任何缺点。

//This is employee controller class
    public ActionResult Create([Bind(Include = "EmployeeId,FirstName,LastName,DOJ,DOB,Address,City,State,Mobile,Landline,ReportsTo,Salary")] Employee employee)
    {
        if (ModelState.IsValid)
        {
            AccountController accountController = new AccountController();
            accountController.ControllerContext = this.ControllerContext;
            //accountController.UserManager;
            var userId = accountController.RegisterAccount(new RegisterViewModel { Email = "temp@temp.com", Password = "Pravallika!23" });
            if (!string.IsNullOrEmpty(userId))
            {
                employee.UserId = userId;                    
                employee.CreatedBy = User.Identity.GetUserId();
                db.Employees.Add(employee);
                db.SaveChanges();
                return RedirectToAction("Index");
            }                
        }

//customized method in AccountController
    public string RegisterAccount(RegisterViewModel model)
    {
        if (ModelState.IsValid)
        {
            var user = new ApplicationUser() { UserName = model.Email, Email = model.Email };                
            IdentityResult result = UserManager.Create(user, model.Password);
            //to add roles
            //UserManager.AddToRole(user.Id, "Admin");
            if (result.Succeeded)
            {
                return user.Id;
            }
            else
            {
                AddErrors(result);
            }
        }
        // If we got this far, something failed
        return null;
    }