Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET API versioning

I'm new to ASP.NET but I'm looking to implement some versioning for a new API I'm about to start.

I'm not even sure if what I'm looking for is possible but I'm after a very clean version method using a header variable.

Ideally I want to be able to have a versions folder within the code structure and different folders containing the different API versions within that. Each version folder would contain a full copy of the core API code so I'd know there would never be any conflicts etc. I know this would inflate the code but it's worth to keep it very clean and there would only be over 2-3 versions of the API active.

I've found many header samples on the Internet but they all require the classes to be in different namespaces and if I'm doing a complete copy of the code then it's not practical to have to rename all the classes each time they are copied.

Is what I'm trying to do possible? Or is there a cleaner solution when dealing with multiple classes?

like image 275
widget Avatar asked Feb 21 '17 15:02

widget


Video Answer


2 Answers

There are four basic approaches to version the RESTful way -

  1. URI Path This approach takes the following form:

    http://api/v2/Tasks/{TaskId}

  2. URI Parameter This approach takes the following form:

    http://api/Tasks/{TaskId}?v=2

  3. Content Negotiation This is done in the HTTP header.

    Content Type: application/vnd.taskManagerApp.v2.param.json

  4. Request Header This is also done in the HTTP header.

    x-taskManagerApp-version: 2

I personally like 1st approach. You can read Mike Wasson's ASP.NET Web API: Using Namespaces to Version Web APIs.

Many people have modified Mike Wasson's Original Source. I like the one used in ASP.NET Web API 2 book by Jamie Kurtz, Brian Wortman.

Since it has too many moving pieces, I created a sample project at GitHub.

config.Routes.MapHttpRoute(
   name: "DefaultApi",
   routeTemplate: "api/{version}/{controller}",
   defaults: new { version = "v2" }
);

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

Then, you add ApiVersionConstraint

public class ApiVersionConstraint : IHttpRouteConstraint
{
    public ApiVersionConstraint(string allowedVersion)
    {
        AllowedVersion = allowedVersion.ToLowerInvariant();
    }

    public string AllowedVersion { get; private set; }

    public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName,
        IDictionary<string, object> values, HttpRouteDirection routeDirection)
    {
        object value;
        if (values.TryGetValue(parameterName, out value) && value != null)
        {
            return AllowedVersion.Equals(value.ToString().ToLowerInvariant());
        }
        return false;
    }
}

Usage

You just place RoutePrefix on a controller, and you are done.

[RoutePrefix("api/{apiVersion:apiVersionConstraint(v1)}/values")]
public class ValuesController : ApiController
{
    // GET api/v1/values
    [Route("")]
    public IEnumerable<string> Get()
    {
        return new string[] { "v1-value1", "v1-value2" };
    }

    // GET api/v1/values/5
    [Route("{id}")]
    public string Get(int id)
    {
        return "v1-value-" + id;
    }
}
like image 140
Win Avatar answered Oct 10 '22 08:10

Win


A bit of a late answer, but for anyone still wanting to apply versioning to ASP.NET Web API stacks, ASP.NET API Versioning has become a very common way to achieve it. The Basic Sample will take you through all the necessary steps to get started.

API Versioning supports query string, header, media type, and URL segment out-of-the-box. You can compose methods together if you support more than one and you can even create your own method to extract the API version. The default method of versioning is by query string. Despite its popularity and the suggestion from @Win, I would not recommend versioning by URL segment. It is the least RESTful method as it violates the Uniform Interface constraint and has a plethora of problematic edge cases.

Start by adding the ASP.NET Web API Versioning NuGet package. Exactly how you want to version and how you want to organize your code is highly subjective, but here's a very barebones setup:

namespace Example
{
   public class WebApiConfig
   {
       public static Configure(HttpConfiguration config)
       {
           // TODO: this is the minimum, but there are many options that can be configured
           config.AddApiVersioning();

           // TODO: remaining configuration
       }
   }

   // this is one of many possible ways you might organize controllers
   namespace Controllers
   {
       namespace V1
       {
           [ApiVersion("1.0")]
           [RoutePrefix("values")]
           public class ValuesController : ApiController
           {
               // GET /values?api-version=1.0
               [Route]
               public IHttpActionResult Get(ApiVersion version) =>
                   Ok(new []{$"v{version}-Value1", $"v{version}-Value2"});

               // GET /values/{id}?api-version=1.0
               [Route("{id}")]
               public IHttpActionResult Get(int id, ApiVersion version) =>
                   Ok($"v{version}-{id}")
           }
       }

       namespace V2
       {
           [ApiVersion("2.0")]
           [RoutePrefix("values")]
           public class ValuesController : ApiController
           {
               // GET /values?api-version=2.0
               [Route]
               public IHttpActionResult Get(ApiVersion version) =>
                   Ok(new []{$"v{version}-Value1", $"v{version}-Value2"});

               // GET /values/{id}?api-version=2.0
               [Route("{id}")]
               public IHttpActionResult Get(int id, ApiVersion version) =>
                   Ok($"v{version}-{id}")
           }
       }
   }
}
like image 39
Chris Martinez Avatar answered Oct 10 '22 09:10

Chris Martinez