<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Dotnet Core多版本API共存的優(yōu)雅實現(xiàn)

          共 12084字,需瀏覽 25分鐘

           ·

          2020-12-23 22:39

          API升級,新舊版本的API共存,怎么管理呢?

          ?

          一、前言

          最近,單位APP做了升級,同步的,API也做了升級。

          升級過程中,出現(xiàn)了一點問題:API升級后,舊API也需要保留,因為有舊的APP還在使用中。

          那么,API端如何作到多個版本共存呢?

          二、快速的解決辦法

          API的露出,是在API的Route定義中實現(xiàn)的。看下面的例子:

          [Route("api/[controller]")]
          public class DemoController : ControllerBase
          {
              [Route("demo")]
              public ActionResult  DemoFunc()
              {
              }
          }

          那我們知道,這個API的終結(jié)點是:/api/demo/demo。代碼中[controller]是個可替換變量,編譯時會替換為當前控制器的名稱。

          這個Route,里面的參數(shù)是個字符串,也就是說是可以隨便換的。所以,對于多版本API,有個快速的辦法,就是在里面做文章。

          我們可以寫成:

          [Route("api/v1/[controller]")]
          public class DemoController : ControllerBase
          {
              [Route("demo")]
              public ActionResult  DemoFunc()
              {
              }
          }

          或者

          [Route("api/[controller]")]
          public class DemoController : ControllerBase
          {
              [Route("v1/demo")]
              public ActionResult  DemoFunc()
              {
              }
          }

          這樣就區(qū)分出了版本號。

          ?

          當然,這樣做比較LOW,因為版本號是硬編碼在代碼中的。而且,這個改動會影響到API的終結(jié)點,例如上面兩個變化,會讓終結(jié)點變?yōu)椋?code style="font-size: inherit;line-height: inherit;word-wrap: break-word;padding: 2px 4px;border-top-left-radius: 4px;border-top-right-radius: 4px;border-bottom-right-radius: 4px;border-bottom-left-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background-color: rgb(248, 248, 248);">/api/v1/demo/demo和/api/demo/v1/demo。如果前端可以方便修改,也算是一個方法。但對于我們APP已經(jīng)上線運行來說,這個改動無法接受。

          三、優(yōu)雅的解決辦法

          這個方案,才是今天要說的核心內(nèi)容。

          ?

          首先,我們需要從Nuget上引入兩個庫:

          % dotnet add package Microsoft.AspNetCore.Mvc.Versioning
          % dotnet add package Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer

          這兩個庫,Versioning用來實現(xiàn)API的版本控制,Versioning.ApiExplorer用來實現(xiàn)元數(shù)據(jù)的發(fā)現(xiàn)工作。

          引入完成后,修改Startup.cs

          public void ConfigureServices(IServiceCollection services)
          {
              services.AddApiVersioning(options =>
              {
                  options.DefaultApiVersion = new ApiVersion(10);
                  options.AssumeDefaultVersionWhenUnspecified = true;
                  options.ReportApiVersions = true;
              });

              services.AddVersionedApiExplorer(options =>
              {
                  options.GroupNameFormat = "'v'VVV";
                  options.SubstituteApiVersionInUrl = true;
              });

              services.AddControllers();
          }

          就可以了。

          這里面用了兩個配置:AddApiVersioning,主要用來配置向前兼容,定義了如果沒有帶版本號的訪問,會默認訪問v1.0的接口。AddVersionedApiExplorer用來添加API的版本管理,并定義了版本號的格式化方式,以及兼容終結(jié)點上帶版本號的方式。

          到這兒,引入版本管理的工作就完成了。

          ?

          使用時,就直接在控制器或方法上定義版本號:

          [ApiVersion("1")]
          [Route("api/[controller]")]
          public class DemoController : ControllerBase
          {
              [MapToApiVersion("2")]
              [Route("demo")]
              public ActionResult  DemoFunc()
              {
              }
          }

          這里面,又是兩個屬性:ApiVersion定義控制器提供哪個版本的API。這個屬性可以定義多個。例如,我們控制器里既有v1的API,也有v2的API,我們可以寫成:

          [ApiVersion("1")]
          [ApiVersion("2")]
          [Route("api/[controller]")]
          public class DemoController : ControllerBase
          {
          }

          MapToApiVersion是API的版本定義,定義我們這個API是哪一個版本。

          方法就這么簡單。其它的,微軟都幫我們做好了。

          ?

          那,通常我們會用Swagger來做API文檔。這個方法如何跟Swagger配合呢?

          四、與Swagger的配合

          Swagger也來自于Nuget的引用:

          % dotnet add package swashbuckle.aspnetcore

          引用后,通常我們Startup.cs里的配置是這樣的:

          public void ConfigureServices(IServiceCollection services)
          {
              services.AddSwaggerGen(option =>
              {
                  option.SwaggerDoc("v1"new OpenApiInfo { Title = "Demo", Version = "V1" });
              });

              services.AddControllers();
          }
          public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
          {
              app.UseSwagger();
              app.UseSwaggerUI(option =>
              {
                  option.SwaggerEndpoint("/swagger/v1/swagger.json""Demo");
              });

          }

          ?

          API多版本管理與Swagger配合,也有一個快速但比較LOW的方法:

          public void ConfigureServices(IServiceCollection services)
          {
              services.AddSwaggerGen(option =>
              {
                  option.SwaggerDoc("v1"new OpenApiInfo { Title = "Demo", Version = "V1" });
                  option.SwaggerDoc("v1"new OpenApiInfo { Title = "Demo", Version = "V2" });

              });

              services.AddControllers();
          }
          public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
          {
              app.UseSwagger();
              app.UseSwaggerUI(option =>
              {
                  option.SwaggerEndpoint("/swagger/v1/swagger.json""Demo V1");
                  option.SwaggerEndpoint("/swagger/v2/swagger.json""Demo V2");
              });
          }

          這個方法也可以快速實現(xiàn),不過跟上邊的情況一樣,版本號是硬編碼的。

          ?

          其實,也有另一個比較優(yōu)雅的方式,就是手動實現(xiàn)IConfigureOptions 和過濾IOperationFilter

          先看Startup.cs里:

          public void ConfigureServices(IServiceCollection services)
          {
              services.AddTransient , ConfigureSwaggerOptions>();
              services.AddSwaggerGen(options => options.OperationFilter ());

              services.AddControllers();
          }
          public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
          {
              app.UseSwagger();
              app.UseSwaggerUI(option =>
              {
                  foreach (var description in provider.ApiVersionDescriptions)
                  {
                      c.SwaggerEndpoint($ "/swagger/{description.GroupName}/swagger.json", description.GroupName.ToUpperInvariant());
                  }
              });
          }

          這里加了兩個類,第一個ConfigureSwaggerOptions

          internal class ConfigureSwaggerOptions : IConfigureOptions
               
                
          {
               private readonly IApiVersionDescriptionProvider _provider;
               public ConfigureSwaggerOptions(IApiVersionDescriptionProvider provider) => _provider = provider;

               public void Configure(SwaggerGenOptions options)
              
          {
                  foreach (var description in _provider.ApiVersionDescriptions)
                  {
                      options.SwaggerDoc(description.GroupName, CreateInfoForApiVersion(description));
                  }
              }

               private OpenApiInfo CreateInfoForApiVersion(ApiVersionDescription description)
              
          {
                  var info =  new OpenApiInfo()
                  {
                      Title =  "Demo API",
                      Version = description.ApiVersion.ToString(),
                  };

                   if (description.IsDeprecated)
                  {
                      info.Description +=  " 方法被棄用.";
                  }

                   return info;
              }
          }

          第二個SwaggerDefaultValues

          internal class SwaggerDefaultValues : IOperationFilter
          {
              public void Apply(OpenApiOperation operation, OperationFilterContext context)
              
          {
                      var apiDescription = context.ApiDescription;
                      operation.Deprecated |= apiDescription.IsDeprecated();

                      if (operation.Parameters == null)
                      return;

                      foreach (var parameter in operation.Parameters)
                      {
                      var description = apiDescription.ParameterDescriptions.First(p => p.Name == parameter.Name);
                      if (parameter.Description == null)
                      {
                          parameter.Description = description.ModelMetadata?.Description;
                      }

                      if (parameter.Schema.Default == null && description.DefaultValue != null)
                      {
                          parameter.Schema.Default = new OpenApiString(description.DefaultValue.ToString());
                      }

                      parameter.Required |= description.IsRequired;
                  }
              }
          }

          代碼不一行行解釋了,都是比較簡單的。

          ?

          運行,進入Swagger界面,右上角Select a definition,可以選擇我們定義的版本號。?

          今天的配套代碼已上傳到Github,位置在:https://github.com/humornif/Demo-Code/tree/master/0035/demo

          回復 【關(guān)閉】關(guān)
          回復 【實戰(zhàn)】獲取20套實戰(zhàn)源碼
          回復 【被刪】
          回復 【訪客】
          回復 【小程序】學獲取15套【入門+實戰(zhàn)+賺錢】小程序源碼
          回復 【python】學微獲取全套0基礎(chǔ)Python知識手冊
          回復 【2019】獲取2019 .NET 開發(fā)者峰會資料PPT
          回復 【加群】加入dotnet微信交流群

          再見,VIP,臥槽又來一個看片神器!


          你應(yīng)該知道.NET Core的8點總結(jié)!



          瀏覽 95
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美一级精品在线视频免费 | 特黄特色特刺激免费播放 | 无码在线视频播放 | 尹人大香蕉网 | 五月天婷婷久久 |