使用Fetch API携带__RequestVerificationToken调用带[ValidateAntiForgeryToken]接口失败求助
问题:使用Fetch API提交带防伪令牌的ASP.NET表单时返回400错误
我在ASP.NET后端的Action上使用了[ValidateAntiForgeryToken]特性,代码如下:
[HttpPost("new")] [ValidateAntiForgeryToken] public JsonResult NewUser(NewUserViewModel user) { // 业务逻辑代码 }
用jQuery AJAX提交时可以成功:
$.ajax({ url: window.location.pathname, type: 'POST', data: { Id: "1", UserName: "ajax", Password: "ajax", ConfirmPassword: "ajax", FullName: "ajax", Email: "ajax@ajax.is", Client: "ajax", Roles: ["ajax"], __RequestVerificationToken: document.querySelector('[name=__RequestVerificationToken]').value }, success: function (response) { alert('Tókst'); }, error: function () { alert('tókst ekki'); } });
但换成Fetch API提交就收到400错误,移除[ValidateAntiForgeryToken]后就能正常运行:
let response = await fetch(window.location.pathname, { method: 'POST', body: { Id: "1", UserName: "fetch", Password: "fetch", ConfirmPassword: "fetch", FullName: "fetch", Email: "fetch@fetch.is", Client: "fetch", Roles: ["fetch"], __RequestVerificationToken: document.querySelector('[name=__RequestVerificationToken]').value } });
请问需要补充什么才能让Fetch请求成功提交?
解决方案
这个问题我之前也碰到过,核心原因是Fetch API和jQuery AJAX在处理请求数据的方式上有本质差异:
- jQuery的
$.ajax默认会把对象数据序列化为application/x-www-form-urlencoded格式,自动设置对应的请求头,还能正确处理数组类型的参数(比如Roles: ["ajax"]会被转成Roles[]=ajax这种后端能识别的格式)。 - 而你直接把JS对象传给Fetch的
body时,浏览器会把它转换成字符串[object Object],后端完全无法解析,加上防伪令牌的传递不符合ASP.NET的预期,就触发了[ValidateAntiForgeryToken]的验证失败,返回400错误。
下面给你两种不需要修改后端的解决方案:
方法1:使用URLSearchParams序列化表单数据
这种方式和jQuery的默认行为一致,适合普通表单数据:
// 构建表单参数 const formParams = new URLSearchParams(); formParams.append('Id', '1'); formParams.append('UserName', 'fetch'); formParams.append('Password', 'fetch'); formParams.append('ConfirmPassword', 'fetch'); formParams.append('FullName', 'fetch'); formParams.append('Email', 'fetch@fetch.is'); formParams.append('Client', 'fetch'); // 数组参数需要逐个append,后缀加[]让后端识别 formParams.append('Roles[]', 'fetch'); // 添加防伪令牌 formParams.append('__RequestVerificationToken', document.querySelector('[name=__RequestVerificationToken]').value); // 发起Fetch请求 const response = await fetch(window.location.pathname, { method: 'POST', body: formParams // URLSearchParams会自动设置Content-Type为application/x-www-form-urlencoded,无需手动添加 });
方法2:使用FormData对象
如果你的表单包含文件上传或者更复杂的字段,用FormData更合适,后端同样能兼容:
const formData = new FormData(); formData.append('Id', '1'); formData.append('UserName', 'fetch'); formData.append('Password', 'fetch'); formData.append('ConfirmPassword', 'fetch'); formData.append('FullName', 'fetch'); formData.append('Email', 'fetch@fetch.is'); formData.append('Client', 'fetch'); formData.append('Roles[]', 'fetch'); formData.append('__RequestVerificationToken', document.querySelector('[name=__RequestVerificationToken]').value); const response = await fetch(window.location.pathname, { method: 'POST', body: formData // FormData会自动设置Content-Type为multipart/form-data,后端可正常解析 });
可选:用JSON格式提交(需修改后端)
如果你想改用JSON格式传递数据,需要同时修改前端和后端:
前端代码
const requestData = { Id: "1", UserName: "fetch", Password: "fetch", ConfirmPassword: "fetch", FullName: "fetch", Email: "fetch@fetch.is", Client: "fetch", Roles: ["fetch"] }; const response = await fetch(window.location.pathname, { method: 'POST', headers: { 'Content-Type': 'application/json', // 把防伪令牌放在请求头里,ASP.NET会自动读取这个头 'RequestVerificationToken': document.querySelector('[name=__RequestVerificationToken]').value }, body: JSON.stringify(requestData) });
后端修改
需要给Action的参数加上[FromBody]特性,告诉ASP.NET从请求体的JSON中解析数据:
[HttpPost("new")] [ValidateAntiForgeryToken] public JsonResult NewUser([FromBody] NewUserViewModel user) { // 业务逻辑代码 }
内容的提问来源于stack exchange,提问作者ðorvarður
相关产品推荐
相关产品推荐

