WebAPI多版本控制实现的几种方式——自定义IHttpControllerSelector

  这种方法的原理是通过分析url来区分版本。

1) WebApiConfig 中的路由改成如下:

    使用这种方式的好处:以后每次新添加api版本只需要在webconfig配置中新增加一条路由配置,比如项目中新增了名为v3版本,只需要把

routeTemplate: "api/v2/{controller}/{id}"中的v2改成v3即可。当然如果需要改变路由的格式也可以,后面的配置格式也要按照改变的格式来。

         // Web API 配置和服务

            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/v1/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
            config.Routes.MapHttpRoute(
                name: "DefaultApi1",
                routeTemplate: "api/v2/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
                );   

2)不同版本的 Controller 放到不同的 namespace 下:

    由于该方式是通过判断namespace名来选择版本,所以在Controllers文件下为每个版本创建新的文件夹,文件夹名就为路由配置中版本号格式,

    API文件在放入对应的文件夹下。例如我的项目例子中:

    

 

    APIDemoController (v1)代码:

    

namespace webapi.Controllers.v1
{
    public class APIDemoController : ApiController
    {
        public string Get(int id)
        {
            return "我是APIDemoV1" + id;
        }  
    }
}

    APIDemoController(v2)代码

 

namespace webapi.Controllers.v2
{
    public class APIDemoController : ApiController
    {
     public string Get(int id)
        {
            return "我是APIDemoV2" + id;
        }
    {
{    

3)编写一个名为VersionControllerSelectord类(当然这个名字不是必须叫这个的):所有的实现都是在这个类中

  

public class VersionControllerSelector : DefaultHttpControllerSelector
    {
        private HttpConfiguration _config;
        IDictionary<string, HttpControllerDescriptor> ctlMapping;
        public VersionControllerSelector(HttpConfiguration config) : base(config)
        {
            _config = config;
        }
        /// <summary>
        /// 获得控制器下所有的动作
        /// </summary>
        /// <returns></returns>
        public override IDictionary<string, HttpControllerDescriptor> GetControllerMapping()
        {
            Dictionary<string, HttpControllerDescriptor> dict = new Dictionary<string, HttpControllerDescriptor>();

            foreach (var asm in _config.Services.GetAssembliesResolver().GetAssemblies())
            {
                //获取所有继承自ApiController的非抽象类
                var controllerTypes = asm.GetTypes()
                    .Where(t => !t.IsAbstract &&
                    typeof(ApiController).IsAssignableFrom(t)).ToArray();
                foreach (var ctrlType in controllerTypes)
                {
                //在从namespace中提取出版本号,这里用的正则表达式来获取
                    var match = Regex.Match(ctrlType.Namespace, @"\.v(\d)");
                    if (match.Success)
                    {
                        string verName = match.Groups[1].Value; //拿到版本号
                        var matchController = Regex.Match(ctrlType.Name, @"^(.+)Controller$");
                        if (matchController.Success)
                        {
                            string ctrlName = matchController.Groups[1].Value; //拿到控制器的名字
                            string key = ctrlName + "v" + verName;//将控制器和版本号拼接起来
                            dict[key] = new HttpControllerDescriptor(_config, ctrlName, ctrlType);
                        }

                    }
                }

            }
            ctlMapping = dict;
            //因为这个方法里已经拿到了,所以把它缓存起来,给SelectController中使用
            return dict;
        }

        /// <summary>
        /// 选择那个版本下的控制器
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public override HttpControllerDescriptor SelectController(HttpRequestMessage request)
        {
            //没有的话在调用一次
            if(ctlMapping==null)
            {
                //获取所有Controller键值集合
                ctlMapping = GetControllerMapping();
            }
            //获取路由数据
            var routeData = request.GetRouteData();
            //从路由中获取当前Controller的名字
            string controllerName = routeData.Values["controller"].ToString();
            //从url中获得版本号
            string verNum =Regex.Match(request.RequestUri.PathAndQuery, @"api/v(\d+)").Groups[1].Value;
            string key = controllerName + "v" + verNum;
            if (ctlMapping.ContainsKey(key))
            {
                return ctlMapping[key];
            }else
            {
                return null;
            }
        }
    }

4)在 WebApiConfig中添加

 config.Services.Replace(typeof(IHttpControllerSelector),new VersionControllerSelector(config));

5)最后我们来测试写好的api

  

    这个可以说是个一劳永逸的方法,新增版本后只需要新増一条webconfig中配置即可。

posted @ 2018-06-01 16:20  an_blog  阅读(186)  评论(0)    收藏  举报