【问题标题】:Web-Api versioning with URL c#带有 URL c# 的 Web-Api 版本控制
【发布时间】:2023-11-28 04:37:01
【问题描述】:

我已经苦苦挣扎了好几天,我正在尝试使用 url 后缀实现我的 API 版本。 f.e. http://localhost/api/v1/sites & http://localhost/api/v2/sites.

我猜我已经很接近了,但突然间我把一切都搞砸了..

非常感谢您帮助我。

现在我收到:“System.invalidOperationException:”映射到约束键“apiVersion”的约束类型“ApiVersionRouteConstraint”必须实现 IhttpRouteConstraint 接口。在 Global.asax.cs 上

Global.asax.cs

using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;

namespace HIDDEN
{
    public class WebApiApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            GlobalConfiguration.Configure(WebApiConfig.Register);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
        }
    }
}

WebApiConfig.cs

using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.Owin.Security.OAuth;
using System.Web.Http;
using System.Web.Http.Routing;

namespace HIDDEN
{
    public static class WebApiConfig
        {
        public static void Register(HttpConfiguration config)
        {
            // Web API configuration and services  
            // Configure Web API to use only bearer token authentication.  
            config.SuppressDefaultHostAuthentication();
            config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));

            var contraintResolver = new DefaultInlineConstraintResolver()
            {
                ConstraintMap =
                {
                    ["apiVersion"] = typeof(ApiVersionRouteConstraint)
                }
            };

            // Web API routes  
            config.MapHttpAttributeRoutes(contraintResolver);
            config.AddApiVersioning(o =>
            {
                o.AssumeDefaultVersionWhenUnspecified = true;
                //o.DefaultApiVersion = new ApiVersion(1, 0);
            });

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

            // WebAPI when dealing with JSON & JavaScript!  
            // Setup json serialization to serialize classes to camel (std. Json format)  
            var formatter = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
            formatter.SerializerSettings.ContractResolver = new     Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver();

            // Adding JSON type web api formatting.  
            config.Formatters.Clear();
            config.Formatters.Add(formatter);

        }
    }
}   

RouteConfig.cs

using System.Web.Mvc;
using System.Web.Routing;


namespace HIDDEN
{
    public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );
        }
    }
}

站点控制器

using FirebirdSql.Data.FirebirdClient;
using System;
using System.Web.Http;
using System.Configuration;
using System.Collections;
using Microsoft.Web.Http;

        namespace HIDDEN.Controllers.V1
        {
        [ApiVersion("1.0")]
        [Route("api/v{version:apiVersion}/[controller]")]

        public class SitesController : ApiController
        {
              //code for V1
        }

        namespace HIDDEN.Controllers.V2
        {
        [ApiVersion("2.0")]
        [Route("api/v{version:apiVersion}/[controller]")]

        public class SitesController : ApiController
        {
              //code for V2
        }
    }

【问题讨论】:

  • 你在使用 ConfigureAuth 吗?
  • oAuth2 身份验证是的
  • 在管道中设置 API 版本控制属性并在每个控制器(使用属性路由)上指定 ApiVersion 有什么好处,无论如何你都可以使用 [Route("api/v2/[controller]")]?它是否在其他地方添加了其他功能?
  • 嗨 Craig,所以我可以将 [Route("api/v{version:apiVersion}/[controller]")] 留在控制器之外?
  • @CraigH - 是的,还有其他功能。 API 版本控制为版本控制策略、发现、文档等提供了一个全面的框架。使用文字字符串 v2 对于 API 版本控制来说是未知的,因为它不允许、使用或解析 魔术字符串{version:apiVersion} 路由参数定义名称 version,它使用 ApiVersionRouteConstraint。这是 API 版本控制如何知道从哪里提取值的方式。

标签: c# asp.net rest asp.net-web-api aspnet-api-versioning


【解决方案1】:

确保您使用的是正确的ApiVersionRouteConstraint,因为Microsoft.AspNetCore.Mvc.RoutingMicrosoft.Web.Http.Routing 之间似乎存在冲突

如果你正在使用这个版本控制库Microsoft.AspNet.WebApi.Versioning,你应该使用Microsoft.Web.Http.Routing

using Microsoft.Web.Http.Routing;

此外,您还可以将apiVersion 约束添加到您的路线,如下所示

config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/v{apiVersion}/{controller}/{action}/{id}",
                defaults: new { id = RouteParameter.Optional },
                constraints: new { apiVersion = new ApiVersionRouteConstraint() }
);

【讨论】:

  • 感谢 Hasta,按照您的建议进行了更正。但仍然收到“System.invalidOperationException”。
  • 您能否确认您正在使用 x.x.WebApi.Versioning 如果是的话,您的 WebApiConfig 中有 using Microsoft.Web.Http.Routing; 吗?
  • 嗨 Hasta,查看上面的代码,当您尝试使用 Microsoft.Web.Http.Routing 时,您会看到“使用 System.Web.Http.Routing”,我遇到了一个新错误: “ApiVersionRouteConstraint”是“Microsoft.Web.Http.Routing.ApiVersionRouteConstraint”和“Microsoft.AspNetCore.Mvc.routing.ApiVersionRouteConstraint”之间的模糊引用。
  • 我相信这是你的问题。请改用 Microsoft.Web.Http.Routing。
  • 当尝试“使用 Microsoft.Web.Http.Routing”时,DefaultInlineConstraintResolver 不再工作“缺少程序集引用”
【解决方案2】:

我认为你没有很好地配置 ASP.Net Web API 版本

http://www.intstrings.com/ramivemula/articles/asp-net-web-api-versioning/

你可以看看这篇文章,你可能有一些没有正确配置的属性,例如:

ReportApiVersions = true;

【讨论】:

  • 嗨,艾哈迈德,这种方法行得通吗?由于请求参数中不会提供“Api-version”。 (仅在 URL 中)
  • @Gregory - 是的,这行得通。 API 版本控制不关心在报告 API 版本时请求 API 版本的位置。所有 API 版本信息都是预先发现和缓存的。如果您考虑一下,它必须这样做才能从候选操作中做出决定。匹配候选人确实需要读取一个或多个包含传入 API 版本的请求源。一旦进行匹配,所有其他候选者都是已知的,并且如果这样配置,则可以报告其 API 版本。
【解决方案3】:

已修复,最大的问题是不同版本的控制器在不同的命名空间中。

这成功了:

 namespace HIDDEN.Controllers.Sites
 {
     [Authorize]
     [ApiVersion("1.0")]
     [Route("v{version:apiVersion}/sites")]   
     public class valuesV1Controller : ApiController
     {
         // GET: api/values
         public IEnumerable<string> Get()
         {
             return new string[] { "value1", "value2" };
         }
     }

     [Authorize]
     [ApiVersion("2.0")]
     [ApiVersion("2.1")]
     [Route("v{version:apiVersion}/sites")]
     public class valuesV2Controller : ApiController
     {
         // GET: api/values
         public IEnumerable<string> Get()
         {
             return new string[] { "value3", "value4" };
         }
     }
 }

【讨论】:

  • 虽然这可能是公认的答案,但我要补充一点,命名空间不是问题。控制器在不同的命名空间中可能具有相同的类型名称。 API 版本控制存储库中的 Version By Namespace 示例演示了一种设置方法。
  • 另外要提的是,原来的问题使用了[controller] 令牌,但我相信只有在 ASP.NET Core 路由模板中才支持。此问题和线程适用于 ASP.NET Web API。我还要重申,混合路由样式(基于约定与基于属性)会使故障排除变得困难。我强烈建议选择一种方法。如果将两者混合使用,则存在一些极端情况,即 API 版本控制在 Web API 中无法正常工作。
【解决方案4】:

您可以使用swagger 获得更好的 api 文档,也可以用于 api 版本控制。

【讨论】:

  • 您的回答与提出的问题无关。请先阅读问题,然后添加您的答案。或者如果有不清楚的地方最好写 cmets。
最近更新 更多