`

Asp.net WebApi版本控制

阅读更多

关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 pdf

    最后,考虑到API的可演化性,《ASP.NET Web API设计》可能还会讨论版本控制、API设计的最佳实践,以及如何利用Swagger等工具来生成API文档,帮助开发者和消费者更好地理解和使用API。 总之,ASP.NET Web API提供了...

    ASP.NET Web API 2 Recipes(英文pdf)

    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设计》依托ASP.NET Web API阐述API设计与开发的通用技术,是一本全面介绍如何构建真实可演化API的实践指南。本书共分三部分。第一部分介绍Web/HTTP和API开发的基础知识,介绍ASP.NET Web API,为...

    ASP.NET Web API 接收文件上传

    要实现ASP.NET Web API文件上传,你需要创建一个可以接收多部分/FormData类型的控制器方法。`HttpPostedFileBase`类是.NET Framework提供的,用于处理上传文件的接口。以下是一个简单的示例: ```csharp [HttpPost]...

    ASP.NET Web API 2 框架揭秘,带完整目录和源码

    6. **Web API控制器**:Web API 使用特殊的控制器类,继承自`ApiController`,负责处理HTTP请求并返回响应。控制器方法通常对应于HTTP操作,如HttpGet、HttpPost等。 7. **自定义媒体类型格式化**:Web API 2 允许...

    《ASP.NET Web API 2框架揭秘》源码示例

    第1章 S101 一个简单的ASP.NET Web API应用 第2章 S201 通过路由实现请求地址与.aspx页面的映射 S202 基本路由注册 S203 在路由注册中指定约束 S204 针对现有物理文件的路由(关闭) S205 针对现有物理文件的...

    asp.net webapi2 基于token令牌的身份验证

    本文将深入探讨ASP.NET WebAPI2如何实现基于Token的令牌身份验证。 首先,我们需要理解Token的身份验证原理。这种机制下,用户登录后,服务器会生成一个唯一的Token,这个Token包含了一些关于用户的信息,经过加密...

    Asp.Net WebApi 上传文件方法(原生js上传和JQ ajax上传)

    在这个例子中,`/api/UploadFile`是你的WebApi控制器中处理文件上传的URL。服务器端,你需要在WebApi控制器中创建一个`HttpPost`标记的方法,接收`HttpPostedFileBase`类型的参数。 对于jQuery AJAX上传,我们可以...

    ASP.NET Web API

    8. **版本控制**:ASP.NET Web API允许通过URL或HTTP头部进行API版本控制,确保向后兼容性。 9. **跨域支持**:通过CORS(Cross-Origin Resource Sharing)机制,Web API可以安全地支持来自不同源的请求,这对于...

    ASP.NET WebAPI连接数据库的方法

    首先,需要了解的是WebAPI项目的基本创建流程,即通过Visual Studio创建一个新的*** Web Application (.NET Framework)项目,选择WebAPI作为项目模板。创建项目后,我们会在项目中看到一个名为ValuesController的...

    ASP.NET Web API 高级编程(Pro ASP.NET Web API HTTP Web Services in ASP.NET)

    最新的Pro ASP.NET Web API HTTP Web Services in ASP.NET; 英文高清pdf; 2013年新书,学习ASP.NET Web API 现阶段最权威的一本专业教材; 前置技能:C#(掌握) , MVC(掌握或了解) ,数据库(掌握),HTML(掌握)...

    asp.net WebAPI OWIN OAuth2.0授权自定义返回结果及错误或异常问题处理办法

    asp.net WebAPI OWIN OAuth2.0授权自定义返回结果及错误或异常问题处理核心代码,详情: https://www.cnblogs.com/wgx0428/p/12315546.html

    ASP.NET Web API 2 框架揭秘(完整带目录、源码版

    《ASP.NET Web API 2框架揭秘》一本注重实证的书,功能各异、多达120个可供下载的示例,大量最佳实践与实用性扩展,可直接用于解决实际开发问题。全新的学习方法,通过完整论证来实现彻底的融会贯通。本书可以作为讲...

    Asp.Net Web API 实例

    5. **模型绑定和验证**:Asp.Net Web API支持模型绑定,自动将请求数据(如JSON或FormUrlEncoded)映射到控制器方法的参数。同时,你可以利用`System.ComponentModel.DataAnnotations`命名空间中的特性进行数据验证...

    asp.net webApi

    在描述中提到的"webapi技术小demo"是一个示例项目,旨在帮助学习者理解和实践如何使用ASP.NET WebAPI来开发接口。这样的项目通常包含了从创建控制器、定义路由到处理HTTP请求和返回响应的完整流程。 1. **控制器...

    ASP.NET Web API 2 框架揭秘(完整带目录、源码版)

    ASP.NET Web API 2框架揭秘》一本注重实证的书,功能各异、多达120个可供下载的示例,大量最佳实践与实用性扩展,可直接用于解决实际开发问题。全新的学习方法,通过完整论证来实现彻底的融会贯通。

    【ASP.NET编程知识】.net webapi接收xml格式数据的3种情况小结.docx

    在 ASP.NET 编程中,Web API 是一个框架,可以很容易构建达成了广泛的 HTTP 服务客户端,包括浏览器和移动设备。是构建 RESTful 应用程序的理想平台的 .NET 框架。在本文中,我们将主要介绍 .NET Web API 接收 XML ...

    ASP.NET WebAPI 接口自测工具(v4.0最新版)

    自己在研究ASP.NET WebAPI的过程中,为了方便自己调试,写了一个自测工具。 可以进行 application/json 协议的访问,也可以进行 application/x-www-form-urlencoded 协议的访问。 为了方便经常调试某个接口,可将 ...

Global site tag (gtag.js) - Google Analytics