版本控制

 

版本控制的方法有很多,这里提供一种将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编程——版本控制的更多相关文章

  1. ASP.NET Web API编程——路由

    路由过程大致分为三个阶段: 1)请求URI匹配已存在路由模板 2)选择控制器 3)选择操作 1匹配已存在的路由模板 路由模板 在WebApiConfig.Register方法中定义路由,例如模板默认生 ...

  2. ASP.NET Web API编程——序列化与内容协商

    1 多媒体格式化器 多媒体类型又叫MIME类型,指示了数据的格式.在HTTP协议中多媒体类型描述了消息体的格式.一个多媒体类型包括两个字符串:类型和子类型. 例如: text/html.image/p ...

  3. ASP.NET Web API编程——构建api帮助文档

    1 概要 创建ASP.NET Web Api 时模板自带Help Pages框架. 2 问题 1)使用VS创建Web Api项目时,模板将Help Pages框架自动集成到其中,使得Web Api项目 ...

  4. ASP.NET Web API编程——模型验证与绑定

    1.模型验证 使用特性约束模型属性 可以使用System.ComponentModel.DataAnnotations提供的特性来限制模型. 例如,Required特性表示字段值不能为空,Range特 ...

  5. ASP.NET Web API编程——文件上传

    首先分别介绍正确的做法和错误的做法,然后分析他们的不同和错误之处,以便读者在实现此功能时可避开误区 1正确的做法 public class AvaterController : BaseApiCont ...

  6. ASP.NET Web API编程——异常捕获

    1 向客户端发送错误消息 使用throw new HttpResponseException()向客户端抛出错误信息. HttpResponseException包含两个重载的构造函数,其中一个是构造 ...

  7. ASP.NET Web API编程——文件下载

    断点续传基本原理 HTTP协议中与断点续传相关的HTTP头为:Range和Content-Range标头,断点续传实现流程: 1)客户端请求下载一个文件,文件的总长度为n:已经下载了一部分文件,长度为 ...

  8. ASP.NET Web API编程——客户端调用

    可以使用HttpClient这个调用Web API,下面是HttpClient的定义,列举了一些常用的方法,其中还有一些没有列举,包括重载的方法. public class HttpClient : ...

  9. ASP.NET Web API编程——使用Odata

    路由配置 routePrefix路由前缀,必须含有Odata字符串,否则路由不到Odata控制器. V1表示版本,可以使用这种方式进行版本控制,也可以使用其他方式. config.Count().Fi ...

随机推荐

  1. Hadoop实战之四~hadoop作业调度详解(2)

    这篇文章将接着上一篇wordcount的例子,抽象出最简单的过程,一探MapReduce的运算过程中,其系统调度到底是如何运作的. 情况一:数据和运算分开的情况 wordcount这个例子的是hado ...

  2. Java - 避免使用finalizer

    Finalizers are unpredictable ,often dangerous ,and generally unnecessary. 在Java中,GC会自动回收不可达对象相关的空间,而 ...

  3. SQL语句表处理

    1.create table:创建表,语法: create table 表格名 ( 列名 数据类型, 列名 数据类型, ...... ) 2.constrain:约束,关键词 NOT NULL. UN ...

  4. Java实现在线预览–openOffice实现

    实现逻辑有两种: 一.利用jodconverter(基于OpenOffice服务)将文件(.doc..docx..xls..ppt)转化为html格式. 二.利用jodconverter(基于Open ...

  5. PHP5中Static和Const关键字

    (1) static static要害字在类中是,描述一个成员是静态的,static能够限制外部的访问,因为static后的成员是属于类的,是不属于任何对象实例,其他类是无法访问的,只对类的实例共享, ...

  6. js-js和HTML的两种结合方式

    第一种: - 使用一个标签 <script type="text/javascript"> js代码; </script> 第二种: - 使用script标 ...

  7. Luogu3403: 跳楼机

    题面 传送门 Sol 有一个显然的想法 处理出\(y, z\)能凑出的高度 然后这些高度凑一些\(x\)就可以得到其它的高度 那么可以把这些\(y, z\)凑出的高度对\(x\)取模,其它的用\(x\ ...

  8. javascript运算符之==和===

    1.== :判断两个数是否相等,在比较之前会自动转化类型再做比较.为确定两个运算数是否相等,这两个运算符都会进行类型转换. 执行类型转换的规则如下: 1.如果一个运算数是 Boolean 值,在检查相 ...

  9. 用CSS隐藏页面元素的5种方法

    1.opacity设置一个元素的透明度只是从视觉上隐藏元素,对页面布局还是有影响,读屏软件会原样读出 2.visibility设置为hidden将隐藏我们的元素,对网页布局还是起作用,子元素也会被隐藏 ...

  10. 关于 class 的命名

    class名称中只出现小写字符和破折号 使用有组织或目的明确的名称,不使用表现形式 基于最近的的父class 作为新class的前缀 使用 .js-* 来标识行为,并且不要将这些class包含到css ...