天天看點

使用ASP.NET Web Api建構基于REST風格的服務實戰系列教程【九】——API變了,用戶端怎麼辦?

前言

一旦我們将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中找,如果有資料,就獲得。

用戶端發送的請求如下:

使用ASP.NET Web Api建構基于REST風格的服務實戰系列教程【九】——API變了,用戶端怎麼辦?

這麼做也有缺點,就是添加了一個請求頭(注:這個缺點不是很了解),下面介紹第四種方式

使用Accept Header設定版本

這種方法是直接使用Accept Header, 請求的時候将它設定為“Accept:application/json; version=2”,我們依舊這麼認為:如果用戶端不提供版本号,我們就給他V1的資料。

在“LearningControllerSelector”類中添加“GetVersionFromAcceptHeaderVersion”方法,具體實作如下:

這個實作看上去比上面更标準,更專業了。