ASP.NET Core – Web API Versioning

前言

项目持续维护, API 就需要版本控制. ASP.NET Core 有官方的插件专门处理 API 版本控制.

 

主要参考

Your Guide to REST API Versioning in ASP.NET Core

Asp.Net.Core WebApi 4种版本控制的方式

How to use API versioning in ASP.NET Core

ASP.NET Core WebApi版本控制的实现 (有讲到 swagger 哦)

Asp.Net Core 5 - API Versioning

Github aspnet-api-versioning

Docs – aspnet-api-versioning(最新版本,上面的是旧版本的参考)

 

 

安装 nuget

dotnet add package Asp.Versioning.Mvc

 

Program.cs

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddApiVersioning(options =>
{
    options.AssumeDefaultVersionWhenUnspecified = true; // 如果请求没有声明就使用默认版本
    options.DefaultApiVersion = new ApiVersion(1, 0); // 默认版本
    options.ReportApiVersions = true; // 在 header 返回支持的版本 (尤其用于 deprecated 弃用的 API)
}).AddMvc();
//builder.Services.AddSwaggerGen();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
    //app.UseSwagger();
    //app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();

主要是 .AddApiVersioning 的配置.

注释掉 Swagger 是因为 versioning 会导致 Swagger 需要额外配置. 看这篇: Swagger API Versioning.

 

两种管理方式

第一种方式: 一个 controller 多个 version 

namespace WebApiVersioning.Controllers
{
    [ApiController]
    // 表示这个 Controller 支持 1.0 和 2.0
    [ApiVersion("1.0", Deprecated = true)] // Deprecated 表示废弃了, 会在返回的 header 表示, 虽然是废弃了, 但是依然会跑和 response 的哦 (自行处理)
    [ApiVersion("2.0")]
    [Route("api/v{version:apiVersion}/[controller]")] // 声明 version in path
    public class WeatherForecastController : ControllerBase
    {
        [HttpGet(Name = "GetWeatherForecast"), MapToApiVersion("1.0")] // 通过 MapToApiVersion 声明这个 action 是 for 哪个 version 
        public IEnumerable<WeatherForecast> Get_v1() // 名字是 v1
        {
            return Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Date = DateTime.Now.AddDays(index),
                TemperatureC = Random.Shared.Next(-20, 55),
                Summary = Summaries[Random.Shared.Next(Summaries.Length)] + " v1"
            })
            .ToArray();
        }

        [HttpGet(Name = "GetWeatherForecast"), MapToApiVersion("2.0")]
        public IEnumerable<WeatherForecast> Get_v2() // 名字是 v2
        {
            return Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Date = DateTime.Now.AddDays(index),
                TemperatureC = Random.Shared.Next(-20, 55), 
                Summary = Summaries[Random.Shared.Next(Summaries.Length)] + " v2"
            })
            .ToArray();
        }

        private static readonly string[] Summaries = new[]
        {
            "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
        };
    }
}

请求 v1.0 就会执行 Get_v1, v2.0 就是 Get_v2

header response 会表示 api-supported-versions 和 api-deprecated-versions (如果有).

如果都没有 deprecated, 会是这样

 

第二种方式: 1 个 controller 一个 version

可以通过 namespace 或者 controller name 来设定版本号, 关键就是不要把 Action 放一起.

namespace WebApiVersioning.Controllers.v1
{
    [ApiController]
    [ApiVersion("1.0")]
    [Route("api/v{version:apiVersion}/[controller]")]
    public class WeatherForecastController : ControllerBase
    {
        [HttpGet(Name = "GetWeatherForecast")] 
        public IEnumerable<WeatherForecast> Get()
        {
            return Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Date = DateTime.Now.AddDays(index),
                TemperatureC = Random.Shared.Next(-20, 55),
                Summary = Summaries[Random.Shared.Next(Summaries.Length)] + " v1"
            })
            .ToArray();
        }
        private static readonly string[] Summaries = new[]
        {
            "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
        };
    }
}
namespace WebApiVersioning.Controllers.v2
{
    [ApiController]
    [ApiVersion("2.0")]
    [Route("api/v{version:apiVersion}/[controller]")]
    public class WeatherForecastController : ControllerBase
    {
        [HttpGet(Name = "GetWeatherForecast")]
        public IEnumerable<WeatherForecast> Get()
        {
            return Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Date = DateTime.Now.AddDays(index),
                TemperatureC = Random.Shared.Next(-20, 55),
                Summary = Summaries[Random.Shared.Next(Summaries.Length)] + " v2"
            })
            .ToArray();
        }
      
        private static readonly string[] Summaries = new[]
        {
            "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
        };
    }
}

两种方法各有所长. 并用是可以的.

 

Non-version

不需要 API versioning 的就加上 attribute

[ApiVersionNeutral]

 

Range Version

当我们发布新的 version 时,绝大部分的 API 是向后兼容的,所以在 define API version 时,我们会希望这样写 "^1.0" 表示 1.0 - 1.9 都可以用。

[HttpGet, MapToApiVersion("^1.0")]
public IEnumerable<Product> Get_v1()
{
  return [
    new () { Id = 1, Name = "p1" },
    new () { Id = 2, Name = "p1" }
  ];
}

但它不支持,你唯一能做的是写多几个 MapToApiVersion("1.1"), MapToApiVersion("1.2")

相关 feature request 很少就有提了 Github – API range attribute,并且他们也做了一个扩展来支持 PB.ITOps.AspNetCore.Versioning

不清楚还有没有人在维护。

 

posted @ 2021-10-25 14:02  兴杰  阅读(491)  评论(0编辑  收藏  举报