为Web Api版本控制自定义MapHttpAttributeRoutes

我正在实现Web API版本控制中的Web API版本控制 。 我的控制器位于2个独立的命名空间中,我使用自定义的SelectController方法根据查询参数选择要使用的版本。 例如

http://myapi/api/values?version=1.0 

这一切都很好,但控制器中的某些操作使用Route属性

 [Route("api/values/getNames")] public HttpResponseMessage Get() { ... } 

默认使用哪些映射到正确的控制器

 config.MapHttpAttributeRoutes(); 

在WebApiConfig.cs中

如果我有多个版本的API具有相同的路由,这将不起作用。 我是否能够为config.MapHttpAttributeRoutes()提供自定义实现,以便我可以选择要使用的API的正确版本,或者有更好的方法吗?

在Codeplex上的官方WebApi 2.1 examplex中有一个例子。 它依赖于请求标头值来存储版本。

我认为它更好,因为它允许所有版本的路由保持不变。 客户端只需在请求中包含HTTP标头(在本例中为版本号)即可选择版本。

此示例演示如何使用ASP.NET Web API中的属性路由和约束来通过“api-version”HTTP标头动态过滤控制器。 当路由使用约束时,每个约束都有机会阻止路由匹配给定的请求。 在此示例中,自定义RouteFactoryAttribute(VersionedRoute)为每个属性路由添加约束。

自定义约束实现(VersionConstraint)是基于匹配整数值的’api-version’的值实现的。 约束的允许版本的值由放置在每个控制器上的VersionedRoute属性提供。 当请求进入时,’api-version’的标头值与预期版本匹配。 此示例使用标头,但约束实现可以使用任何条件来确定请求是否对路由有效。

无论如何,最终结果将最终看起来像这样:

 [VersionedRoute("api/Customer", 1)] public class CustomerVersion1Controller : ApiController { // controller code goes here } [VersionedRoute("api/Customer", 2)] public class CustomerVersion2Controller : ApiController { // controller code goes here } 

除了查询参数支持(即使用名为’api-version’的头文件或名为’?api-version = XXX的查询字符串参数)之外,这个解决方案将允许您使用Web API 2版本化路由(标头)。 ”。

HTTP Route约束完成工作:

 ///  /// Add a route constraint to detect version header or by query string ///  public class RouteVersionHttpConstraint : IHttpRouteConstraint { public const string VersionHeaderName = "api-version"; private const int DefaultVersion = 1; ///  /// Add a route constraint to detect version header or by query string ///  ///  public RouteVersionHttpConstraint(int allowedVersion) { AllowedVersion = allowedVersion; } public int AllowedVersion { get; private set; } ///  /// Perform the controller match ///  ///  ///  ///  ///  ///  ///  public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary values, HttpRouteDirection routeDirection) { if (routeDirection == HttpRouteDirection.UriResolution) { int version = GetVersionHeaderOrQuery(request) ?? DefaultVersion; if (version == AllowedVersion) { return true; } } return false; } ///  /// Check the request header, and the query string to determine if a version number has been provided ///  ///  ///  private int? GetVersionHeaderOrQuery(HttpRequestMessage request) { string versionAsString; IEnumerable headerValues; if (request.Headers.TryGetValues(VersionHeaderName, out headerValues) && headerValues.Count() == 1) { versionAsString = headerValues.First(); int version; if (versionAsString != null && Int32.TryParse(versionAsString, out version)) { return version; } } else { var query = System.Web.HttpUtility.ParseQueryString(request.RequestUri.Query); string versionStr = query[VersionHeaderName]; int version = 0; int.TryParse(versionStr, out version); if (version > 0) return version; } return null; } } 

和路线工厂:

 ///  /// Versioning support for the WebAPI controllers ///  public class RouteVersionAttribute : RouteFactoryAttribute { public int Version { get; private set; } public RouteVersionAttribute() : this(null, 1) { } ///  /// Specify a version for the WebAPI controller ///  ///  public RouteVersionAttribute(int version) : this(null, version) { } public RouteVersionAttribute(string template, int version) : base(template) { Version = version; } public override IDictionary Constraints { get { var constraints = new HttpRouteValueDictionary(); constraints.Add("version", new RouteVersionHttpConstraint(Version)); return constraints; } } public override IDictionary Defaults { get { var defaults = new HttpRouteValueDictionary(); defaults.Add("version", 1); return defaults; } } } 

用法:

 [RouteVersion("api/versiontest", 1)] public class Version1TestController : BaseApiController { // get: api/versiontest [HttpGet] public HttpResponseMessage get() { return Request.CreateResponse(HttpStatusCode.OK, new { Version = "API Version 1 selected" }); } } [RouteVersion("api/versiontest", 2)] public class Version2TestController : ApiController { // get: api/versiontest [HttpGet] public HttpResponseMessage get() { return Request.CreateResponse(HttpStatusCode.OK, new { Version = "API Version 2 selected" }); } }