.net webapi 實現 介面版本控制並打通swagger支持

来源:https://www.cnblogs.com/berkerdong/archive/2022/07/13/16475921.html
-Advertisement-
Play Games

我們在開發 webapi 項目時如果遇到 api 介面需要同時支持多個版本的時候,比如介面修改了入參之後但是又希望支持老版本的前端(這裡的前端可能是網頁,可能是app,小程式 等等)進行調用,這種情況常見於 app,畢竟網頁前端我們可以主動控制發佈,只要統一發佈後所有人的瀏覽器下一次訪問網頁時都會重 ...


我們在開發 webapi 項目時如果遇到 api 介面需要同時支持多個版本的時候,比如介面修改了入參之後但是又希望支持老版本的前端(這裡的前端可能是網頁,可能是app,小程式 等等)進行調用,這種情況常見於 app,畢竟網頁前端我們可以主動控制發佈,只要統一發佈後所有人的瀏覽器下一次訪問網頁時都會重新載入到最新版的代碼,但是像 app 則無法保證用戶一定會第一時間升級更新最新版的app,所以往往需要 api介面能夠同時保持多個版本的邏輯,同支持新老版本的調用端app進行調用。

針對上面的描述舉一個例子:

比如一個創建用戶的介面,api/user/createuser

如果我們這個時候對該介面的入參和返回參數修改之後,但是又希望原本的 api/user/createuser 介面邏輯也可以正常運行,常見的做法有以下幾種:

  1. 修改介面名稱,將新的創建用戶介面地址定義為 api/user/newcreateuser
  2. url傳入版本標記,將新的創建用戶介面地址定義為 api/user/createuser?api-version=2
  3. header傳入版本標記,通過校驗 header 中的 api-version 欄位的值,用來區分調用不同版本的api

第一種方式的缺陷很明顯,當介面版本多了之後介面的地址會定義很亂,本文主要講解後面兩種方法,如何在 asp.net webapi 項目中優雅的使用 header 或者 query 傳入 版本標記,用來支持api的多個版本邏輯共存,並且擴展 Swagger 來實現 SwaggerUI 對於 api-version 的支持。

截至本文撰寫時間,最新的 .net 版本為 .net6 ,本文中的所有示例也是基於 .net 6 來構建的。

首先創建一個 asp.net webapi 項目,本文使用 vs2022 直接創建 asp.net webapi 項目

項目創建好之後安裝如下幾個nuget包:

Swashbuckle.AspNetCore

Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer

註冊 api 版本控制服務

            #region 註冊 api 版本控制

            builder.Services.AddApiVersioning(options =>
            {
                //通過Header向客戶端通報支持的版本
                options.ReportApiVersions = true;

                //允許不加版本標記直接調用介面
                options.AssumeDefaultVersionWhenUnspecified = true;

                //介面預設版本
                //options.DefaultApiVersion = new ApiVersion(1, 0);

                //如果未加版本標記預設以當前最高版本進行處理
                options.ApiVersionSelector = new CurrentImplementationApiVersionSelector(options);

                //配置為從 Header 傳入 api-version
                options.ApiVersionReader = new HeaderApiVersionReader("api-version");

                //配置為從 Query 傳入 api-version
                //options.ApiVersionReader = new QueryStringApiVersionReader("api-version");

            });

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

            #endregion

這裡我們可以選擇 api-version 版本標記的傳入方式是從 url query 傳遞還是從 http header 傳遞。

移除項目預設的 swagger 配置

            // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
            builder.Services.AddEndpointsApiExplorer();
            builder.Services.AddSwaggerGen();

            // Configure the HTTP request pipeline.
            if (app.Environment.IsDevelopment())
            {
                app.UseSwagger();
                app.UseSwaggerUI();
            }

採用如下 swagger 配置

            #region 註冊 Swagger

            builder.Services.AddTransient<IConfigureOptions<SwaggerGenOptions>, SwaggerConfigureOptions>();

            builder.Services.AddSwaggerGen(options =>
            {
                options.OperationFilter<SwaggerOperationFilter>();

                options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, $"{typeof(Program).Assembly.GetName().Name}.xml"), true);
            });

            #endregion

其中用到了兩個自定義的類 SwaggerConfigureOptions 和 SwaggerOperationFilter ,

SwaggerConfigureOptions 是一個自定義的 Swagger 配置方法,主要用於根據 api 控制器上的描述用來迴圈添加不同版本的 SwaggerDoc;

SwaggerOperationFilter 是一個自定義過濾器主要實現SwaggerUI 的版本參數 api-version 必填驗證和標記過期的 api 的功能,具體內容如下

SwaggerConfigureOptions .cs

    /// <summary>
    /// 配置swagger生成選項。
    /// </summary>
    public class SwaggerConfigureOptions : IConfigureOptions<SwaggerGenOptions>
    {
        readonly IApiVersionDescriptionProvider provider;


        public SwaggerConfigureOptions(IApiVersionDescriptionProvider provider) => this.provider = provider;



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

                var modelPrefix = Assembly.GetEntryAssembly()?.GetName().Name + ".Models.";
                var versionPrefix = description.GroupName + ".";
                options.SchemaGeneratorOptions = new SchemaGeneratorOptions { SchemaIdSelector = type => (type.ToString()[(type.ToString().IndexOf("Models.") + 7)..]).Replace(modelPrefix, "").Replace(versionPrefix, "").Replace("`1", "").Replace("+", ".") };
            }
        }

        static OpenApiInfo CreateInfoForApiVersion(ApiVersionDescription description)
        {
            var info = new OpenApiInfo()
            {
                Title = Assembly.GetEntryAssembly()?.GetName().Name,
                Version = "v" + description.ApiVersion.ToString(),
                //Description = "",
                //Contact = new OpenApiContact() { Name = "", Email = "" }
            };

            if (description.IsDeprecated)
            {
                info.Description += "此 Api " + info.Version + " 版本已棄用,請儘快升級新版";
            }

            return info;
        }
    }

SwaggerOperationFilter.cs

    /// <summary>
    /// swagger 集成多版本api自定義設置
    /// </summary>
    public class SwaggerOperationFilter : IOperationFilter
    {

        public void Apply(OpenApiOperation operation, OperationFilterContext context)
        {
            var apiDescription = context.ApiDescription;

            //判斷介面遺棄狀態,對介面進行標記調整
            operation.Deprecated |= apiDescription.IsDeprecated();

            if (operation.Parameters == null)
            {
                return;
            }


            //為 api-version 參數添加必填驗證
            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;
            }
        }
    }

這些都配置完成之後,開始對 控制模塊進行調整

為了方便代碼的版本區分,所以我這裡在 Controllers 下按照版本建立的獨立的文件夾 v1 和 v2

然後在 v1 和 v2 的文件夾下防止了對於的 Controllers,如下圖的結構

 

然後只要在對應文件夾下的控制器頭部加入版本標記

[ApiVersion("1")] [ApiVersion("2")] [ApiVersion("......")]

如下圖的兩個控制器

​ 這樣就配置好了兩個版本的 UserController 具體控制器內部的代碼可以不同,然後運行 項目觀察 Swagger UI 就會發現如下圖:

  

​ 可以通過 SwaggerUI 右上角去切換各個版本的 SwaggerDoc

 

 ​點擊單個介面的 Try it out 時介面這邊也同樣會出現一個 api-version 的欄位,因為我們這邊是配置的從 Header 傳入該參數所以從界面中可以看出該欄位是從 Header 傳遞的,如果想要從 url 傳遞,主要調整上面 註冊 api 版本控制服務 那邊的設置為從 Query 傳入即可。

至此基礎的 api 版本控制邏輯就算完成了。

下麵衍生講解一下如果 項目中有部分 api 控制器並不需要版本控制,是全局通用的如何處理,有時候我們一個項目中總會存在一些基礎的 api 是基本不會變的,如果每次 api 版本升級都把所有的 控制器都全部升級顯然太過繁瑣了,所以我們可以把一些全局通用的控制器單獨標記出來。

只要在這些控制器頭部添加 [ApiVersionNeutral] 標記即可,添加了 [ApiVersionNeutral] 標記的控制器則表明該控制器退出了版本控制邏輯,無論 app 前端傳入的版本號的是多少,都可以正常進入該控制的邏輯。如下

    [ApiVersionNeutral]
    [ApiController]
    [Route("api/[controller]")]
    public class FileController : ControllerBase
    {

    }

還有一種就是當我們的 api 版本升級之後,我們希望標記某個 api 已經是棄用的,則可以使用 Deprecated 來表示該版本的 api 已經淘汰。

    [ApiVersion("1", Deprecated = true)]
    [ApiController]
    [Route("api/[controller]")]
    public class UserController : ControllerBase
    {

        [HttpPost("CreateUser")]
        public void CreateUser(DtoCreateUser createUser)
        {

            //內部註冊邏輯此處省略
        }

    }

添加淘汰標記之後運行 SwaggerUI 就會出現下圖的樣式

 

 

​ 通過 SwaggerDoc 就可以很明確的看出 v1 版本的 api 已經被淘汰了。

至此 關於asp.net core webapi 中 api 版本控制的基本操作就講解完了,有任何不明白的,可以在文章下麵評論或者私信我,歡迎大家積極的討論交流,有興趣的朋友可以關註我目前在維護的一個 .net 基礎框架項目 https://github.com/berkerdong/NetEngine.git


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 因為有些主題的原因,一些新加欄目不能按照需求,在首頁調出部分列表數據。我們可以這樣做: 1.找到該主題的include.php文件,在該文件最後添加代碼如下: 註意: San_Tiger_GetArticleCategorys函數名中,需要將 San_Tiger換成網站所正在用的主題名 functi ...
  • instanceof instanceof是Java 的保留關鍵字。 它的作用是測試它左邊的對象是否是它右邊的類的實例,返回 boolean 的數據類型。 類的實例包含本身的實例,以及所有直接或間接子類的實例 instanceof左邊顯式聲明的類型與右邊操作元必須是同種類或存在繼承關係,也就是說需要 ...
  • 來源:https://juejin.cn/post/6844903954308939784 導語 自從畢業後,今年已經是我工作的第 8 個年頭了,我甚至都快忘記了到底是哪年畢業的。 從出來,本人一直在做 Java 相關的工作,現在終於有時間坐下來,寫一篇關於 Java 寫法的一篇文章,來探討一下如果 ...
  • java入門 java誕生過程:1972年誕生c語言,因為指針和記憶體管理複雜,難以移植性。1982年產生了改進後的c++但是還是很複雜,於是在1995年就建立了java語言。 java優點: 語法有點像c 沒有指針 沒有記憶體管理 運行在jvm上,實現了真正的一次編譯到處運行 面向對象 類型安全 …… ...
  • 前言 大家早好、午好、晚好吖~ 環境使用: Python 3.8 Pycharm 2021.2版本 ffmpeg <需要設置環境變數> 模塊使用: import requests >>> pip install requests 內置模塊 你安裝好python環境就可以了 import re imp ...
  • JDBC簡單一句話,就是用java代碼去控制資料庫,對資料庫進行增刪改查 JDBC 的相關API 總結 最常用是阿裡巴巴的德魯伊資料庫連接池技術 資料庫連接步驟 必須先創建資料庫哈 引入德魯伊的jar包 加入配置文件(properties),要放在src目錄下,根據一些提示信息去做相應的配置,如果是 ...
  • 用Python檢測用戶輸入密碼的複雜度,灰常簡單! 密碼強度檢測規則: 至少包含一個數字 至少包含一個大寫字母 長度至少 8 位 主要知識點 while 迴圈 推導式 列表 any 函數 命令行 input 代碼部分 密碼強度檢測 1、創建 python 文件 密碼強度檢測規則 1 至少包含一個數字 ...
  • SparkSubmit提交流程分析 tips:分析基於如下執行命令開始 ./spark-submit \ --class org.apache.spark.examples.SparkPi \ --master yarn \ --deploy-mode cluster \ ./examples/ja ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...