在ASP.NET Web API中,當我們的API發生改變,就涉及到版本問題了。如何實現API的版本呢?
1、通過路由設置版本
最簡單的一種方式是通過路由設置,不同的路由,不同的版本,不同的controller。
config.Routes.MapHttpRoute( name: "Food", routeTemplate: "api/v1/nutrition/foods/{foodid}", defaults:... ) config.Routes.MapHttpRoute( name: "Foodv2", routeTemplate: "api/v2/nutrition/foods/{foodid}", defaults:... )
2、通過HttpControllerSelector
通過更改HttpControllerSelector也可以實現。
首先寫一個繼承 DefaultHttpControllerSelector的類。
using System.Web.http.Dispatcher public class CountingKsControllerSelector : DefaultHttpControllerSelector { private HttpConfiguraion _config; public CountgKsControllerSelector(HttpConfiguraiton cofig) : base(config) { _config = config; } //設計就是返回HttpControllerDesriptor的過程 public override System.Web.Http.Controllers.HttpControllerDescriptor SelectController(HttpRequestMessage request) { //獲取所有的controller鍵值集合 var controllers = GetControllerMapping(); //獲取路由數據 var routeData = request.GetRouteData(); //從路由中獲取當前controller的名稱 var controllerName = (string)routeData.Values["controller"]; HttpControllerDescriptor descriptor; if(controllers.TryGetValue(controllerName, out descriptor)) { var version = "2"; //從QueryString中獲取版本 var newName = string.Concat(controllerName, "V", version); HttpControllerDescriptor versionedDescriptor; if(controllers.TryGetValue(newName, out versionedDescriptor)) { return versionedDescriptor; } return descriptor; } return null; } }
在WebApiConfig.cs注冊自定義的ControllerSelector
config.Services.Replace(typeof(IHttpControllerSelector), new CountingKsControllerSelector(config) );
以上是大致的實現思路。具體來說可以通過如下幾種方式實現。
■ 通過Query String實現版本
客戶端大致這樣請求:
http://localhost:8901/api/nutrition/foods/4492/measures/7269?v=2
using System.Web.http.Dispatcher public class CountingKsControllerSelector : DefaultHttpControllerSelector { private HttpConfiguraion _config; public CountgKsControllerSelector(HttpConfiguraiton cofig) : base(config) { _config = config; } //設計就是返回HttpControllerDesriptor的過程 public override System.Web.Http.Controllers.HttpControllerDescriptor SelectController(HttpRequestMessage request) { //獲取所有的controller鍵值集合 var controllers = GetControllerMapping(); //獲取路由數據 var routeData = request.GetRouteData(); //從路由中獲取當前controller的名稱 var controllerName = (string)routeData.Values["controller"]; HttpControllerDescriptor descriptor; if(controllers.TryGetValue(controllerName, out descriptor)) { //var version = "2"; //從QueryString中獲取版本 var version = GetVersionFromQueryString(request); var newName = string.Concat(controllerName, "V", version); HttpControllerDescriptor versionedDescriptor; if(controllers.TryGetValue(newName, out versionedDescriptor)) { return versionedDescriptor; } return descriptor; } return null; } //從QueryString中獲取版本 private string GetVersionFromQueryString(HttpRequestMessage request) { var query = HttpUtility.ParseQueryString(request.RequestUri.Query); var version = query["v"]; if(version != null) { return version; } return "1"; } }
■ 通過Header實現版本
客戶端大致這樣請求:
User-Agent:Fiddler
Host:locahohost:8901
X-CountingKs-Version:2
private string GetVersionFromHeader(HttpRequestMessage request) { const string HEADER_NAME = "X-CountingKs-Version"; if(request.Headers.Contains(HEADER_NAME)) { var header = request.Headers.GetValues(HEADER_NAME).FirstOrDefault(); if(header != null) { return header; } } return "1"; }
■ 通過Accept-Header實現版本
客戶端大致這樣請求:
User-Agent:Fiddler
Host:locahohost:8901
Accept: application/json;version=2
private string GetVersionFromAcceptHeaderVersion(HttpRequestMessage request) { var accept = request.Headers.Accept; foreach(var mime in accept) { if(mime.MediaType == "applicaiton/json") { var value = mime.Parameters .Where(v => v.Name.Equals("version",StringComparison.OrdinalIngoreCase)) .FirstOrDefault(); return value.Value; } } return "1"; }
■ 通過MediaType實現版本
在WebApiConfig.cs中
var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().FirstOrDefault(); jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); CreateMediaTypes(jsonFormatter); private static void CreateMediaTypes(JsonMediaTypeFormatter jsonFormatter) { var mediaTypes = new string[] { "application/vnd.counting,s.food.v1+json", "application/vnd.countingks.measure.v1+json", "application/vnd.countgks.measure.v2+json", "applicatikon/vnd.countingks.diary.v1+json", "application/vnd.countingks.diaryEntry.v1+json" }; foreach(var mediaType in mediaTypes) { jsonFormatter.SupportedMeidaTypes.Add(new MediaTypeHeaderValue(mediaType)); } }
在客戶端大致這樣請求:
User-Agent:Fiddler
Host:localhost:8901
Accept:application/vnd.countingks.food.v1+json
private string GetVersonFromMediaType(HttpRequestMessage request) { var accept = request.Headers.Accept; var ex = new Regex(@"application\/vnd\.countingks\.([a-z]+)\.v([0-9]+)\+json". RegexOptions.IgnoreCase); foreach(var mime in accept) { var match = ex.Match(mime.MediaType); if(match != null) { return match.Groups[2].Value; } } return "1"; }
■ 使用SDammann.WebApi.Versioning
https://github.com/Sebazzz/SDammann.WebApi.Versioning