My home page has a list of product options each giving the user a different account level on sign up. Each option presents a "Sign Up" button which takes the user to the /Identity/Account/Register
page to sign up.
I need to communicate to the Register page which option the user selected.
When the Register page loads, the value is there, but when I submit the form, it disappears.
I'm at a loss. By what mechanism am I meant to get this required information across?
For the most part my code is pretty much just standard boilerplate stuff:
[AllowAnonymous]
public class RegisterModel : PageModel
{
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly UserManager<ApplicationUser> _userManager;
private readonly ILogger<RegisterModel> _logger;
private readonly IEmailSender _emailSender;
public RegisterModel(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager,
ILogger<RegisterModel> logger,
IEmailSender emailSender)
{
_userManager = userManager;
_signInManager = signInManager ?? throw new ArgumentNullException(nameof(signInManager));
_logger = logger;
_emailSender = emailSender;
}
[BindProperty]
public InputModel Input { get; set; }
[BindProperty]
public int AccountLevel { get; set; }
public string ReturnUrl { get; set; }
public void OnGet(string returnUrl = null, int acclevel = 1)
{
AccountLevel = acclevel;
ReturnUrl = returnUrl;
}
public async Task<IActionResult> OnPostAsync(string returnUrl = null, int acclevel = 0)
{
returnUrl = returnUrl ?? Url.Content("~/");
if (ModelState.IsValid)
{
if (acclevel == 0) throw new ArgumentException(nameof(acclevel));
Input.LicenseCount = acclevel * 10;
var user = new ApplicationUser { UserName = Input.Email, Email = Input.Email, Name = Input.FirstName, Surname = Input.Surname, PhoneNumber = Input.PhoneNumber, SaIdNumber = Input.IdNumber, LicensesCount = Input.LicenseCount };
var result = await _userManager.CreateAsync(user, Input.Password);
await _userManager.AddToRoleAsync(user, nameof(SystemRoles.AppUser));
if (result.Succeeded)
{
_logger.LogInformation("User created a new account with password.");
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
var callbackUrl = Url.Page(
"/Account/ConfirmEmail",
pageHandler: null,
values: new { userId = user.Id, code = code },
protocol: Request.Scheme);
await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
$"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
//await _signInManager.SignInAsync(user, isPersistent: false);
return LocalRedirect(returnUrl);
}
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
}
// If we got this far, something failed, redisplay form
return Page();
}
}
Here's the Page (this is using Pages with the PageModel for some reason, rather than Views and Controllers - it scaffolded this way when I added Identity).
@page
@model RegisterModel
@{
ViewData["Title"] = "Register";
}
<h1>@ViewData["Title"]</h1>
<div class="row">
<div class="col-md-4">
<form asp-route-returnUrl="@Model.ReturnUrl" method="post">
<h4>Create a new account.</h4>
<hr />
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="Input.FirstName"></label>
<input asp-for="Input.FirstName" class="form-control" />
<span asp-validation-for="Input.FirstName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Input.Surname"></label>
<input asp-for="Input.Surname" class="form-control" />
<span asp-validation-for="Input.Surname" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Input.PhoneNumber"></label>
<input asp-for="Input.PhoneNumber" class="form-control" />
<span asp-validation-for="Input.PhoneNumber" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Input.IdNumber"></label>
<input asp-for="Input.IdNumber" class="form-control" />
<span asp-validation-for="Input.IdNumber" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Input.Email"></label>
<input asp-for="Input.Email" class="form-control" />
<span asp-validation-for="Input.Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Input.Password"></label>
<input asp-for="Input.Password" class="form-control" />
<span asp-validation-for="Input.Password" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Input.ConfirmPassword"></label>
<input asp-for="Input.ConfirmPassword" class="form-control" />
<span asp-validation-for="Input.ConfirmPassword" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-primary">Register</button>
</form>
</div>
</div>
@section Scripts {
<partial name="_ValidationScriptsPartial" />
}
Note: I don't work with Razor Pages, so someone with knowledge in that technology might have a better answer.
You can use a hidden field in the form to keep the value on POST-back:
<form asp-route-returnUrl="@Model.ReturnUrl" method="post">
@Html.HiddenFor(model => model.AccountLevel)
@*OR, TagHelper way*@
<input asp-for="AccountLevel" type="hidden"/>
<h4>Create a new account.</h4>
....
</form>
This is also fairly easy to do with sessions after you enable them:
public void OnGet(string returnUrl = null, int acclevel = 1)
{
HttpContext.Session.SetInt32("AccountLevel", acclevel);
ReturnUrl = returnUrl;
}
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
int accountLevel = HttpContext.Session.GetInt32("AccountLevel");
...
}