2

I have the following form:

<form asp-action="GetUsersAPICall" asp-controller="UsersObject" asp-antiforgery="true" data-ajax="true" data-ajax-method="get" data-ajax-mode="replace" data-ajax-update="#userSearchResult">
    Enter email or name to search for: <input type="text" id="query" name="query"/>

    <input type="submit" value="Search" />
</form>

<div id="userSearchResult"></div>

(Yes, I do realize that I'm kind of mixing Unobtrusive AJAX syntax with ASP.NET Core tag helpers).

I have the following Action Method in my controller:

    [HttpPost]
    [Authorize(Roles = "PatchUser")]
    //[ValidateAntiForgeryToken]
    public async Task<IActionResult> PatchUserAPICall(UserPatchViewModel vm)
    {
        if (vm == null)
        {
            return BadRequest();
        }
        else if (!ModelState.IsValid)
        {
            return View(vm);
        }
        else
        {
            bool result = await vm.User.Update();

            if (result)
            {
                return RedirectToAction("Confirmation");
            }
            else
            {
                return StatusCode((int)HttpStatusCode.InternalServerError);
            }
        }
    }

This works just fine, unless I uncomment the ValidateAntiForgeryToken attribute.

I've seen examples of doing validation for jQuery AJAX calls, but many of those rely on sending it in the headers (e.g. in this Q&A). Supposedly, this will make the ValidateAntiForgeryToken just work as expected (with a few configuration changes in code). Hopefully I'm not missing something obvious, but I've searched quite a bit and can't find how to actually add a header in Unobtrusive AJAX, so I haven't even be able to try something like that to see if it would work.

I know that you can do User.IsInRole("RoleName") as an alternative to the Authorize attribute. Is there a way to send the Anti-Forgery Token as a parameter and do it that way? Or is there a way to edit the headers and do it that way? Or is there a better way to do this that I haven't thought of yet?

In general, is there some way to edit the Ajax call before it's sent?

4

1 回答 1

1

首先,你data-ajax-method="get"应该是data-ajax-method="post". 这是因为您使用 asp-antiforgery="true" 会将隐藏的 __RequestVerificationToken 添加到您的表单中。通常,此隐藏字段与您的其他表单数据一起提交。但是,__RequestVerificationToken 不能在查询字符串(即 GET)中提交,它必须在标头或正文中(即 POST)提交。如果您进行此更改,则整个事情可能会“正常工作”。

但是,也可以将 Antiforgery 令牌放入 ajax 请求的标头中。jquery unobtrusive ajax 并没有开箱即用,但添加起来并不难。在jquery.unobtrusive-ajax.js中,有一个函数,asyncRequest(element, options){..}在该函数的末尾,是实际的 jquery ajax 调用:

$.ajax(options);

在此调用之前,在链接代码的第 143 行,插入以下内容:

if (method === "POST") {
    var token = $("input[name='__RequestVerificationToken'][type='hidden']").val();
    if (token) {
        options.headers = { RequestVerificationToken: token };
    }
}

如果您使用<a>标签进行删除或其他操作,这也很有用:

@Html.AntiforgeryToken() //add the hidden antiforgery token, assuming you don't have a form tag.
<a href="#" data-ajax="true" data-ajax-method="post" 
  data-ajax-url="@Url.Action("Delete", new {Model.Id})" data-ajax-confirm="Are you sure?" 
  data-ajax-success="alert('success')">Delete This</a>
于 2020-04-27T23:57:34.663 回答