mcpField
A
api-versioning

api-versioning

github.com/codewithmukesh开发7912.9k 次安装12 周前更新
代码仓库

关于 api-versioning

一项 API 版本设计技能,介绍 URL 路径版本、端点分组和版本弃用,适用于规划新 API 或在保留现有版本的同时处理破坏性变更。

主要功能

  1. 01使用 URL 路径段标记 API 版本
  2. 02按 API 版本分组端点
  3. 03弃用版本并报告状态
  4. 04区分破坏性与兼容性变更

适用场景

  1. 01为公共 API 从一开始设置版本路由
  2. 02管理响应结构的破坏性变更
  3. 03弃用较旧的 API 版本
  4. 04让相关端点保持同一版本

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/codewithmukesh/dotnet-claude-kit --skill api-versioning

安装后在 Claude Code 中输入/api-versioning确认已加载。

详细介绍

API 版本控制

核心原则

  1. 从第一天起就进行版本控制 — 之后再添加版本控制会很麻烦。即使只有 v1,也应从一开始就在 URL 中包含版本号。
  2. 默认使用 URL 路径段进行版本控制 — /api/v1/orders 是最容易发现且最适合缓存的策略。
  3. 绝不破坏现有版本 — 对于破坏性变更,应新增版本。为旧版本设定弃用时间表。
  4. 对 API 进行版本控制,而非对单个端点进行版本控制 — 同一版本组中的所有端点都使用相同的版本号。

模式

使用 Asp.Versioning 进行设置
// Program.cs
builder.Services.AddApiVersioning(options =>
{
    options.DefaultApiVersion = new ApiVersion(1, 0);
    options.AssumeDefaultVersionWhenUnspecified = true;
    options.ReportApiVersions = true;
    options.ApiVersionReader = new UrlSegmentApiVersionReader();
})
.AddApiExplorer(options =>
{
    options.GroupNameFormat = "'v'VVV";
    options.SubstituteApiVersionInUrl = true;
});
URL 路径段版本控制(推荐)
var v1 = app.NewApiVersionSet()
    .HasApiVersion(new ApiVersion(1, 0))
    .Build();

var v2 = app.NewApiVersionSet()
    .HasApiVersion(new ApiVersion(2, 0))
    .Build();

app.MapGroup("/api/v{version:apiVersion}/orders")
    .WithApiVersionSet(v1)
    .WithTags("Orders")
    .MapOrderEndpointsV1();

app.MapGroup("/api/v{version:apiVersion}/orders")
    .WithApiVersionSet(v2)
    .WithTags("Orders")
    .MapOrderEndpointsV2();
标头版本控制(替代方案)
options.ApiVersionReader = new HeaderApiVersionReader("X-Api-Version");

// Client sends: X-Api-Version: 2.0
弃用某个版本
var v1 = app.NewApiVersionSet()
    .HasDeprecatedApiVersion(new ApiVersion(1, 0))
    .HasApiVersion(new ApiVersion(2, 0))
    .Build();

// Response headers will include: api-deprecated-versions: 1.0
特定版本的端点组
public static class OrderEndpointsV1
{
    public static RouteGroupBuilder MapOrderEndpointsV1(this RouteGroupBuilder group)
    {
        group.MapGet("/{id:guid}", GetOrderV1);
        group.MapPost("/", CreateOrderV1);
        return group;
    }

    private static async Task<Results<Ok<OrderResponseV1>, NotFound>> GetOrderV1(
        Guid id, ISender sender, CancellationToken ct)
    {
        // V1 response shape
        var result = await sender.Send(new GetOrder.Query(id), ct);
        return result.IsSuccess
            ? TypedResults.Ok(result.Value.ToV1())
            : TypedResults.NotFound();
    }
}

public static class OrderEndpointsV2
{
    public static RouteGroupBuilder MapOrderEndpointsV2(this RouteGroupBuilder group)
    {
        group.MapGet("/{id:guid}", GetOrderV2);
        group.MapPost("/", CreateOrderV2);
        return group;
    }

    private static async Task<Results<Ok<OrderResponseV2>, NotFound>> GetOrderV2(
        Guid id, ISender sender, CancellationToken ct)
    {
        // V2 response shape — includes new fields
        var result = await sender.Send(new GetOrder.Query(id), ct);
        return result.IsSuccess
            ? TypedResults.Ok(result.Value.ToV2())
            : TypedResults.NotFound();
    }
}

反模式

不要对单个端点进行版本控制
// BAD — inconsistent versioning within a group
app.MapGet("/api/v1/orders", ListOrdersV1);
app.MapGet("/api/v2/orders/{id}", GetOrderV2); // V2 only for this endpoint?

// GOOD — version the entire group
app.MapGroup("/api/v1/orders").MapOrderEndpointsV1();
app.MapGroup("/api/v2/orders").MapOrderEndpointsV2();
不要默认使用查询字符串进行版本控制
// BAD for REST APIs — version hidden in query string, not cache-friendly
GET /api/orders?api-version=2.0

// GOOD — version in URL, discoverable and cacheable
GET /api/v2/orders

决策指南

场景建议
新的公共 API从第一天起使用 URL 路径段版本控制
服务之间的内部 API使用标头版本控制(URL 更简洁)
响应结构发生破坏性变更新建版本
添加新的可选字段使用相同版本(向后兼容)
弃用某个版本标记为已弃用,设置终止日期,并记录迁移路径

评论

支持 Markdown