【发布时间】:2016-07-17 14:04:12
【问题描述】:
在我的项目中,我实现了自定义路由约束,以允许通过自定义标头变量 (api-version) 进行 API 版本控制,类似于 this sample project on Codeplex,尽管我修改了约束以允许使用 major.minor 约定。
这涉及创建两个单独的控制器,它们的路由通过 FullVersionedRoute 属性进行区分:
Sample1Controller.cs
/// <summary>
/// v1.0 Controller
/// </summary>
public class Sample1Controller : ApiController
{
[FullVersionedRoute("api/test", "1.0")]
public IEnumerable<string> Get()
{
return new[] { "This is version 1.0 test!" };
}
}
Sample2Controller.cs
/// <summary>
/// v2.0 Controller
/// </summary>
public class Sample2Controller : ApiController
{
[FullVersionedRoute("api/test", "2.0")]
public IEnumerable<string> Get()
{
return new[] { "This is version 2.0 test!" };
}
}
FullVersionedRoute.cs
using System.Collections.Generic;
using System.Web.Http.Routing;
namespace HelperClasses.Versioning
{
/// <summary>
/// Provides an attribute route that's restricted to a specific version of the api.
/// </summary>
internal class FullVersionedRoute : RouteFactoryAttribute
{
public FullVersionedRoute(string template, string allowedVersion) : base(template)
{
AllowedVersion = allowedVersion;
}
public string AllowedVersion
{
get;
private set;
}
public override IDictionary<string, object> Constraints
{
get
{
var constraints = new HttpRouteValueDictionary();
constraints.Add("version", new FullVersionConstraint(AllowedVersion));
return constraints;
}
}
}
}
FullVersionConstraint.cs
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Web.Http.Routing;
namespace HelperClasses.Versioning
{
/// <summary>
/// A Constraint implementation that matches an HTTP header against an expected version value.
/// </summary>
internal class FullVersionConstraint : IHttpRouteConstraint
{
public const string VersionHeaderName = "api-version";
private const string DefaultVersion = "1.0";
public FullVersionConstraint(string allowedVersion)
{
AllowedVersion = allowedVersion;
}
public string AllowedVersion
{
get;
private set;
}
public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary<string, object> values, HttpRouteDirection routeDirection)
{
if (routeDirection == HttpRouteDirection.UriResolution)
{
var version = GetVersionHeader(request) ?? DefaultVersion;
return (version == AllowedVersion);
}
return false;
}
private string GetVersionHeader(HttpRequestMessage request)
{
IEnumerable<string> headerValues;
if (request.Headers.TryGetValues(VersionHeaderName, out headerValues))
{
// enumerate the list once
IEnumerable<string> headers = headerValues.ToList();
// if we find once instance of the target header variable, return it
if (headers.Count() == 1)
{
return headers.First();
}
}
return null;
}
}
}
这很好用,但是auto-generated help files 无法区分两个控制器中的操作,因为它们看起来像相同的路由(如果您只注意 url 路由,默认情况下会这样做)。因此,来自 Sample2Controller.cs 的操作会覆盖来自 Sample1Controller.cs 的操作,因此帮助页面上仅显示 Sample2 API。
有没有办法配置 Web API 帮助页面包以识别自定义约束并识别有两个单独的 API,然后在帮助页面上将它们显示为单独的 API 组?
【问题讨论】:
-
我在使用自定义路由时遇到了类似的问题。我的问题是为什么不部署到 server/approot/version,将多个版本嵌套在同一个代码库中似乎有点奇怪,因为每次重新部署都是对所有版本的更改,即使逻辑中不应该有任何更改原版你不能100%
-
根据这篇文章,这是因为该解决方案会给人一种印象,即您拥有多个不同的资源(即 v1/myresource、v2/myresource、v3/myresource)。实际上,只有一个资源(即 /myresource),只是它的不同版本(基于 Accept 标头提供服务,这是根据 HTTP SPEC 的目的)。这是一个语义论证。 troyhunt.com/2014/02/your-api-versioning-is-wrong-which-is.html
标签: c# asp.net-mvc asp.net-web-api