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 - 3], items[count - 2], items[count - 1]);

                // 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()方法与前述方法相同。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏跟着阿笨一起玩NET

如何让DevExpress TreeList的每个结点高亮显示?

原文地址:http://www.devexpresscn.com/devResources/page-18-78.html

1922
来自专栏偏前端工程师的驿站

网页优化系列三:使用压缩后置viewstate

  Asp.net中的服务器控件都启用了viewstate,虽然方便了开发人员,但页面大小及性能上确实有所影响,对于无需viewstate的控件及页面可以直接把...

2145
来自专栏Golang语言社区

用golang写的golang解析json数据的包

gojson是快速解析json数据的一个golang包,你使用它可以快速的查找json内的数据 安装 go get github.com/widuu/gojs...

35911
来自专栏技术之路

wpf listBox 多列大图片效果

修改ListBox的模版 多列大图片效果,加上删除button 看图 ? 上代码! <Window x:Class="Thunder.SetCenter.Roo...

4967
来自专栏Golang语言社区

Go语言test之类方法测试

Go语言提供了完善的单元测试支持,开发人员可以方便的编写测试代码,保证自己代码的质量。在目前的例子中,一般看到都是普通函数的例子。下面我将举类方法的测试例子来展...

37410
来自专栏DOTNET

asp.net web api 构建api帮助文档

1 概要 创建ASP.NET Web Api 时模板自带Help Pages框架。 2 问题 1)使用VS创建Web Api项目时,模板将Help Pages框...

3118
来自专栏逸鹏说道

weiapi2.2 HelpPage自动生成接口说明文档和接口测试功能

在开发Webapi项目时每写完一个方法时,是不是需要添加相应的功能说明和测试案例呢?为了更简单方便的写说明接口文档和接口测试HelpPage提供了一个方便的途径...

2977
来自专栏函数式编程语言及工具

FunDA(16)- 示范:整合并行运算 - total parallelism solution

   在对上两篇讨论中我们介绍了并行运算的两种体现方式:并行构建数据源及并行运算用户自定义函数。我们分别对这两部分进行了示范。本篇我准备示范把这两种情况集成一体...

20610
来自专栏大内老A

[ASP.NET MVC]为HtmlHelper添加一个RadioButtonList扩展方法

在前面一篇文章中,我们通过对HtmlHelper的扩展简化了对DropDownList(Single-Line-Select)和ListBox(Multiple...

22210
来自专栏.NET后端开发

ADO.NET入门教程(七) 谈谈Command对象高级应用

摘要 在上一篇文章《你必须知道的ADO.NET(六) 谈谈Command对象与数据检索》中,我详细讲解了Command对象的基础知识以及基本用法。作为ADO.N...

5269

扫码关注云+社区

领取腾讯云代金券