WebApi的多版本管理


什么是 API 的多版本問題?Android 等 App 存在着多版本客戶端共存的問題:App 最新版已經升級到了 5.0 了,但是有的用戶手機上還運行着 4.8、3.9 甚至 2.2 版本的 App,由於早期沒有內置升級機制、用戶不會升級、用戶拒絕升級等原因,造成這些舊版本 App 也在運行。開發新版本 App 的時候,要給接口增加新的功能或者修改以前接口的規范,會造成舊版本 App 無法使用,因此在一定情況下會“保留舊接口的運行、新功能用新接口”,這樣就會存在多版本接口共存的問題。

 通常的做法是:舊版接口做一個代碼分支,除了進行 bug 修改外,舊版本接口不再做改動;新接口代碼繼續演化升級。在客戶端請求的時候帶着要請求的接口版本號,在服務器端選擇合適的版本代碼進行處理。

技術處理方法:

1、  (最推薦)不同版本用不同的域名:v1.api.rsfy.com、v2.api.rsfy.com、v3……;

2、  在 url、報文頭等中帶不同的版本信息,用 Nginx 等做反向代理服務器,然后將 http://api.rsfy.com/api/v1/User/1http://api.rsfy.com/api/v2/User/1到不同的服務器處理。

3、  多個版本的 Controller 共處在一個項目中,然后使用 [RoutePrefix] 或者

IHttpControllerSelector 根據報文頭、路徑等選擇不同的 Controller 執行。下面主要講這方法

 (推薦):自定義 IHttpControllerSelector

修改默認路由,如果還想用{controller}/{action}的方式,那么改就是了

其中v1,v2代表這版本號,不同版本的 Controller 放到不同的 namespace 下

 config.Routes.MapHttpRoute(
                name: "DefaultApiv1",
                routeTemplate: "api/v1/{controller}/{action}/{id}",
                defaults: new { id = RouteParameter.Optional });

            config.Routes.MapHttpRoute(
                name: "DefaultApiv2",
                routeTemplate: "api/v2/{controller}/{action}/{id}", defaults: new { id = RouteParameter.Optional }
            );

首先自定義類VersionControllerSelector繼承DefaultHttpControllerSelector

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Dispatcher;

namespace ApiDemo
{
    public class VersionControllerSelector:DefaultHttpControllerSelector
    {
        private readonly HttpConfiguration _config;

        public VersionControllerSelector(HttpConfiguration config) : base(config)
        {
            _config = config;
        }

        public override IDictionary<string, HttpControllerDescriptor> GetControllerMapping()
        {
            Dictionary<string, HttpControllerDescriptor> dict = new Dictionary<string, HttpControllerDescriptor>();
            foreach (var asm in _config.Services.GetAssembliesResolver().GetAssemblies())
            {
                //獲取所有繼承自ApiController的非抽象類 
                var controllerTypes = asm.GetTypes()
                    .Where(t => !t.IsAbstract && typeof(ApiController)
                    .IsAssignableFrom(t)).ToArray();

                foreach (var ctrlType in controllerTypes)
                {
                    //從namespace中提取出版本號                  命名空間,有可能不是當前的weiapi項目
                    var match = Regex.Match(ctrlType.Namespace, GetType().Namespace + @".Controllers.v(\d+)");
                    if (match.Success)
                    {
                        string verNum = match.Groups[1].Value;//獲取版本號  
                        //從PersonController中拿到Person
                        string ctrlName = Regex.Match(ctrlType.Name, "(.+)Controller").Groups[1].Value;
                        //Personv2為key                
                        string key = ctrlName + "v" + verNum;

                        dict[key] = new HttpControllerDescriptor(_config, ctrlName, ctrlType);
                    }
                }
            }
            return dict;
        }

        //設計就是返回HttpControllerDesriptor的過程 
        public override HttpControllerDescriptor SelectController(HttpRequestMessage request)
        {
            //獲取所有的controller鍵值集合 
            var controllers = GetControllerMapping();
            //獲取路由數據 
            var routeData = request.GetRouteData();
            //從路由中獲取當前controller的名稱 
            var controllerName = (string)routeData.Values["controller"];

            var verNum = request.Headers.TryGetValues("ApiVersion", out var versions) ?
                versions.Single() :
                Regex.Match(request.RequestUri.PathAndQuery, @"api/v(\d+)").Groups[1].Value;


            //獲取版本號 

            var key = controllerName + "v" + verNum;//獲取Personv2             
            return controllers.ContainsKey(key) ? controllers[key] : null;
        }
    }
}

然后在WebApiConfig 的 Register 中添加

 config.Services.Replace(typeof(IHttpControllerSelector), new VersionControllerSelector(config));

最后我們就可以以不同的版本號,來訪問不同的controller了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM