亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

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

2019-11-15 02:31:09
字體:
來源:轉載
供稿:網友

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

系列導航地址http://www.49028c.com/fzrain/p/3490137.html

前言

一旦我們將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方法是,會返回如下數據:

[{    "id": 2,    "url": "http://localhost:8323/api/students/HasanAhmad",    "firstName": "Hasan",    "lastName": "Ahmad",    "gender": 0,    "enrollmentsCount": 4},{    "id": 3,    "url": "http://localhost:8323/api/students/MoatasemAhmad",    "firstName": "Moatasem",    "lastName": "Ahmad",    "gender": 0,    "enrollmentsCount": 4}]

我們期待訪問“StudentsV2Controller”的Get方法后應該的到:

[{    "id": 2,    "url": "http://localhost:8323/api/students/HasanAhmad",    "fullName": "Hasan Ahmad",    "gender": 0,    "enrollmentsCount": 4,    "coursesDuration": 13},{    "id": 3,    "url": "http://localhost:8323/api/students/MoatasemAhmad",    "fullName": "Moatasem Ahmad",    "gender": 0,    "enrollmentsCount": 4,    "coursesDuration": 16}]

ok,下面來實現,復制粘貼”StudnetsController”并重命名為“StudnetsV2Controller”,更改Get方法的實現:

public IEnumerable<StudentV2BaseModel> Get(int page = 0, int pageSize = 10)    {        IQueryable<Student> query;         query = TheRepository.GetAllStudentsWithEnrollments().OrderBy(c => c.LastName);         var totalCount = query.Count();        var totalPages = Math.Ceiling((double)totalCount / pageSize);         var urlHelper = new UrlHelper(Request);        var PRevLink = page > 0 ? urlHelper.Link("Students", new { page = page - 1, pageSize = pageSize }) : "";        var nextLink = page < totalPages - 1 ? urlHelper.Link("Students", new { page = page + 1, pageSize = pageSize }) : "";         var paginationHeader = new        {            TotalCount = totalCount,            TotalPages = totalPages,            PrevPageLink = prevLink,            NextPageLink = nextLink        };         System.Web.HttpContext.Current.Response.Headers.Add("X-Pagination",        Newtonsoft.Json.JsonConvert.SerializeObject(paginationHeader));         var results = query        .Skip(pageSize * page)        .Take(pageSize)        .ToList()        .Select(s => TheModelFactory.CreateV2Summary(s));         return results;    }

可以看到,這里我們改的很少,返回的類型變成了“StudentV2BaseModel”,而這個類型是由ModelFactory的CreateV2Summary方法創建的。因此我們需要添加StudentV2BaseModel類和CreateV2Summary方法:

public class StudentV2BaseModel    {        public int Id { get; set; }        public string Url { get; set; }        public string FullName { get; set; }        public Data.Enums.Gender Gender { get; set; }        public int EnrollmentsCount { get; set; }        public double CoursesDuration { get; set; }    }     public class ModelFactory    {        public StudentV2BaseModel CreateV2Summary(Student student)        {            return new StudentV2BaseModel()            {                Url = _UrlHelper.Link("Students", new { userName = student.UserName }),                Id = student.Id,                FullName = string.Format("{0} {1}", student.FirstName, student.LastName),                Gender = student.Gender,                EnrollmentsCount = student.Enrollments.Count(),                CoursesDuration = Math.Round(student.Enrollments.Sum(c => c.Course.Duration))            };        }    }

到目前為止,我們的準備工作就算做完了,下面介紹四種方式實現版本變化

使用URI控制Web Api的版本

在URI中包含版本號是最常見的做法,如果想用V1版本的api(使用http://localhost:{your_port}/api/v1/students/),同理,如果想用V2版本的api(使用http://localhost:{your_port}/api/v2/students/)

這種做法的好處就是客戶端知道自己用的是哪一版本的api,實現方法就是在“WebApiConfig”中添加2條路由:

config.Routes.MapHttpRoute(                name: "Students",                routeTemplate: "api/v1/students/{userName}",                defaults: new { controller = "students", userName = RouteParameter.Optional }                ); config.Routes.MapHttpRoute(                name: "Students2",                routeTemplate: "api/v2/students/{userName}",                defaults: new { controller = "studentsV2", userName = RouteParameter.Optional }                );

在上面代碼中,我們添加了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()”方法,具體代碼如下:

public class LearningControllerSelector : DefaultHttpControllerSelector    {        private HttpConfiguration _config;        public LearningControllerSelector(HttpConfiguration config)            : base(config)        {            _config = config;        }         public override HttpControllerDescriptor SelectController(HttpRequestMessage request)        {            var controllers = GetControllerMapping(); //Will ignore any controls in same name even if they are in different namepsace             var routeData = request.GetRouteData();             var controllerName = routeData.Values["controller"].ToString();             HttpControllerDescriptor controllerDescriptor;             if (controllers.TryGetValue(controllerName, out controllerDescriptor))            {                 var version = "2";                 var versionedControllerName = string.Concat(controllerName, "V", version);                 HttpControllerDescriptor versionedControllerDescriptor;                if (controllers.TryGetValue(versionedControllerName, out versionedControllerDescriptor))                {                    return versionedControllerDescriptor;                }                 return controllerDescriptor;            }             return null;         }    }

上述代碼主要意思如下:

1.調用父類方法GetControllerMapping()獲取所有實現了ApiController的類。

2.通過request對象獲取routeData ,然后進一步獲得Controller的name

3.根據我們剛剛得到的Controller,名字創建“HttpControllerDescriptor”對象,這個對象包含了描述Controller的信息

.4.接著,在我們找到的Controller的名字后面加上“V”和版本號,重復上面步驟即可。關于如何獲得版本號,我們一會兒討論,這里暫時寫死成“2”。

為了使我們自定義的“Controller Selector”生效,因此需要在“WebApiConfig”中做如下配置:

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

接下來我們就來實現請求如何發送版本號

使用Query String設置版本

使用query string設置版本顧名思義,就是在請求URI后面加上”?v=2“,例如這個URI:http://localhost:{your_port}/api/students/?v=2

我們可以認為客戶端沒有提供query string的版本號,那么版本號默認為“1”。

實現起來也不復雜,在我們的“LearningControllerSelector”類中添加一個“GetVersionFromQueryString()”方法,該方法接收一個HttpRequestMessage參數,并從這個請求對象中獲取客戶端所需要的版本:

private string GetVersionFromQueryString(HttpRequestMessage request)    {        var query = HttpUtility.ParseQueryString(request.RequestUri.Query);         var version = query["v"];         if (version != null)        {            return version;        }         return "1";     }

我們只需要在SelectController方法中調用這個方法即可,唯一的缺點依然是URI會變,不符合REST規范。

通過自定義請求頭設置版本

現在我們使用另一種方式來發生版本號——自定義請求頭,它不是URI的一部分,添加一個頭“X-Learning-Version”并把版本

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
久久人人爽亚洲精品天堂| 亚洲欧洲视频在线| 亚洲自拍偷拍在线| 国产精品极品美女在线观看免费| 久久久999国产精品| 热久久这里只有精品| 欧美性视频在线| 日韩欧中文字幕| 国产日韩欧美日韩大片| 最新亚洲国产精品| 日本一本a高清免费不卡| 亚洲性日韩精品一区二区| 国产精品美女主播| 亚洲一区二区三区在线视频| 国产人妖伪娘一区91| 一区二区三区视频免费| 97精品免费视频| 欧美精品999| 日韩av电影免费观看高清| 国产精品jvid在线观看蜜臀| 欧洲亚洲在线视频| 欧美黄色片在线观看| 精品亚洲aⅴ在线观看| 欧美黑人一级爽快片淫片高清| 日韩欧美国产成人| 黄色精品在线看| 亚洲精品在线观看www| 91免费的视频在线播放| 日韩中文字幕免费| 91久久中文字幕| 亚洲一区二区三区视频播放| 国产香蕉一区二区三区在线视频| 国产精品视频99| 在线免费观看羞羞视频一区二区| 亚洲国产精品免费| 欧洲美女7788成人免费视频| 欧美激情精品久久久久久黑人| 久久久久这里只有精品| 欧美激情xxxxx| 日韩国产在线看| 亚洲欧洲在线看| 日韩美女视频中文字幕| 懂色av影视一区二区三区| 日韩美女主播视频| 日韩福利视频在线观看| 91夜夜揉人人捏人人添红杏| 亚洲日本欧美日韩高观看| 北条麻妃久久精品| 亚洲女同精品视频| 欧美性猛xxx| 日本精品久久久久影院| 992tv成人免费影院| xxx一区二区| 国产69精品99久久久久久宅男| 欧美大荫蒂xxx| 亚洲女同性videos| 成人淫片在线看| 国产91成人video| 日韩最新av在线| 性色av一区二区三区在线观看| 久久久国产精品免费| 国产一区二区三区18| 91人人爽人人爽人人精88v| 狠狠躁夜夜躁人人躁婷婷91| xx视频.9999.com| 国产精品老女人视频| 亚洲自拍小视频| 亚洲一区二区三区香蕉| 久久综合久久美利坚合众国| 亚洲石原莉奈一区二区在线观看| 亚洲欧美日韩成人| 久久中文久久字幕| 国产一区二区美女视频| 亚洲男人的天堂网站| 亚洲激情视频网站| 日本伊人精品一区二区三区介绍| 色偷偷噜噜噜亚洲男人| 国产一区欧美二区三区| 日韩有码在线视频| 亚洲欧洲日本专区| 久久亚洲电影天堂| 海角国产乱辈乱精品视频| 免费91麻豆精品国产自产在线观看| 裸体女人亚洲精品一区| 91精品久久久久久| 国内精品久久久久| 亚洲电影av在线| 色综合久综合久久综合久鬼88| 欧美精品电影在线| 91国偷自产一区二区三区的观看方式| 国产精品美女www爽爽爽视频| 91视频国产高清| 日韩一区二区福利| 亚洲国产女人aaa毛片在线| 国产精品欧美激情| 中文字幕欧美精品日韩中文字幕| 亚洲激情视频在线观看| 中文字幕视频一区二区在线有码| 国产美女91呻吟求| 日韩国产激情在线| 美日韩在线视频| 一本色道久久综合狠狠躁篇怎么玩| 久久久人成影片一区二区三区观看| 国产精品999| 亚洲国产成人精品久久久国产成人一区| 538国产精品一区二区免费视频| 色七七影院综合| 成人写真视频福利网| 91久久久久久国产精品| 欧美精品videossex性护士| 欧美乱人伦中文字幕在线| 成人h猎奇视频网站| 欧美激情在线观看视频| 欧美精品18videos性欧| 亚洲图片在区色| 日韩美女免费观看| 欧美日韩成人在线视频| 91精品中文在线| 成人免费观看49www在线观看| 国产欧洲精品视频| 精品国偷自产在线| 91成人免费观看网站| 日韩在线观看免费全集电视剧网站| 神马久久桃色视频| 亚洲人成欧美中文字幕| 亚洲jizzjizz日本少妇| 欧美人与性动交a欧美精品| www.美女亚洲精品| 国产精品av在线播放| 国产视频久久久久| 国产精品久久久av| 性欧美视频videos6一9| 国产精品视频免费观看www| 国产精品久久久久久久午夜| 久久国产精品影视| 亚洲免费中文字幕| 深夜精品寂寞黄网站在线观看| 成人午夜一级二级三级| 欧美精品久久一区二区| 国产精品久久久久久久久久东京| 日韩经典中文字幕| 亚洲精品免费av| 日韩精品在线观看一区二区| 亚洲精品一区av在线播放| 国产成人欧美在线观看| 岛国精品视频在线播放| 综合av色偷偷网| 亚洲一区亚洲二区亚洲三区| 日韩欧美一区二区三区久久| 色先锋资源久久综合5566| 一区二区欧美亚洲| 日本一区二区三区四区视频| 国产69精品久久久久久| 91成人在线视频| 亚洲国产精品资源| 久久国产一区二区三区| 国产精品美腿一区在线看| 欧美激情在线视频二区| 国产精品最新在线观看| 久久精品视频网站| 92国产精品视频| 亚洲综合成人婷婷小说| 日韩一区二区三区xxxx| 国产精品午夜国产小视频|