我有一个名为“ LoginIndexViewModel”的viewmodel类,用于登录剃须刀页面,其中包含登录名,登录名和忘记密码的形式。它包含几个属性,每个属性分别是一个视图模型。这是“ LoginIndexViewModel”视图模型:
public class LoginIndexViewModel
{
public LoginViewModel Login { get; set; }
public SignUpViewModel SignUp { get; set; }
public ForgetPasswordViewModel ForgetPassword { get; set; }
}
在“ SignUpViewModel”上,有一个具有远程验证的属性,我想在操作方法调用之前检查防伪令牌。这是“ SignUpViewModel”的主体:
public class SignUpViewModel
{
.
.
.
[Display(Name = "Email *")]
[DataType(DataType.EmailAddress)]
[Required(ErrorMessage = "The Email Is Required.")]
[EmailAddress(ErrorMessage = "Invalid Email Address.")]
[RegularExpression("^[a-zA-Z0-9_\\.-]+@([a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,6}$", ErrorMessage = "Invalid Email Address.")]
[Remote("CheckUsername", "Account", ErrorMessage = "The Email Address Have Already Registered.", HttpMethod = "POST", **AdditionalFields = "__RequestVerificationToken")]
public string Username { get; set; }
.
.
.
}
我在剃刀视图上的操作方法“ CheckUsername”和@ Html.AntiForgeryToken()上方使用了[ValidateAntiForgeryToken]属性,例如:
[HttpPost]
[AjaxOnly]
[AllowAnonymous]
[ValidateAntiForgeryToken]
[OutputCache(Location = OutputCacheLocation.None, NoStore = true)]
public virtual async Task<JsonResult> CheckUsername([Bind(Prefix = "SignUp.UserName")]string userName)
{
return Json(await _userManager.CheckUsername(username), JsonRequestBehavior.AllowGet);
}
剃刀视图:
@using (Html.BeginForm(MVC.Account.ActionNames.Register, MVC.Account.Name, FormMethod.Post, new { id = "RegisterForm", @class = "m-login__form m-form", role = "form" }))
{
@Html.AntiForgeryToken()
.
.
.
<div class="form-group">
@Html.TextBoxFor(m => m.SignUp.UserName, new { @class = "form-control", placeholder = "Email *", autocomplete = "off", @Value = "" })
<span class="helper">@Html.ValidationMessageFor(model => model.SignUp.UserName)</span>
</div>
.
.
.
}
问题在于远程验证调用会引发异常:“所需的防伪表单字段“ __RequestVerificationToken”不存在。”
根据Mozilla调试器工具,我发现CheckUsername调用的是'SignUp .__ RequestVerificationToken'参数而不是'__RequestVerificationToken'参数,因此会引发异常。
任何人都知道发生了什么,为什么__RequestVerificationToken不提供?
答案 0 :(得分:1)
在AdditionalFields
中定义的属性中添加前缀是设计使然,并由jquery.validate.unobtrusive.js
添加。相关代码是在adapters.add("remote", ["url", "type", "additionalfields"], function (options) {
方法中添加前缀。
有很多选项可以解决您的问题
根据现有的源代码创建自己的自定义(例如)[ValidateAntiForgeryTokenWithPrefix]
属性,但进行修改以从请求中删除任何前缀(不建议)
进行自己的ajax调用,而不是使用[Remote]
属性-即处理文本框的.blur()
事件以调用服务器方法,同时传递值和令牌,并更新占位符由@Html.ValidationMessageFor()
在成功回调中生成,并处理.keyup()
事件以清除所有消息。这确实具有性能优势,因为在初始验证之后,remote
规则会在每个keyup()
事件上进行ajax调用,因此可能导致大量服务器和数据库调用。
例如,最简单的解决方案是基于LoginViewModel
,SignUpViewModel
和ForgetPasswordViewModel
创建3个局部,然后使用@Html.Partial()
从主视图中调用,例如>
_SignUpViewModel.cshtml
@model SignUpViewModel
....
@using (Html.BeginForm(MVC.Account.ActionNames.Register, MVC.Account.Name, FormMethod.Post, new { id = "RegisterForm", @class = "m-login__form m-form", role = "form" }))
{
@Html.AntiForgeryToken()
....
<div class="form-group">
@Html.TextBoxFor(m => m.UserName, new { @class = "form-control", placeholder = "Email *", autocomplete = "off" })
<span class="helper">
@Html.ValidationMessageFor(model => model.UserName)
</span>
</div>
....
}
并在主视图中
@model LoginIndexViewModel
....
@Html.Partial("_SignUpViewModel", Model.SignUp)
.... // ditto for Login and ForgetPassword properties
然后您可以从[Bind(Prefix = "SignUp.UserName")]
方法中省略CheckUsername()
。
或者,您基于说LoginViewModel
来创建主视图,然后使用@Html.Action()
调用[ChildActionOnly]
方法,该方法返回其他两种形式的部分视图。
已经说过,用户将只使用一次SignUp
形式,并且可能永远不会使用ForgetPassword
形式,因此包括所有这些额外的html只是降低性能,这会更好具有链接以将它们重定向到SignUp
和ForgetPassword
的单独页面,或者如果您希望它们在同一页面中,则可以使用ajax根据需要加载它们的部分。
作为旁注,您不需要在JsonRequestBehavior.AllowGet
中使用return Json(...)
参数(它是一种[HttpPost]
方法),并且在任何情况下都不要设置value
属性使用HtmlHelper
方法。