关web api的版本控制网上有很多,如Web API 版本控制的几种方式 Web API 版本化的介绍 但是具体的code并不多,或者说可以run的demo 不多。
版本控制如果项目一开始还好做关键是很多项目都在中后期才引入版本控制。如:
如这里的ValuesController先前是没有版本控制的,可能项目运行一段时间后才需要增加版本控制,不得影响已有的使用哦。
版本控制的原理主要就是如何确定controler。新建VersionHttpControllerSelector如下:
public class VersionHttpControllerSelector : IHttpControllerSelector { private const string VersionKey = "version"; private const string ControllerKey = "controller"; private readonly HttpConfiguration _configuration; private readonly Lazy<Dictionary<string, HttpControllerDescriptor>> _controllers; private readonly HashSet<string> _duplicates; public VersionHttpControllerSelector(HttpConfiguration config) { _configuration = config; _duplicates = new HashSet<string>(StringComparer.OrdinalIgnoreCase); _controllers = new Lazy<Dictionary<string, HttpControllerDescriptor>>(InitializeControllerDictionary); } private Dictionary<string, HttpControllerDescriptor> InitializeControllerDictionary() { var dictionary = new Dictionary<string, HttpControllerDescriptor>(StringComparer.OrdinalIgnoreCase); // Create a lookup table where key is "namespace.controller". The value of "namespace" is the last // segment of the full namespace. For example: // MyApplication.Controllers.V1.ProductsController => "V1.Products" IAssembliesResolver assembliesResolver = _configuration.Services.GetAssembliesResolver(); IHttpControllerTypeResolver controllersResolver = _configuration.Services.GetHttpControllerTypeResolver(); ICollection<Type> controllerTypes = controllersResolver.GetControllerTypes(assembliesResolver); foreach (Type t in controllerTypes) { var segments = t.Namespace.Split(Type.Delimiter); // For the dictionary key, strip "Controller" from the end of the type name. // This matches the behavior of DefaultHttpControllerSelector. var controllerName = t.Name.Remove(t.Name.Length - DefaultHttpControllerSelector.ControllerSuffix.Length); string version=segments[segments.Length - 1]; var key = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", version, controllerName); if (version == "Controllers") { key = String.Format(CultureInfo.InvariantCulture, "{0}", controllerName); } // Check for duplicate keys. if (dictionary.Keys.Contains(key)) { _duplicates.Add(key); } else { dictionary[key] = new HttpControllerDescriptor(_configuration, t.Name, t); } } // Remove any duplicates from the dictionary, because these create ambiguous matches. // For example, "Foo.V1.ProductsController" and "Bar.V1.ProductsController" both map to "v1.products". foreach (string s in _duplicates) { dictionary.Remove(s); } return dictionary; } // Get a value from the route data, if present. private static T GetRouteVariable<T>(IHttpRouteData routeData, string name) { object result = null; if (routeData.Values.TryGetValue(name, out result)) { return (T)result; } return default(T); } public HttpControllerDescriptor SelectController(HttpRequestMessage request) { IHttpRouteData routeData = request.GetRouteData(); if (routeData == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } // Get the version and controller variables from the route data. string version = GetRouteVariable<string>(routeData, VersionKey); if (string.IsNullOrEmpty(version)) { version = GetVersionFromHTTPHeaderAndAcceptHeader(request); } string controllerName = GetRouteVariable<string>(routeData, ControllerKey); if (controllerName == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } // Find a matching controller. string key = String.Format(CultureInfo.InvariantCulture, "{0}", controllerName); if (!string.IsNullOrEmpty(version)) { key = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", version, controllerName); } HttpControllerDescriptor controllerDescriptor; if (_controllers.Value.TryGetValue(key, out controllerDescriptor)) { return controllerDescriptor; } else if (_duplicates.Contains(key)) { throw new HttpResponseException( request.CreateErrorResponse(HttpStatusCode.InternalServerError, "Multiple controllers were found that match this request.")); } else { throw new HttpResponseException(HttpStatusCode.NotFound); } } public IDictionary<string, HttpControllerDescriptor> GetControllerMapping() { return _controllers.Value; } private string GetVersionFromHTTPHeaderAndAcceptHeader(HttpRequestMessage request) { if (request.Headers.Contains(VersionKey)) { var versionHeader = request.Headers.GetValues(VersionKey).FirstOrDefault(); if (versionHeader != null) { return versionHeader; } } var acceptHeader = request.Headers.Accept; foreach (var mime in acceptHeader) { if (mime.MediaType == "application/json" || mime.MediaType == "text/html") { var version = mime.Parameters .Where(v => v.Name.Equals(VersionKey, StringComparison.OrdinalIgnoreCase)) .FirstOrDefault(); if (version != null) { return version.Value; } return string.Empty; } } return string.Empty; } }
同时需要修改WebApiConfig.cs如下:
public static class WebApiConfig { public static void Register(HttpConfiguration config) { config.Routes.MapHttpRoute( name: "DefaultVersion", routeTemplate: "api/{version}/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); config.Services.Replace(typeof(IHttpControllerSelector), new VersionHttpControllerSelector((config))); } } namespace WebApi.Controllers { public class ValuesController : ApiController { public HttpResponseMessage Get() { return new HttpResponseMessage() { Content = new StringContent("This is a value response.") }; } } } namespace WebApi.Controllers.V1 { public class ValuesController : ApiController { public HttpResponseMessage Get() { return new HttpResponseMessage() { Content = new StringContent("This is a V1 response.") }; } } } namespace WebApi.Controllers.V2 { public class ValuesController : ApiController { public HttpResponseMessage Get() { return new HttpResponseMessage() { Content = new StringContent("This is a V2 response.") }; } } }
我的demo中的version可以从url获取也可以从request header中获取。至于实际中把version放在url还是request中还的根据实际情况。 如标准中推荐放到request header中,但是实际放在url中比较一目了然。并且在跨域的时候可能 减少预请求。
再强调一下 这里的版本控制主要是控制controler的版本,也就是SelectController方法。
相关推荐
ASP.NET Web API 2 Recipes provides you with the code to solve a full range of Web API problems and question marks that you might face when developing line-of-business applications. ASP.NET Web API 2 ...
《ASP.NET Web API设计》依托ASP.NET Web API阐述API设计与开发的通用技术,是一本全面介绍如何构建真实可演化API的实践指南。本书共分三部分。第一部分介绍Web/HTTP和API开发的基础知识,介绍ASP.NET Web API,为...
asp.net webapi2 的最新书籍,喜欢的同学可以拿去看看。
ASP.NET Web API File Upload and Multipart MIME
主要为大家详细介绍了ASP.NET WebAPI连接数据库的方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
第1章 S101 一个简单的ASP.NET Web API应用 第2章 S201 通过路由实现请求地址与.aspx页面的映射 S202 基本路由注册 S203 在路由注册中指定约束 S204 针对现有物理文件的路由(关闭) S205 针对现有物理文件的...
asp.net webapi2 基于token令牌的身份验证 通过浏览器模拟附加token的headers请求授权
ASP.NET WebAPI 接口 实现单文件,多文件上传下载,图片缩略图访问实现接口,实用于APP,web系统应用,上传下载文件、图片访问分离处理。
asp.net web api 教程 《ASP.NET Web API设计》依托ASP.NET Web API阐述API设计与开发的通用技术,是一本全面介绍如何构建真实可演化API的实践指南。
最新的Pro ASP.NET Web API HTTP Web Services in ASP.NET; 英文高清pdf; 2013年新书,学习ASP.NET Web API 现阶段最权威的一本专业教材; 前置技能:C#(掌握) , MVC(掌握或了解) ,数据库(掌握),HTML(掌握)...
ASP.NET Web API and Angular 2 英文pdf
《ASP.NET Web API 2框架揭秘》一本注重实证的书,功能各异、多达120个可供下载的示例,大量最佳实践与实用性扩展,可直接用于解决实际开发问题。全新的学习方法,通过完整论证来实现彻底的融会贯通。本书可以作为讲...
【ASP.NET编程知识】.net webapi接收xml格式数据的3种情况小结.docx
ASP.NET Web API 2框架揭秘是一本注重实证的书,功能各异、多达120个可供下载的示例,大量实践与实用性扩展,可直接用于解决实际开发问题。全新的学习方法,通过完整论证来实现彻底的融会贯通。本书可以作为讲设计...
Practical ASP.NET Web API
asp.net WebAPI OWIN OAuth2.0授权自定义返回结果及错误或异常问题处理核心代码,详情: https://www.cnblogs.com/wgx0428/p/12315546.html
依托ASP.NET Web API阐述API设计与开发的通用技术,是一本全面介绍如何构建真实可演化API的实践指南。
小程序开发 简单实现用户登录功能,服务端使用ASP.NET Web API 具体代码详细讲解见:http://www.51softs.com/aspnet/xiaochengxu-kaifa-shiyong-webapi-shixian-denglu.html
Asp.Net Web API 实例 包括了: 更改默认路由表 方法名字的HttpMethod以及ActionName的注解属性 基本的实体框架入门 调用API的Demo