You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 06:50:28