3

I'm struggling for days now, i'm trying to achieve my API to version with the url suffix. f.e. http://localhost/api/v1/sites & http://localhost/api/v2/sites.

I was pretty close I guess but suddenly I messed it all up..

Many thanks for helping me out.

Now i'm receiving: "System.invalidOperationException:' The constraint type 'ApiVersionRouteConstraint' which is mapped to constraint key 'apiVersion' must implement the IhttpRouteConstraint interface.' on 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 }
            );
        }
    }
}

SitesController

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
        }
    }
Gregory
  • 149
  • 2
  • 5
  • 13
  • you are using ConfigureAuth ? – Ahmed Msaouri Oct 18 '19 at 10:47
  • Yes for oAuth2 authentication – Gregory Oct 18 '19 at 11:05
  • What is the advantage of setting up API versioning attributes into the pipeline and specifying `ApiVersion` on every controller (which is using attribute routing), when you can do `[Route("api/v2/[controller]")]` anyway? Does it add other functionality in somewhere else? – Craig H Oct 18 '19 at 13:02
  • Hi Craig, so I could just leave [Route("api/v{version:apiVersion}/[controller]")] out of the controller? – Gregory Oct 18 '19 at 13:35
  • @CraigH - yes, there is additional functionality. API Versioning provides a comprehensive framework for versioning policies, discovery, documentation, and so on. Using the literal string `v2` will be unknown to API versioning as it does not allow, use, or parse _magic strings_. The `{version:apiVersion}` route parameter defines the name **version**, which uses the **ApiVersionRouteConstraint**. This is _how_ API versioning knows where to extract the value from. – Chris Martinez Feb 24 '20 at 22:13
  • @Gregory - no, API Versioning will **not** work if you remove the route parameter with the `apiVersion` route constraint. This is how API Versioning extracts the version number from your template. Without it, `v2` will be a constant string segment and API Versioning will think that no version has been specified because will not know how to _read_ it from a request. – Chris Martinez Feb 24 '20 at 22:14

4 Answers4

4

Make sure you are using the correct ApiVersionRouteConstraint as there seems to be a conflict between Microsoft.AspNetCore.Mvc.Routing and Microsoft.Web.Http.Routing

If you are using this versioning library Microsoft.AspNet.WebApi.Versioning you should use Microsoft.Web.Http.Routing

using Microsoft.Web.Http.Routing;

In addition you can also add apiVersion constraints to your routes like below

config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/v{apiVersion}/{controller}/{action}/{id}",
                defaults: new { id = RouteParameter.Optional },
                constraints: new { apiVersion = new ApiVersionRouteConstraint() }
);
Hasta Tamang
  • 2,205
  • 1
  • 18
  • 17
  • Thanks Hasta, corrected as you suggested. But still receiving 'System.invalidOperationException'. – Gregory Oct 18 '19 at 11:17
  • Could you confirm that you are using x.x.WebApi.Versioning if so do you have `using Microsoft.Web.Http.Routing;` in you WebApiConfig? – Hasta Tamang Oct 18 '19 at 12:45
  • Hi Hasta, Check out the code above, as you can see 'using System.Web.Http.Routing' when attempting to use Microsoft.Web.Http.Routing i'm getting a new error: 'ApiVersionRouteConstraint' is an ambiguous reference between 'Microsoft.Web.Http.Routing.ApiVersionRouteConstraint' and 'Microsoft.AspNetCore.Mvc.routing.ApiVersionRouteConstraint. – Gregory Oct 18 '19 at 13:33
  • I believe that is your issue. Use Microsoft.Web.Http.Routing instead. – Hasta Tamang Oct 18 '19 at 13:43
  • When trying 'using Microsoft.Web.Http.Routing' then DefaultInlineConstraintResolver is no longer working 'Missing assembly reference' – Gregory Oct 18 '19 at 14:16
  • You need to install this package from Nuget https://www.nuget.org/packages/Microsoft.AspNet.WebApi.Versioning/3.0.4 – Hasta Tamang Oct 18 '19 at 14:23
  • Check your references you should have `System.Web.Http`. – Hasta Tamang Oct 18 '19 at 14:32
  • This package is installed, just did a reinstall and System.Web.Http is referenced – Gregory Oct 18 '19 at 14:38
  • And do you have `using System.Web.Http.Routing` ? [DefaultInlineConstraintResolve](https://learn.microsoft.com/en-us/previous-versions/aspnet/dn202118(v=vs.118)) lives inside `System.Web.Http.Routing` namespace – Hasta Tamang Oct 18 '19 at 14:46
  • It's also worth mentioning that mixing attribute and convention-based routing should be avoided. Yes - it's supported, but in practice it can become very confusing over time. I recommend picking one or the other, then stick to it. – Chris Martinez Nov 03 '19 at 14:42
1

I think you don't have well configured ASP.Net Web API Versioning

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

You can look at this post, you might have some property without the correct configuration such as:

ReportApiVersions = true;
Ahmed Msaouri
  • 316
  • 1
  • 10
  • Hi Ahmed, will this approach work? Since 'Api-version' will not be provided in the request parameters. (only in URL) – Gregory Oct 18 '19 at 13:38
  • @Gregory - yes, this will work. API Versioning doesn't care where the API version is requested when reporting API versions. All API version information is discovered and cached upfront. If you think about it, it _has_ to do this in order to make a decision from the candidate actions. Matching a candidate does require reading one or more request sources that contain the incoming API version. Once a match is made, all other candidates are already known and their API versions can be reported if so configured. – Chris Martinez Feb 24 '20 at 22:09
1

Fixed it, biggest problem was the different versions of the controller being in a different namespace.

This did the trick:

 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" };
         }
     }
 }
Gregory
  • 149
  • 2
  • 5
  • 13
  • 1
    While this might the accepted answer, I'll add that namespaces are not an issue. Controllers may have the same type name in different namespaces. The [Version By Namespace](https://github.com/microsoft/aspnet-api-versioning/tree/master/samples/webapi/ByNamespaceWebApiSample) example in the API Versioning repo demonstrates one way of setting it up. – Chris Martinez Feb 24 '20 at 22:20
  • 1
    Another thing to mention is that the original question uses the `[controller]` token, but I believe that is only supported in ASP.NET Core route templates. This question and thread is for ASP.NET Web API. I'll also reiterate that mixing routing styles (convention-based vs attribute-based) can make troubleshooting difficult. I strongly recommend choosing one method or the other. There are edge cases where API Versioning will not work properly in Web API if you mix the two. – Chris Martinez Feb 24 '20 at 22:22
-2

You can use swagger for better api documentation also for api versioning.

Abdul Rauf
  • 23
  • 1
  • 4
  • 2
    Your answer is not related to the question being asked. Please read the question first and then add your answer. or better write comments if anything is not clear. – Mubashar Shahzad Aug 08 '21 at 12:55