为什么值不保留在回传上?
Why don't values persist on Postback?
我的主页有一个产品选项列表,每个选项在注册时为用户提供不同的帐户级别。每个选项都显示一个 "Sign Up" 按钮,该按钮将用户带到 /Identity/Account/Register
页面进行注册。
我需要将用户选择的选项告知注册页面。
- 我无法使用会话,因为它显然已被拿走
- 我不能使用 Cookies,因为它显然已经被拿走了
- 当我提交表单时,ViewData 值不存在
- 当我提交表单时,查询字符串值不存在
- 当我提交表单时,全局变量值不存在
- 最初加载页面时我无法设置视图模型的属性(NullReferenceException)
当注册页面加载时,该值在那里,但当我提交表单时,它消失了。
我很茫然。我打算通过什么机制来获取这些必需的信息?
在大多数情况下,我的代码几乎只是标准的样板文件:
[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();
}
}
这是页面(出于某种原因,这是将页面与页面模型一起使用,而不是视图和控制器 - 当我添加身份时,它以这种方式构建)。
@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" />
}
注意:我不使用 Razor Pages,所以了解该技术的人可能会有更好的答案。
您可以在表单中使用隐藏字段来保留 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>
这也很容易通过会话 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");
...
}
请求后唯一存在的数据是与该请求一起发送的数据。即使在会话的情况下,关键的会话标识符也必须由客户端在请求中传输到服务器以恢复该会话。
您从数据库中检索到的任何数据都必须 posted 或再次查询。此外,不应 post 编辑不允许用户明确更改的任何数据。因此,大部分数据应该要重新查询。
至于用户选择的选项,当然应该 posted。但是,应该通过再次查询该数据库来获取所选计划和其他计划选项的实际详细信息。这将是必要的,例如,如果由于验证错误而需要重新显示 post 之后的表单。
我的主页有一个产品选项列表,每个选项在注册时为用户提供不同的帐户级别。每个选项都显示一个 "Sign Up" 按钮,该按钮将用户带到 /Identity/Account/Register
页面进行注册。
我需要将用户选择的选项告知注册页面。
- 我无法使用会话,因为它显然已被拿走
- 我不能使用 Cookies,因为它显然已经被拿走了
- 当我提交表单时,ViewData 值不存在
- 当我提交表单时,查询字符串值不存在
- 当我提交表单时,全局变量值不存在
- 最初加载页面时我无法设置视图模型的属性(NullReferenceException)
当注册页面加载时,该值在那里,但当我提交表单时,它消失了。
我很茫然。我打算通过什么机制来获取这些必需的信息?
在大多数情况下,我的代码几乎只是标准的样板文件:
[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();
}
}
这是页面(出于某种原因,这是将页面与页面模型一起使用,而不是视图和控制器 - 当我添加身份时,它以这种方式构建)。
@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" />
}
注意:我不使用 Razor Pages,所以了解该技术的人可能会有更好的答案。
您可以在表单中使用隐藏字段来保留 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>
这也很容易通过会话 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");
...
}
请求后唯一存在的数据是与该请求一起发送的数据。即使在会话的情况下,关键的会话标识符也必须由客户端在请求中传输到服务器以恢复该会话。
您从数据库中检索到的任何数据都必须 posted 或再次查询。此外,不应 post 编辑不允许用户明确更改的任何数据。因此,大部分数据应该要重新查询。
至于用户选择的选项,当然应该 posted。但是,应该通过再次查询该数据库来获取所选计划和其他计划选项的实际详细信息。这将是必要的,例如,如果由于验证错误而需要重新显示 post 之后的表单。