.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
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...