asp.net core 内容协商是 objectresult 默认行为,ok(todo) 支持 json/xml 因其返回 objectresult 并自动匹配 accept 头与已注册格式器;xml 需显式启用,且模型须兼容各格式器要求。

ASP.NET Core 的内容协商不是可选插件,而是 ObjectResult 的默认行为——只要客户端发了 Accept 头,框架就会自动尝试匹配并序列化为对应格式,不需要手动判断 MIME 类型或写分支逻辑。
为什么返回 Ok(todo) 就能支持 JSON/XML?
因为 Ok() 返回的是 ObjectResult 实例,它内置内容协商流程:读取请求头中的 Accept,遍历已注册的输出格式器(IOutputFormatter),按质量因子(q=0.9)和注册顺序选出最匹配的一个。默认注册了 JsonOutputFormatter 和 XmlDataContractSerializerOutputFormatter(需显式启用)。
实操建议:
- XML 格式器默认不启用,必须在
Program.cs中调用builder.Services.AddControllers().AddXmlDataContractSerializerFormatters() - 若只注册了 JSON 格式器,即使客户端传
Accept: application/xml,也会 fallback 到 JSON 并返回200 OK(不是 406) -
Content-Type响应头由选中的格式器自动设置,无需手动赋值
Accept 头没生效?检查这三处
常见现象:客户端明确传了 Accept: application/xml,但响应仍是 JSON,且 Content-Type 也是 application/json。
原因通常不是代码写错,而是配置或模型层面的问题:
- 控制器方法返回类型不能是
IEnumerable<t></t>或IQueryable<t></t>——XmlDataContractSerializer不支持接口序列化,会静默 fallback 到 JSON;改用List<t></t>或加[XmlRoot]/[XmlArray]特性 - 模型类缺少
[DataContract]或[Serializable],XML 序列化器可能跳过该类型,导致协商失败 - 没启用 XML 格式器,或启用顺序靠后,而 JSON 格式器已匹配成功(内容协商按注册顺序“短路”执行)
JSON 和 XML 字段名不一致?统一用显式命名
JSON 默认用 camelCase,XML 默认用 PascalCase,同一属性在两种格式下字段名不同,前端或第三方系统解析容易出错。
正确做法是关闭隐式策略,显式声明:
- 给属性同时加
[JsonPropertyName("id")]和[XmlElement("id")],而非只加一个 - 或全局禁用 JSON 的命名策略:
options.JsonSerializerOptions.PropertyNamingPolicy = null(注意:这会影响所有 JSON 输出) - 避免依赖
DefaultContractResolver或XmlSerializerNamespaces等旧机制,它们与当前System.Text.Json+DataContractSerializer双轨体系不兼容
真正容易被忽略的点是:内容协商发生在模型序列化之前,但序列化结果是否合法,取决于模型能否被目标格式器完整处理——XML 不认 IEnumerable、JSON 不认循环引用、两者对 null 值处理方式也不同。别只盯着 Accept 头,先确保模型本身对所有目标格式“友好”。











