如何让ASP.NET Core MVC绑定JSON请求中的简单数据类型?
正在将中型.NET 4 ASP.NET MVC应用迁移至.NET 7 ASP.NET Core MVC,大量控制器操作方法使用简单类型做数据绑定,示例代码如下:
public class HomeController : Controller { // ... public ActionResult Foo(string bar) { return Content(bar); } // ... }
在旧版ASP.NET MVC中,发送Content-Type: application/json; charset=utf-8的JSON请求:
{ "bar": "BAZ" }
用以下测试代码验证(实际请求来自前端,希望尽量少改前端):
var client = new HttpClient(); var content = new StringContent("{ \"bar\": \"BAZ\" }", new MediaTypeHeaderValue("application/json", "utf-8")); var response = await client.PostAsync("http://localhost:5261/Home/Foo", content); Console.WriteLine(await response.Content.ReadAsStringAsync());
旧版中参数能正确绑定并返回BAZ,但ASP.NET Core MVC中bar始终为null。尝试过添加[HttpPost]、[FromBody],仅当创建包含bar属性的复杂类型并加[FromBody]时才生效,也试过给控制器加[ApiController],但不想逐个创建大量「无用」复杂类型,希望找到像旧版那样直接绑定JSON字段到简单类型参数的方法。
以下几种方法可实现不创建复杂类型的前提下,完成JSON字段到简单类型参数的绑定:
方法1:使用JObject接收并手动提取值
修改操作方法,用JObject接收整个JSON请求体,手动提取对应字段的值,无需创建额外类型:
[HttpPost] public ActionResult Foo([FromBody] JObject payload) { string bar = payload["bar"]?.ToString(); return Content(bar); }
这种方式改动小,适合快速适配,无需全局配置,直接修改目标方法即可。
方法2:自定义全局模型绑定器
如果有大量这类方法需要适配,可以自定义模型绑定器,让ASP.NET Core自动将JSON中的对应字段绑定到简单类型参数:
- 创建自定义模型绑定器:
public class SimpleJsonParameterBinder : IModelBinder { public Task BindModelAsync(ModelBindingContext bindingContext) { if (bindingContext == null) throw new ArgumentNullException(nameof(bindingContext)); // 仅处理POST/PUT的JSON请求 var request = bindingContext.HttpContext.Request; if (!request.ContentType?.StartsWith("application/json", StringComparison.OrdinalIgnoreCase) ?? true) { bindingContext.Result = ModelBindingResult.Failed(); return Task.CompletedTask; } // 读取请求体 using var reader = new StreamReader(request.Body); var json = reader.ReadToEnd(); if (string.IsNullOrEmpty(json)) { bindingContext.Result = ModelBindingResult.Failed(); return Task.CompletedTask; } // 解析JSON并提取与参数名匹配的字段 var jObj = JObject.Parse(json); var parameterName = bindingContext.FieldName; var value = jObj[parameterName]; if (value != null) { var targetType = bindingContext.ModelType; var convertedValue = value.ToObject(targetType); bindingContext.Result = ModelBindingResult.Success(convertedValue); } else { bindingContext.Result = ModelBindingResult.Failed(); } return Task.CompletedTask; } }
- 创建模型绑定提供器:
public class SimpleJsonParameterBinderProvider : IModelBinderProvider { public IModelBinder GetBinder(ModelBinderProviderContext context) { if (context == null) throw new ArgumentNullException(nameof(context)); // 为简单类型(非复杂类型、非集合)提供绑定器 if (context.Metadata.IsSimpleType && !context.Metadata.IsCollectionType) { return new BinderTypeModelBinder(typeof(SimpleJsonParameterBinder)); } return null; } }
- 在
Program.cs中注册绑定器:
builder.Services.AddControllersWithViews(options => { // 将自定义绑定器添加到绑定器提供器列表的最前面,确保优先使用 options.ModelBinderProviders.Insert(0, new SimpleJsonParameterBinderProvider()); });
注册完成后,原来的方法只需添加[HttpPost]即可正常绑定:
[HttpPost] public ActionResult Foo(string bar) { return Content(bar); }
方法3:调整ApiController的绑定规则(仅适用于API控制器)
如果控制器标记了[ApiController],可以通过配置修改参数绑定源的优先级,让简单类型优先从请求体获取值:
在Program.cs中添加:
builder.Services.Configure<ApiBehaviorOptions>(options => { options.SuppressInferBindingSourcesForParameters = true; });
然后给简单类型参数添加[FromBody]:
[ApiController] public class HomeController : ControllerBase { [HttpPost] public ActionResult Foo([FromBody] string bar) { return Content(bar); } }
这种方式需要给每个简单类型参数添加[FromBody],但无需创建复杂类型。
内容的提问来源于stack exchange,提问作者Oskar Sjöberg

