为何Ajax请求可正常绑定模型,XMLHttpRequest需加[FromBody]?
问题
我有如下简单的.NET Core后端接口:
[HttpPost] [AllowAnonymous] public JsonResult Test(TestRequestModel requestModel) { //do stuff return Json(new { result.Success }); }
对应的请求模型:
public class TestRequestModel { public string Email { get; set; } = string.Empty; }
我需要通过POST请求调用该接口,但因特殊原因必须使用XMLHttpRequest而非$.ajax。以下是两种调用方式:
调用入口函数:
function MyTestFunction() { let parameters = { Email: "test@test.com" } CallServer(function () { //Do stuff }, function () { //Do other stuff }, "/Home/Test", parameters) }
Ajax方式(可正常工作):
function CallServer(resolve, reject, path, parameters) { $.ajax({ type: "POST", url: path, data: AddAntiForgeryToken(parameters), success: function (response) { //do stuff }, error: function (xhr) { //do stuff }, complete: function () { //do more stuff } }); }
XMLHttpRequest方式(模型创建但Email字段未填充,添加[FromBody]后正常):
function CallServer(resolve, reject, path, parameters) { let xhr = new XMLHttpRequest(); xhr.open("POST", path, true); xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8"); xhr.setRequestHeader('RequestVerificationToken', GetMyToken()); xhr.onreadystatechange = function (e) { if (xhr.readyState === XMLHttpRequest.DONE) { if (xhr.status === 200 || xhr.status === 201) { //do stuff } else { //do other stuff } } }; xhr.send(JSON.stringify(parameters)); }
已知$.ajax底层基于XMLHttpRequest,请问为何XMLHttpRequest请求需添加[FromBody]才能正确绑定模型?是否可修改XMLHttpRequest代码,无需添加[FromBody]即可正常请求?
解答
为什么XMLHttpRequest请求需要[FromBody]
两种请求的Content-Type和数据格式完全不同,.NET Core的模型绑定规则会根据这两点决定数据源:
- $.ajax默认会把
parameters序列化为application/x-www-form-urlencoded格式(类似Email=test%40test.com&__RequestVerificationToken=xxx),并自动设置对应Content-Type。.NET Core默认优先从**请求表单(Form)**或URL查询字符串绑定模型,不需要额外标记。 - 你写的XMLHttpRequest代码主动设置了Content-Type为
application/json,且发送JSON字符串。这种情况下,.NET Core不会自动读取请求体中的JSON绑定模型,必须通过[FromBody]显式告知框架:从请求体的JSON数据中绑定该模型。
修改XMLHttpRequest代码,无需[FromBody]即可正常请求
要和$.ajax行为一致,只需将数据序列化为application/x-www-form-urlencoded格式,匹配.NET Core默认的模型绑定规则:
方式1:使用FormData(最贴近$.ajax默认逻辑)
function CallServer(resolve, reject, path, parameters) { let xhr = new XMLHttpRequest(); // 构建FormData,模拟$.ajax的参数序列化逻辑 let formData = new FormData(); for (let key in parameters) { if (parameters.hasOwnProperty(key)) { formData.append(key, parameters[key]); } } // 添加防伪令牌 formData.append('__RequestVerificationToken', GetMyToken()); xhr.open("POST", path, true); // 无需手动设置Content-Type,XMLHttpRequest会自动为FormData配置正确的请求头 xhr.onreadystatechange = function (e) { if (xhr.readyState === XMLHttpRequest.DONE) { if (xhr.status === 200 || xhr.status === 201) { //do stuff } else { //do other stuff } } }; xhr.send(formData); }
方式2:手动拼接urlencoded字符串
function CallServer(resolve, reject, path, parameters) { let xhr = new XMLHttpRequest(); // 手动将参数转为urlencoded格式 let urlEncodedParams = new URLSearchParams(parameters); urlEncodedParams.append('__RequestVerificationToken', GetMyToken()); let paramsString = urlEncodedParams.toString(); xhr.open("POST", path, true); // 设置对应Content-Type xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8"); xhr.onreadystatechange = function (e) { if (xhr.readyState === XMLHttpRequest.DONE) { if (xhr.status === 200 || xhr.status === 201) { //do stuff } else { //do other stuff } } }; xhr.send(paramsString); }
以上两种写法都和$.ajax的默认请求格式一致,.NET Core会自动从表单数据中绑定模型,无需添加[FromBody]。
内容的提问来源于stack exchange,提问作者Bagzli
相关产品推荐
相关产品推荐

