前言
一旦我們将API釋出之後,消費者就會開始使用并和其他的一些資料混在一起。然而,當新的需求出現時變化是不可避免的,你也許會慶幸API變了對現有用戶端沒受到影響,但是這種情況不會一直發生。
是以,在具體實作之前仔細考慮一下ASP.NET Web Api的版本政策就變得很有必要了。在我們的案例中,需求發生了變化而且我們通過建立不同版本的API來解決變化,同時不影響已經在使用API的用戶端。我們把新的API版本和舊的API版本一起傳回給用戶端,讓它有足夠的時間遷移到最新版本的API,有時候多版本共存也是有可能的。
實作版本控制的方式有好多,本文主要介紹URI,query string,自定義Header和接收Header
API變了
簡單起見,我們讓“StudentsController”中的Get方法發生變化——在響應封包的body中,我們用“CoursesDuration”和“FullName”屬性替換原來的“FirstName”和“LastName”屬性。
最簡單的做法就是建立一個與“StudentsController”一樣的Controller并命名為“StudentsV2Controller”,我們将根據不同的API版本選擇合适的Controller。在新的Controller中我們實作上述變化并使用相同的Http方法,同時不做任何介紹
現在我們請求“StudentsController”的Get方法是,會傳回如下資料:
我們期待通路“StudentsV2Controller”的Get方法後應該的到:
ok,下面來實作,複制粘貼”StudnetsController”并重命名為“StudnetsV2Controller”,更改Get方法的實作:
可以看到,這裡我們改的很少,傳回的類型變成了“StudentV2BaseModel”,而這個類型是由ModelFactory的CreateV2Summary方法建立的。是以我們需要添加StudentV2BaseModel類和CreateV2Summary方法:
到目前為止,我們的準備工作就算做完了,下面介紹四種方式實作版本變化
使用URI控制Web Api的版本
這種做法的好處就是用戶端知道自己用的是哪一版本的api,實作方法就是在“WebApiConfig”中添加2條路由:
在上面代碼中,我們添加了2條路由規則,它們彼此對應了相應的Controller。如果以後我們打算添加V3,那麼就得再加一條。這裡就會變得越來越混亂。
這種技術的主要缺點就是不符合REST規範因為URI一直會變,換句話說一旦我們釋出一個新版本,就得添加一條新路由。
在我們講解另外3種實作模式之前,我們先來看一下在web api架構是怎麼根據我們的請求來選擇相應的Controller的:在web api中有一個“DefaultHttpControllerSelector”類,其中有一個方法“SelectController()”,這個方法接收一個“HttpRequestMessage”類型的參數。這個對象包含一個含key/value鍵值對的route data,其中就包括在“WebApiConfig”中配置的controller的名字。根據這一條資訊,通過反射擷取所有實作“ApiController”的類,web api就會比對到這個Controller,如果比對結果不等于1(等于0或大于等于2),那麼就會抛出一個異常。
我們自定義一個類“LearningControllerSelector”繼承自“Http.Dispatcher.DefaultHttpControllerSelector”,重寫“SelectController()”方法,具體代碼如下:
上述代碼主要意思如下:
1.調用父類方法GetControllerMapping()擷取所有實作了ApiController的類。
2.通過request對象擷取routeData ,然後進一步獲得Controller的name
3.根據我們剛剛得到的Controller,名字建立“HttpControllerDescriptor”對象,這個對象包含了描述Controller的資訊
.4.接着,在我們找到的Controller的名字後面加上“V”和版本号,重複上面步驟即可。關于如何獲得版本号,我們一會兒讨論,這裡暫時寫死成“2”。
為了使我們自定義的“Controller Selector”生效,是以需要在“WebApiConfig”中做如下配置:
接下來我們就來實作請求如何發送版本号
使用Query String設定版本
我們可以認為用戶端沒有提供query string的版本号,那麼版本号預設為“1”。
實作起來也不複雜,在我們的“LearningControllerSelector”類中添加一個“GetVersionFromQueryString()”方法,該方法接收一個HttpRequestMessage參數,并從這個請求對象中擷取用戶端所需要的版本:
我們隻需要在SelectController方法中調用這個方法即可,唯一的缺點依然是URI會變,不符合REST規範。
通過自定義請求頭設定版本
現在我們使用另一種方式來發生版本号——自定義請求頭,它不是URI的一部分,添加一個頭“X-Learning-Version”并把版本号設定在裡面,當用戶端沒有這條頭資訊是我們可以認為它需要V1版本。
實作這個技術,我們在“LearningControllerSelector”中添加一個“GetVersionFromHeader”方法,代碼如下:
這裡做法很簡單,我們先确定好請求頭的名字,然後去request的Header中找,如果有資料,就獲得。
用戶端發送的請求如下:

這麼做也有缺點,就是添加了一個請求頭(注:這個缺點不是很了解),下面介紹第四種方式
使用Accept Header設定版本
這種方法是直接使用Accept Header, 請求的時候将它設定為“Accept:application/json; version=2”,我們依舊這麼認為:如果用戶端不提供版本号,我們就給他V1的資料。
在“LearningControllerSelector”類中添加“GetVersionFromAcceptHeaderVersion”方法,具體實作如下:
這個實作看上去比上面更标準,更專業了。