如何让ASP.NET Core Web API接受所有Content-Type请求?
解决方案:同时支持form-urlencoded和JSON请求体
针对你的场景,最直接且稳定的方案是拆分端点处理不同的Content-Type,同时共享核心业务逻辑,避免自定义格式化器的复杂度。
1. 拆分端点,分别处理两种请求格式
保留[ApiController]特性(它能提供自动模型验证、路由约定等便利),创建两个重载方法,分别指定支持的Content-Type:
[ApiController] [Route("api/v1/object")] public class ObjectController : ControllerBase { // 处理application/x-www-form-urlencoded格式的请求 [HttpPost] [Consumes("application/x-www-form-urlencoded")] public ObjectInfo CreateObjectFromForm([FromForm] ObjectData objectData) { return ProcessObjectCreation(objectData); } // 处理application/json或application/xml格式的请求 [HttpPost] [Consumes("application/json", "application/xml")] public ObjectInfo CreateObjectFromBody([FromBody] ObjectData objectData) { return ProcessObjectCreation(objectData); } // 核心业务逻辑,抽成内部方法复用 private ObjectInfo ProcessObjectCreation(ObjectData objectData) { // 这里写你的创建对象逻辑,比如数据持久化、返回结果等 return new ObjectInfo { /* 赋值逻辑 */ }; } }
方案优势
[Consumes]特性会让ASP.NET Core根据请求的Content-Type头自动路由到对应方法[FromForm]专门处理表单编码数据,[FromBody]处理JSON/XML格式,两者互不干扰- 核心逻辑复用,避免代码冗余,维护成本更低
2. 为什么去掉[ApiController]后JSON请求报错?
移除[ApiController]时,ASP.NET Core的默认模型绑定规则会改变:
- 普通控制器的参数默认从表单、路由、查询字符串绑定,不会自动读取请求体中的JSON
- 此时若要处理JSON请求,必须给参数加上
[FromBody]特性,但加了之后,ASP.NET Core会强制要求请求体是JSON/XML格式,导致form-urlencoded请求触发415错误 - 因此拆分端点是更清晰的解决方式,无需放弃
[ApiController]的便利特性
3. 无需回退到System.Net.Http
.NET 7使用Microsoft.AspNetCore.Mvc是完全正确的选择,回退到.NET Framework的System.Net.Http会引入不必要的兼容性问题,也无法利用ASP.NET Core的新特性。
备选方案:自定义输入格式化器(适合不愿拆分端点的场景)
如果你希望用同一个方法处理两种格式,可以自定义输入格式化器,让[FromBody]同时支持form-urlencoded:
步骤1:创建自定义输入格式化器
public class FormUrlEncodedToObjectInputFormatter : TextInputFormatter { public FormUrlEncodedToObjectInputFormatter() { SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/x-www-form-urlencoded")); SupportedEncodings.Add(Encoding.UTF8); SupportedEncodings.Add(Encoding.Unicode); } protected override bool CanReadType(Type type) { // 支持ObjectData类型,也可扩展为支持所有普通类 return typeof(ObjectData).IsAssignableFrom(type); } public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding) { var request = context.HttpContext.Request; using var reader = new StreamReader(request.Body, encoding); var formContent = await reader.ReadToEndAsync(); // 解析form-urlencoded为键值对 var formData = formContent.Split('&') .Select(kv => kv.Split('=')) .ToDictionary( kv => Uri.UnescapeDataString(kv[0]), kv => kv.Length > 1 ? Uri.UnescapeDataString(kv[1]) : string.Empty ); // 将键值对映射到ObjectData实例 var objectData = new ObjectData(); foreach (var prop in typeof(ObjectData).GetProperties()) { if (formData.TryGetValue(prop.Name, out var value) && !string.IsNullOrEmpty(value)) { try { prop.SetValue(objectData, Convert.ChangeType(value, prop.PropertyType)); } catch { /* 处理类型转换失败,比如记录日志 */ } } } return await InputFormatterResult.SuccessAsync(objectData); } }
步骤2:注册格式化器到MVC配置
在Program.cs中添加:
var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers(options => { // 将自定义格式化器插入到输入格式化器列表最前面,优先使用 options.InputFormatters.Insert(0, new FormUrlEncodedToObjectInputFormatter()); }); var app = builder.Build(); app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); app.Run();
步骤3:修改原端点
保留[ApiController]和[FromBody],此时该方法就能同时处理JSON和form-urlencoded请求:
[ApiController] [Route("api/v1/object")] [HttpPost] public ObjectInfo CreateObject([FromBody] ObjectData objectData) { // 业务逻辑 return new ObjectInfo(); }
内容的提问来源于stack exchange,提问作者Anna Aimeri
相关产品推荐
相关产品推荐

