1

我继承了一个遗留的 WebAPI 系统,该系统目前在路由模式中使用下划线来表示版本。例如/api/1_0/account/api/1_1/account等等。

我正在尝试更新自动生成的文档以使用 Swagger,但是使用ApiVersion包含下划线的属性的显式路由会导致异常。例如,这很好用:

[ApiVersion("1")]

但是,这会引发异常:

[ApiVersion("1_0")] // < note '_0' here
[RoutePrefix("api/{version:apiVersion}/account")]
public class AccountController : ApiBaseController
{
  // actions...
}

例外是:

FormatException:指定的 API 版本状态“_1”无效。
System.InvalidOperationException:“无法比较数组中的两个元素。”
在 System.Collections.Generic.ArraySortHelper`1.Sort(T[] 键,Int32 索引,Int32 长度,IComparer`1 比较器)
在 System.Array.Sort[T](T[] 数组,Int32 索引,Int32 长度, IComparer`1 比较器)
在 System.Collections.Generic.List`1.Sort(Int32 索引,Int32 计数,IComparer`1 比较器)
在 Microsoft.Web.Http.Dispatcher.ApiVersionControllerSelector.InitializeControllerInfoCache()
在 System.Lazy`1。 CreateValue()
在 System.Lazy`1.LazyInitValue()
在 System.Lazy`1.get_Value()
在 Microsoft.Web.Http.Dispatcher.ApiVersionControllerSelector.GetControllerMapping()
在 System.Web.Http.Routing.AttributeRoutingMapper.AddRouteEntries(SubRouteCollection 收集器,HttpConfiguration 配置,IInlineConstraintResolver constraintResolver,IDirectRouteProvider directRouteProvider)
在 System.Web.Http.Routing.AttributeRoutingMapper。 <>c__DisplayClass1_1.b__1()
在 System.Web.Http.Routing.RouteCollectionRoute.EnsureInitialized(Func`1 初始化程序)
在 System.Web.Http.Routing.AttributeRoutingMapper.<>c__DisplayClass1_0.b__0(HttpConfiguration config)
在 System.Web。
E:\ProjectPath\Foo.cs:line 25 中 ProjectName.Startup.Configuration(IAppBuilder app) 处的Http.HttpConfiguration.EnsureInitialized()

问题很明显,但是如何在版本属性值中包含下划线?问题令人困惑,因为我假设类的内部(在某些时候)将值解析为整数,但属性本身接受字符串......?那为什么会这样呢?

4

3 回答 3

3

关于为什么这不起作用的一些附加信息。该Microsoft.AspNet.WebApi.Versioning包遵循语义版本控制规则,要求主要部分和次要部分之间的分隔符是一个句点。请参阅此包的规则

通过一些黑客攻击,可以让 API 版本控制包解析下划线。这是非常基本的代码,可能还没有准备好生产,但应该给你一个方向。您需要的第一件事是自定义路由约束(基本上撕掉了默认的):

public class CustomApiVersionRouteConstraint : IHttpRouteConstraint
{
    public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, 
        IDictionary<string, object> values, HttpRouteDirection routeDirection)
    {
        if (string.IsNullOrEmpty(parameterName))
        {
            return false;
        }

        var properties = request.ApiVersionProperties();
        var versionString = "";

        if (values.TryGetValue(parameterName, out object value))
        {
            //This is the real 'magic' here, just replacing the underscore with a period
            versionString = ((string) value).Replace('_', '.');

            properties.RawApiVersion = versionString;
        }
        else
        {
            return false;
        }


        if (ApiVersion.TryParse(versionString, out var requestedVersion))
        {
            properties.ApiVersion = requestedVersion;
            return true;
        }

        return false;
    }
}

并确保 Web API 使用新的约束:

var constraintResolver = new DefaultInlineConstraintResolver()
{
    ConstraintMap =
    {
        ["apiVersion"] = typeof( CustomApiVersionRouteConstraint )
    }
};

config.MapHttpAttributeRoutes(constraintResolver);
于 2018-04-18T16:57:21.423 回答
0

该类ApiVersion有一个ParsePattern定义版本字符串的格式。

const string ParsePattern = @"^(\d{4}-\d{2}-\d{2})?\.?(\d{0,9})\.?(\d{0,9})\.?-?(.*)$";

该模式不允许使用下划线。提供与预期模式不匹配的版本会导致FormatException.

来源:https ://github.com/Microsoft/aspnet-api-versioning/blob/master/src/Common/ApiVersion.cs#L25

ASP.NET API 版本格式文档提供了更多信息(由@DavidG 提供)。

于 2018-04-18T16:46:17.923 回答
0

我看到所有答案都集中在您标题中的问题上...

但你的问题可能出在你的方法上,你提到:

...尝试更新自动生成的文档以使用 Swagger,但是使用ApiVersion包含下划线的属性的显式路由会导致异常。

也许简化属性,只使用RoutePrefix,像这样:

[RoutePrefix("api/1_0/account")]
public class AccountController : ApiController

Bamm,问题解决了......
剩下的就是MultipleApiVersions在你的 SwaggerConfig 中配置,简单吧?
需要一个例子看这里:MultiApiVersions/Swagger_Test/App_Start/SwaggerConfig.cs

这是文档的样子:
http ://swagger-net-test-multiapiversions.azurewebsites.net/swagger/ui/index

于 2018-04-20T15:36:22.913 回答