使用__RequestVerificationToken

时间:2018-10-31 01:46:25

标签: asp.net-mvc antiforgerytoken remote-validation

我有一个名为“ 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不提供?

1 个答案:

答案 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调用,因此可能导致大量服务器和数据库调用。

例如,最简单的解决方案是基于LoginViewModelSignUpViewModelForgetPasswordViewModel创建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只是降低性能,这会更好具有链接以将它们重定向到SignUpForgetPassword的单独页面,或者如果您希望它们在同一页面中,则可以使用ajax根据需要加载它们的部分。

作为旁注,您不需要在JsonRequestBehavior.AllowGet中使用return Json(...)参数(它是一种[HttpPost]方法),并且在任何情况下都不要设置value属性使用HtmlHelper方法。