How to move Account scaffoldingto Controller?

49 Views Asked by At

I am trying to move the register page that was scaffolded by ASP.net core to a controller so I can have a better control on the way I open it and for organisation reasons. Where the view was previously in

Areas

  • Identity -- Pages --- Account

now, I want it to be at

Views -Account

And I want to open this view using an AccountController.

I tried opening it; starting the model; calling some methods, just like this:

[HttpGet]
public async Task<IActionResult> RegisterAsync()
{
    var model = new RegisterModel(_userManager, _userStore, _signInManager, _logger, _emailSender);
    model.OnGetAsync("Home/Index").Wait() ;
    return View("Register", model);
}

Everytime I call the action on the controller, I get an exception in the View itself that the model is null.

What should I do?

1

There are 1 best solutions below

0
Ruikai Feng On BEST ANSWER

When you try with MVC ,the services shouldn't be required for your viewmodel ,also, you have to move the codes inside your Razor Page Handlers into ccontroller,don't call the handler directly(model.OnGetAsync("Home/Index")),a minial example here:

Models:

public class InputModel
    {
        
        [Required]
        [EmailAddress]
        [Display(Name = "Email")]
        public string? Email { get; set; }

       
        [Required]
        [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
        [DataType(DataType.Password)]
        [Display(Name = "Password")]
        public string? Password { get; set; }
        
        [DataType(DataType.Password)]
        [Display(Name = "Confirm password")]
        [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
        public string? ConfirmPassword { get; set; }
    }

    public class RegisterViewModel
    {
        public InputModel Input { get; set; } = default!;


        public string? ReturnUrl { get; set; }

        
        public IList<AuthenticationScheme>? ExternalLogins { get; set; }
    }

View:

@model RegisterViewModel

<div class="row">
    <div class="col-md-4">
        <form id="registerForm" asp-route-returnUrl="@Model?.ReturnUrl" method="post">
            <h2>Create a new account.</h2>
            <hr />
            <div asp-validation-summary="ModelOnly" class="text-danger" role="alert"></div>
            <div class="form-floating mb-3">
                <input asp-for="Input.Email" class="form-control" autocomplete="username" aria-required="true" placeholder="[email protected]" />
                <label asp-for="Input.Email">Email</label>
                <span asp-validation-for="Input.Email" class="text-danger"></span>
            </div>
            <div class="form-floating mb-3">
                <input asp-for="Input.Password" class="form-control" autocomplete="new-password" aria-required="true" placeholder="password" />
                <label asp-for="Input.Password">Password</label>
                <span asp-validation-for="Input.Password" class="text-danger"></span>
            </div>
            <div class="form-floating mb-3">
                <input asp-for="Input.ConfirmPassword" class="form-control" autocomplete="new-password" aria-required="true" placeholder="password" />
                <label asp-for="Input.ConfirmPassword">Confirm Password</label>
                <span asp-validation-for="Input.ConfirmPassword" class="text-danger"></span>
            </div>
            <button id="registerSubmit" type="submit" class="w-100 btn btn-lg btn-primary">Register</button>
        </form>
    </div>
    <div class="col-md-6 col-md-offset-2">
        <section>
            <h3>Use another service to register.</h3>
            <hr />
            @{
                if ((Model?.ExternalLogins?.Count ?? 0) == 0)
                {
                    <div>
                        <p>
                            There are no external authentication services configured. See this <a href="https://go.microsoft.com/fwlink/?LinkID=532715">
                                article
                                about setting up this ASP.NET application to support logging in via external services
                            </a>.
                        </p>
                    </div>
                }
                else
                {
                    <form id="external-account" asp-action="./ExternalLogin" asp-route-returnUrl="@Model.ReturnUrl" method="post" class="form-horizontal">
                        <div>
                            <p>
                                @foreach (var provider in Model.ExternalLogins!)
                                {
                                    <button type="submit" class="btn btn-primary" name="provider" value="@provider.Name" title="Log in using your @provider.DisplayName account">@provider.DisplayName</button>
                                }
                            </p>
                        </div>
                    </form>
                }
            }
        </section>
    </div>
</div>

@section Scripts {
    <partial name="_ValidationScriptsPartial" />
}

Controller:

public class AccountController : Controller
    {
        private readonly SignInManager<AppUser> _signInManager;
        private readonly UserManager<AppUser> _userManager;
        private readonly IUserStore<AppUser> _userStore;
        private readonly IUserEmailStore<AppUser> _emailStore;       
        private readonly IEmailSender _emailSender;
        public  AccountController
            (
            UserManager<AppUser> userManager,
            IUserStore<AppUser> userStore,
            SignInManager<AppUser> signInManager,            
            IEmailSender emailSender
            ) 
        {
            _userManager = userManager;
            _userStore = userStore;
            _emailStore = GetEmailStore();
            _signInManager = signInManager;
            
            _emailSender = emailSender;
        }
        [HttpGet]
        public async Task<IActionResult> Register(string returnUrl = null)
        {
            var registermodel = new RegisterViewModel {
                ReturnUrl = returnUrl,
                ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList()
            };
            return View(registermodel);
        }
        [HttpPost]
        public async Task<IActionResult> Register(RegisterViewModel registerViewModel)
        {
            registerViewModel.ReturnUrl ??= Url.Content("~/");
            registerViewModel.ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
            if (ModelState.IsValid)
            {
                var user = CreateUser();

                await _userStore.SetUserNameAsync(user, registerViewModel.Input.Email, CancellationToken.None);
                await _emailStore.SetEmailAsync(user, registerViewModel.Input.Email, CancellationToken.None);
                var result = await _userManager.CreateAsync(user, registerViewModel.Input.Password);

                if (result.Succeeded)
                {
                    

                    var userId = await _userManager.GetUserIdAsync(user);
                    var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
                    code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
                    var callbackUrl = Url.Page(
                        "/Account/ConfirmEmail",
                        pageHandler: null,
                        values: new { area = "Identity", userId = userId, code = code, returnUrl = registerViewModel.ReturnUrl },
                        protocol: Request.Scheme);

                    await _emailSender.SendEmailAsync(registerViewModel.Input.Email, "Confirm your email",
                        $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");

                    if (_userManager.Options.SignIn.RequireConfirmedAccount)
                    {
                        
                        return RedirectToAction("RegisterConfirmation", new { email = registerViewModel.Input.Email, returnUrl = registerViewModel.ReturnUrl });
                    }
                    else
                    {
                        await _signInManager.SignInAsync(user, isPersistent: false);
                        return LocalRedirect(registerViewModel.ReturnUrl);
                    }
                }
                foreach (var error in result.Errors)
                {
                    ModelState.AddModelError(string.Empty, error.Description);
                }
            }

            return View(registerViewModel);
        }

        public IActionResult RegisterConfirmation()
        {
            //migrate from razor page to MVC yourself
            return Ok("Ok");
        }


        private AppUser CreateUser()
        {
            try
            {
                return Activator.CreateInstance<AppUser>();
            }
            catch
            {
                throw new InvalidOperationException($"Can't create an instance of '{nameof(AppUser)}'. " +
                    $"Ensure that '{nameof(AppUser)}' is not an abstract class and has a parameterless constructor, or alternatively " +
                    $"override the register page in /Areas/Identity/Pages/Account/Register.cshtml");
            }
        }

        private IUserEmailStore<AppUser> GetEmailStore()
        {
            if (!_userManager.SupportsUserEmail)
            {
                throw new NotSupportedException("The default UI requires a user store with email support.");
            }
            return (IUserEmailStore<AppUser>)_userStore;
        }
    }

Result: enter image description here