ASP.NET Web API编程——版本控制
版本控制
版本控制的方法有很多,这里提供一种将Odata与普通web api版本控制机制统一的方法,但也可以单独控制,整合控制与单独控制主要的不同是:整合控制通过VersionControllerSelector来选择控制器过滤器,而不是直接选择控制器。
采用此机制来控制版本,应按照如下规则命名控制器:
自定义标识符+版本+Controller
自定义标识符:能体现控制器含义的字符串
版本:表示版本的字符串,例如:V1,V1.0;不建议使用V1.0这样的写法,因为这样控制器名称会相当怪异,如果表示小版本号,那么可以使用V1D0,这种写法,即用一个字母代替句号。
命名空间对应了项目文件的组织形式,控制器的命名空间为:

1 Odata版本控制
扩展DefaultHttpControllerSelector
public class ODataVersionControllerSelector : DefaultHttpControllerSelector
{
public Dictionary<string, string> RouteVersionSuffixMapping { get; set; } public ODataVersionControllerSelector(HttpConfiguration configuration)
: base(configuration)
{
if (RouteVersionSuffixMapping == null)
{
RouteVersionSuffixMapping = new Dictionary<string, string>();
}
} public override string GetControllerName(HttpRequestMessage request)
{
var controllerName = base.GetControllerName(request);
if (string.IsNullOrEmpty(controllerName))
{
return controllerName;
} var routeName = request.ODataProperties().RouteName;
if (string.IsNullOrEmpty(routeName))
{
return controllerName;
} var mapping = GetControllerMapping(); if (!RouteVersionSuffixMapping.ContainsKey(routeName))
{
return controllerName;
} var versionControllerName = controllerName + RouteVersionSuffixMapping[routeName];
return mapping.ContainsKey(versionControllerName)
? versionControllerName
: controllerName;
}
}
修改WebApiConfig.Register方法
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
...... //odata路由
config.MapODataServiceRoute(
routeName: "V1OdataRouteVersioning",
routePrefix: "Odata/V1",
model: GetEdmModel());
config.Count().Filter().OrderBy().Expand().Select().MaxTop(null);
config.AddODataQueryFilter(); config.Services.Replace(typeof(IHttpControllerSelector), new ODataVersionControllerSelector (config));
var controllerSelector = config.Services.GetService(typeof(IHttpControllerSelector)) as ODataVersionControllerSelector ;
controllerSelector.RouteVersionSuffixMapping.Add("V1OdataRouteVersioning", "V1"); ......
}
} private static IEdmModel GetEdmModel()
{
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
#region Publication
var publicationsSet = builder.EntitySet<Publication>("Publications").EntityType.Collection;
var getPublicationsFunction = publicationsSet.Function("GetPublications").Returns<PublicationDTO>();
getPublicationsFunction.Parameter<int>("userId"); publicationsSet.Action("AddPublication").Returns<int>().Parameter<PublicationAddBindingModel>("publicationAddBM");
publicationsSet.Action("DeletePublication").Returns<IHttpActionResult>().Parameter<PublicationDelBindingModel>("publicationDelBM");
#endregion builder.Namespace = "Service";
return builder.GetEdmModel();
}
2 普通Api版本控制
扩展IHttpControllerSelector
public class NormalVersionControllerSelector : IHttpControllerSelector
{
private const string Version = "version";
private const string ControllerKey = "controller"; private readonly HttpConfiguration _configuration;
private readonly Lazy<Dictionary<string, HttpControllerDescriptor>> _controllers;
private readonly HashSet<string> _duplicates; public NormalVersionControllerSelector(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); 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); //去掉HY_WebApi.V1.Controllers.KeyController中的HY_WebApi.
//去掉HY_WebApi.HYDB.V1.Controllers.HYSearchController中的HY_WebApi.HYDB.
//因此,保留V1.Controllers.KeyController这三部分
//键值格式如:V1.Controllers.KeyController
string[] items = t.FullName.Split(new char[]{'.'},StringSplitOptions.RemoveEmptyEntries);
int count = items.Count();
var key = string.Format("{0}.{1}.{2}", items[count - ], items[count - ], items[count - ]); // Check for duplicate keys.
if (dictionary.ContainsKey(key))
{
_duplicates.Add(key);
}
else
{
dictionary[key] = new HttpControllerDescriptor(_configuration, t.Name, t);
}
} 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 namespace and controller variables from the route data.
string version = GetRouteVariable<string>(routeData, Version);
if (version == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
} string controllerName = GetRouteVariable<string>(routeData, ControllerKey);
if (controllerName == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
} // 匹配控制器
string key = String.Format("{0}.Controllers.{1}{2}Controller", version, controllerName,version); 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;
}
}
}
修改WebApiConfig.Register方法
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{ ...... // Web API 路由
config.Routes.MapHttpRoute(
name: "defaultRoute",
routeTemplate: "api/{version}/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Services.Replace(typeof(IHttpControllerSelector), new NormalVersionControllerSelector(config)); ...... }
}
3 同时支持Odata,与普通Web Api版本控制
扩展DefaultHttpControllerSelector
public class VersionControllerSelector : DefaultHttpControllerSelector
{
public Dictionary<string, string> RouteVersionSuffixMapping { get; set; }
private HttpConfiguration configuration;
public VersionControllerSelector(HttpConfiguration configuration)
: base(configuration)
{
this.configuration = configuration;
if (RouteVersionSuffixMapping == null)
{
RouteVersionSuffixMapping = new Dictionary<string, string>();
}
}
public override string GetControllerName(HttpRequestMessage request)
{
return SelectController(request).ControllerName;
} public override HttpControllerDescriptor SelectController(HttpRequestMessage request)
{
var routeName = request.ODataProperties().RouteName; if (!string.IsNullOrWhiteSpace(routeName))
{//odata路由
var selector = new ODataVersionControllerSelector(configuration);
selector.RouteVersionSuffixMapping = RouteVersionSuffixMapping;
return selector.SelectController(request);
}
else
{//普通路由
var selector = new NormalVersionControllerSelector(configuration);
return selector.SelectController(request);
}
}
} 修改WebApiConfig.Register方法
public static class WebApiConfig public static void Register(HttpConfiguration config)
{
// Web API 路由
config.Routes.MapHttpRoute(
name: "defaultRoute",
routeTemplate: "api/{version}/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
); //odata路由
config.MapODataServiceRoute(
routeName: "V1OdataRouteVersioning",
routePrefix: "Odata/V1",
model: GetEdmModel());
config.Count().Filter().OrderBy().Expand().Select().MaxTop(null);
config.AddODataQueryFilter(); config.Services.Replace(typeof(IHttpControllerSelector), new VersionControllerSelector(config));
var controllerSelector = config.Services.GetService(typeof(IHttpControllerSelector)) as VersionControllerSelector;
controllerSelector.RouteVersionSuffixMapping.Add("V1OdataRouteVersioning", "V1");
}
}
其中GetEdmModel()方法与前述方法相同。
ASP.NET Web API编程——版本控制的更多相关文章
- ASP.NET Web API编程——路由
路由过程大致分为三个阶段: 1)请求URI匹配已存在路由模板 2)选择控制器 3)选择操作 1匹配已存在的路由模板 路由模板 在WebApiConfig.Register方法中定义路由,例如模板默认生 ...
- ASP.NET Web API编程——序列化与内容协商
1 多媒体格式化器 多媒体类型又叫MIME类型,指示了数据的格式.在HTTP协议中多媒体类型描述了消息体的格式.一个多媒体类型包括两个字符串:类型和子类型. 例如: text/html.image/p ...
- ASP.NET Web API编程——构建api帮助文档
1 概要 创建ASP.NET Web Api 时模板自带Help Pages框架. 2 问题 1)使用VS创建Web Api项目时,模板将Help Pages框架自动集成到其中,使得Web Api项目 ...
- ASP.NET Web API编程——模型验证与绑定
1.模型验证 使用特性约束模型属性 可以使用System.ComponentModel.DataAnnotations提供的特性来限制模型. 例如,Required特性表示字段值不能为空,Range特 ...
- ASP.NET Web API编程——文件上传
首先分别介绍正确的做法和错误的做法,然后分析他们的不同和错误之处,以便读者在实现此功能时可避开误区 1正确的做法 public class AvaterController : BaseApiCont ...
- ASP.NET Web API编程——异常捕获
1 向客户端发送错误消息 使用throw new HttpResponseException()向客户端抛出错误信息. HttpResponseException包含两个重载的构造函数,其中一个是构造 ...
- ASP.NET Web API编程——文件下载
断点续传基本原理 HTTP协议中与断点续传相关的HTTP头为:Range和Content-Range标头,断点续传实现流程: 1)客户端请求下载一个文件,文件的总长度为n:已经下载了一部分文件,长度为 ...
- ASP.NET Web API编程——客户端调用
可以使用HttpClient这个调用Web API,下面是HttpClient的定义,列举了一些常用的方法,其中还有一些没有列举,包括重载的方法. public class HttpClient : ...
- ASP.NET Web API编程——使用Odata
路由配置 routePrefix路由前缀,必须含有Odata字符串,否则路由不到Odata控制器. V1表示版本,可以使用这种方式进行版本控制,也可以使用其他方式. config.Count().Fi ...
随机推荐
- java 并发(三)---Thread 线程
Thread 的状态 线程共有五种状态.分别是: (1)新建 (2)就绪 (3)运行 (4)阻塞 (5)死亡 ,下面列列举的状态需要结合状态示意图更好理解. 新建状态(New): 新创建了一个线程对 ...
- docker安装Ghost博客
1.安装docker-compose curl -L https://github.com/docker/compose/releases/download/1.22.0/docker-compose ...
- 2017年12月16日 ASP.NET基本用法
ASP.NET初级添加 利用css代码跟ASP.NET还有Javascript原生,LinQ来写增跟展示数据 首先介绍一个非常好用的控件,灵活并且循环展示数据库里面的数据 <asp:Repeat ...
- 2017年11月28日 C#进程和线程
进程 需要放using System.Diagnostics;才可以用进程 用时的方法名为Process 用两个按钮一个为选择文件夹一个为打开可以打开系统内的进程. 注意:打开时一定要用进程名 Pro ...
- 互联网轻量级框架SSM-查缺补漏第六天【级联+延迟加载特辑】
简言:本来这是昨天看的,但是因为想好好写一下[级联]这个东西,所以就看完之后今天来整理一下. 级联 1. 什么是级联 级联是一个数据库实体的概念.比如教师就需要存在学生与之对应,这样就有教师学生表,一 ...
- Hadoop 完全分布式部署(三节点)
用来测试,我在VMware下用Centos7搭起一个三节点的Hadoop完全分布式集群.其中NameNode和DataNode在同一台机器上,如果有条件建议大家把NameNode单独放在一台机器上,因 ...
- js原生带缩略图的图片切换效果
js原生带缩略图的图片切换效果 本例中用到的 moveElement(elementID,final_x,final_y,interval)是来自<JavaScript DOM编程艺术(中文第二 ...
- 理解mouseover,mouseout,mouseenter,mouseleave
mouseover定义和用法 当鼠标指针位于元素上方时,会发生 mouseover 事件. 该事件大多数时候会与 mouseout 事件一起使用. mouseover() 方法触发 mouseover ...
- RoadFlowCore工作流2.8.1 更新日志
1.2.8.1更新了2.8刚发布的一些小BUG. 2.2.8.1增加了移动端,基于微信企业号或企业微信. 详细请参阅官方网站:roadflow.net
- Android MediaPlayer播放音乐并实现进度条
提前工作,往sd卡里放音乐文件 1.布局文件main.xml <?xml version="1.0" encoding="utf-8"?> < ...