如何为ASP.NET Core WebAPI的v0用DataContract、v1用默认序列化器?
实现不同API版本使用不同序列化器的方案
当然可以实现!而且结合Startup配置和FormatFilter(或者更灵活的自定义策略)就能轻松搞定你的需求,我给你梳理一下具体的实现步骤:
1. 先搭好基础:配置API版本控制和序列化器
首先在Startup.cs(.NET 6+的话就是Program.cs)里先把API版本控制和需要的序列化器都注册好:
public void ConfigureServices(IServiceCollection services) { // 配置API版本控制,设置默认版本为v0,同时开启版本报告 services.AddApiVersioning(options => { options.DefaultApiVersion = new ApiVersion(0, 0); options.AssumeDefaultVersionWhenUnspecified = true; options.ReportApiVersions = true; }); // 添加Mvc服务,同时注册WCF兼容的DataContract序列化器 services.AddControllers(options => { // 注册XML版的DataContract序列化器(和WCF的XmlDataContractSerializer一致) options.OutputFormatters.Add(new XmlDataContractSerializerOutputFormatter()); options.InputFormatters.Add(new XmlDataContractSerializerInputFormatter(options)); // 如果v0需要JSON格式也用WCF的DataContractJSONSerializer,就加上下面这俩 options.OutputFormatters.Add(new DataContractJsonSerializerOutputFormatter()); options.InputFormatters.Add(new DataContractJsonInputFormatter()); // 新版的System.Text.Json和XmlSerializer是默认自带的,v1会自动用这些 }) // 可选:如果v1需要新版XML序列化器,加上这个 .AddXmlSerializerFormatters(); }
2. 给v0版本指定用DataContract序列化器
这里有两种方式,一种是用FormatFilter直接标记,另一种是用自定义过滤器自动识别版本,你按需选:
方式一:用FormatFilter属性快速标记
这种适合你想明确指定某些Action/控制器用旧序列化器的场景。首先我们可以给DataContract序列化器绑定专属的媒体类型,这样更容易区分:
在Startup的格式化器配置里加一段:
services.AddControllers(options => { // 给XML版DataContract序列化器绑定v0专属媒体类型 var xmlDcFormatter = new XmlDataContractSerializerOutputFormatter(); xmlDcFormatter.SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/vnd.v0+xml")); options.OutputFormatters.Add(xmlDcFormatter); var xmlDcInputFormatter = new XmlDataContractSerializerInputFormatter(options); xmlDcInputFormatter.SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/vnd.v0+xml")); options.InputFormatters.Add(xmlDcInputFormatter); // JSON版的DataContract序列化器同理 var jsonDcFormatter = new DataContractJsonSerializerOutputFormatter(); jsonDcFormatter.SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/vnd.v0+json")); options.OutputFormatters.Add(jsonDcFormatter); var jsonDcInputFormatter = new DataContractJsonInputFormatter(); jsonDcInputFormatter.SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/vnd.v0+json")); options.InputFormatters.Add(jsonDcInputFormatter); });
然后在v0的控制器上加上FormatFilter和对应的Produces/Consumes属性:
[ApiVersion("0")] [Route("api/v{version:apiVersion}/[controller]")] [FormatFilter] [Produces("application/vnd.v0+json", "application/vnd.v0+xml")] [Consumes("application/vnd.v0+json", "application/vnd.v0+xml")] public class LegacyController : ControllerBase { [HttpPost] public IActionResult Post([FromBody] LegacyDataContractModel model) { // 这里的序列化会自动用WCF兼容的DataContract序列化器 return Ok(model); } }
方式二:自定义过滤器自动识别版本(更省心)
如果不想每个v0的控制器都加属性,可以写个简单的过滤器,根据API版本自动切换序列化器:
先写个过滤器类:
public class VersionedFormatterFilter : IActionFilter { public void OnActionExecuting(ActionExecutingContext context) { // 获取当前请求的API版本 var requestedVersion = context.HttpContext.GetRequestedApiVersion(); // 如果是v0,标记要使用DataContract序列化器 if (requestedVersion?.MajorVersion == 0) { context.HttpContext.Items["UseDataContractFormatter"] = true; } } public void OnActionExecuted(ActionExecutedContext context) { // 这里可以加一些执行后的处理,比如日志啥的,可选 } }
然后在Startup里注册这个过滤器,同时修改格式化器的选择逻辑:
services.AddControllers(options => { // 注册自定义过滤器 options.Filters.Add<VersionedFormatterFilter>(); // 配置XML版DataContract序列化器,只有标记了v0才用它 var xmlDcFormatter = new XmlDataContractSerializerOutputFormatter(); xmlDcFormatter.CanWriteResult = context => { return context.HttpContext.Items.ContainsKey("UseDataContractFormatter") && (context.ContentType.MediaType == "application/xml" || context.ContentType.MediaType == "application/json"); }; options.OutputFormatters.Add(xmlDcFormatter); // 输入格式化器同理 var xmlDcInputFormatter = new XmlDataContractSerializerInputFormatter(options); xmlDcInputFormatter.CanRead = context => { return context.HttpContext.Items.ContainsKey("UseDataContractFormatter") && (context.ContentType.MediaType == "application/xml" || context.ContentType.MediaType == "application/json"); }; options.InputFormatters.Add(xmlDcInputFormatter); // JSON版的DataContract序列化器也按同样逻辑配置就行 });
这样v0的控制器只需要标记ApiVersion("0")就够了,完全不用额外的FormatFilter:
[ApiVersion("0")] [Route("api/v{version:apiVersion}/[controller]")] public class LegacyController : ControllerBase { [HttpPost] public IActionResult Post([FromBody] LegacyDataContractModel model) { return Ok(model); } }
3. v1版本直接用默认序列化器
v1的控制器啥额外配置都不用加,只要标记好ApiVersion("1"),ASP.NET Core会自动用默认的System.Text.Json或者新版XmlSerializer来序列化:
[ApiVersion("1")] [Route("api/v{version:apiVersion}/[controller]")] public class ModernController : ControllerBase { [HttpPost] public IActionResult Post([FromBody] ModernModel model) { return Ok(model); } }
几个关键注意点
- 模型属性要保留:v0的模型一定要保留原来的
[DataContract]、[DataMember]属性,这样DataContract序列化器才能和WCF的逻辑完全一致,比如字段的顺序、忽略未标记的属性这些。 - 媒体类型的匹配:如果用专属媒体类型的方式,客户端请求v0的时候要指定
Content-Type: application/vnd.v0+json或者application/vnd.v0+xml,返回的Accept头也要对应。 - 版本路由要正确:路由里一定要包含
v{version:apiVersion},这样API版本控制中间件才能正确识别请求的版本。
内容的提问来源于stack exchange,提问作者wdspider
相关产品推荐
相关产品推荐

