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
        }
    }
6
  • you are using ConfigureAuth ? Commented Oct 18, 2019 at 10:47
  • Yes for oAuth2 authentication Commented Oct 18, 2019 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? Commented Oct 18, 2019 at 13:02
  • Hi Craig, so I could just leave [Route("api/v{version:apiVersion}/[controller]")] out of the controller? Commented Oct 18, 2019 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. Commented Feb 24, 2020 at 22:13

4 Answers 4

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() }
);
Sign up to request clarification or add additional context in comments.

10 Comments

Thanks Hasta, corrected as you suggested. But still receiving 'System.invalidOperationException'.
Could you confirm that you are using x.x.WebApi.Versioning if so do you have using Microsoft.Web.Http.Routing; in you WebApiConfig?
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.
I believe that is your issue. Use Microsoft.Web.Http.Routing instead.
When trying 'using Microsoft.Web.Http.Routing' then DefaultInlineConstraintResolver is no longer working 'Missing assembly reference'
|
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;

3 Comments

Hi Ahmed, will this approach work? Since 'Api-version' will not be provided in the request parameters. (only in URL)
@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.
The link you provide on your answer is dead
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" };
         }
     }
 }

2 Comments

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 example in the API Versioning repo demonstrates one way of setting it up.
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.
-3

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

1 Comment

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.