Asp.net mvc 使用uu RequestVerificationToken对嵌套视图模型进行远程验证
我有一个名为“LoginIndexViewModel”的viewmodel类,用于登录页面,包含登录、登录和忘记密码表单。它包含多个属性,每个属性分别是一个viewmodel。以下是“LoginIndexViewModel”视图模型:Asp.net mvc 使用uu RequestVerificationToken对嵌套视图模型进行远程验证,asp.net-mvc,antiforgerytoken,remote-validation,Asp.net Mvc,Antiforgerytoken,Remote Validation,我有一个名为“LoginIndexViewModel”的viewmodel类,用于登录页面,包含登录、登录和忘记密码表单。它包含多个属性,每个属性分别是一个viewmodel。以下是“LoginIndexViewModel”视图模型: public class LoginIndexViewModel { public LoginViewModel Login { get; set; } public SignUpViewModel SignUp { get; set; }
public class LoginIndexViewModel
{
public LoginViewModel Login { get; set; }
public SignUpViewModel SignUp { get; set; }
public ForgetPasswordViewModel ForgetPassword { get; set; }
}
在“SignUpViewModel”中,有一个属性具有远程验证,我想在调用操作方法之前检查防伪令牌。以下是“SignUpViewModel”的主体:
我在razor视图的操作方法“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);
}
[HttpPost]
[仅限AjaxOnly]
[异名]
[ValidateAntiForgeryToken]
[OutputCache(Location=OutputCacheLocation.None,NoStore=true)]
公共虚拟异步任务检查用户名([Bind(Prefix=“SignUp.UserName”)]字符串用户名)
{
返回Json(wait _userManager.CheckUsername(username),JsonRequestBehavior.AllowGet);
}
razor视图:
@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>
.
.
.
}
@使用(Html.BeginForm(MVC.Account.ActionNames.Register、MVC.Account.Name、FormMethod.Post、new{id=“RegisterForm”、@class=“m-login\u form m-form”、role=“form”}))
{
@Html.AntiForgeryToken()
.
.
.
@Html.TextBoxFor(m=>m.SignUp.UserName,新的{@class=“form control”,placeholder=“Email*”,autocomplete=“off”,“@Value=”“})
@Html.ValidationMessageFor(model=>model.SignUp.UserName)
.
.
.
}
问题是远程验证调用引发异常:所需的防伪表单字段“\uu RequestVerificationToken”不存在。
根据Mozilla调试器工具,我发现CheckUsername调用的参数是“SignUp.\uuu RequestVerificationToken”而不是“uuuu RequestVerificationToken”,因此会引发异常
任何人都知道发生了什么以及为什么uu RequestVerificationToken没有提供?将前缀添加到
AdditionalFields
中定义的属性是经过设计的,并且是由jquery.validate.unobtrusive.js
添加的。添加前缀的相关代码位于适配器中。add(“remote”、[“url”、“type”、“additionalfields”]、function(options){
方法
有很多方法可以解决你的问题
基于现有源代码创建您自己的自定义(例如)[ValidateAntiForgeryTokenWithPrefix]
属性,但修改以从请求中删除任何前缀(不推荐)
进行您自己的ajax调用,而不是使用[Remote]
属性,即处理文本框的.blur()
事件来调用服务器方法,传递值和令牌,并更新成功回调中由@Html.ValidationMessageFor()
生成的占位符,并处理.keyup()
事件以清除任何消息。这确实具有性能优势,因为在初始验证之后,远程
规则会对每个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>
....
}
然后可以从CheckUsername()
方法中省略[Bind(Prefix=“SignUp.UserName”)]
或者,您可以基于sayLoginViewModel
创建主视图,然后使用@Html.Action()
调用[ChildActionOnly]
方法,返回其他两个表单的部分视图
话虽如此,用户只会使用注册
表单一次,并且可能永远不会使用伪造密码
表单,因此包含所有额外的html只会降低性能,最好有链接将它们重定向到注册
和伪造密码
的单独页面,或者如果您想要我在同一个页面中,然后使用ajax根据需要为它们加载部分
作为补充说明,在返回Json(…)
(它是[HttpPost]
方法)中不需要JsonRequestBehavior.AllowGet
参数,并且在使用HtmlHelper
方法时不应该设置值
属性
@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