Table of Contents

IT:AD:ASP.NET:WebAPI:HowTo:Configure WebAPI Routing

Summary

Process

Basic Routing for WebAPI is trivial. It's the same as for MVC, except for

What's rather more difficult is thinking of dealing with Versioning….

Basics

Basic routing assumes your webAPI Controllers will be in the route of the app.

Example:

//You can force the Controller if you want
routes.MapHttpRoute( 
    name: "ClientApi", 
    routeTemplate: "client/detail/{param1}", 
    defaults: new { controller = "ClientDetail" } 
); 

//or use defaults:
routes.MapHttpRoute(
  name:"DefaultApi"
  routeTemplate: "api/{controller}/{id}",
  defaults: new {id = RouteParameter.Optional} 
  );

* The default route will prepend WebAPI controller paths with api, but you can change to anything. * In MVC4, one no longer uses the global.asax ApplicationStart method, but instead uses a AppStart\WebApiConfig class that is put there when you create your first WebAPI controller.

Example:

public class MvcApplication : System.Web.HttpApplication
{
  ...
  WebApiConfig.Register(GlobalConfiguration.Configuration);
}

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
      //or use defaults:
      routes.MapHttpRoute(
        name:"DefaultApi"
        routeTemplate: "api/{controller}/{id}",
        defaults: new {id = RouteParameter.Optional} 
      );
    }
}

Versioning

It would be poor planning to release an APIs to external clients that is not versioned.

Code wise, versioning implies you will have:

Url resourse wise, there are two schools of thought:

After studying the issue (Versioning), I am concluding that the Accepts based solution would be purer, but the gain from the clarity of the url + the fact that the Accepts solution only works for some verbs, that versions should be in the url.

Areas

You can't have multiple Controllers with the same name in the root, so you have to use Areas.

But Areas are not supported out of the box for WebAPI:

With the above solution, one can:

Example:

    public class APIAreaRegistration : AreaRegistration
    {
      public override string AreaName
      {
        get { 
          return "api.v1"; 
        }
      }

      public override void RegisterArea(AreaRegistrationContext context)
      {
        var r = context.Routes.MapHttpRoute(
          "Admin_Api",
          "api/v1/{controller}/{id}",
          new {area = AreaName, id = RouteParameter.Optional},
          null
          );
        if (r.DataTokens == null)
        {
          r.DataTokens = new RouteValueDictionary();
        }
        r.DataTokens["Namespaces"] = new string[] {"XAct.Spikes.WebAPI.I03.Areas.API.V1.Controllers"};

      }
    }

and


    public class API_V2AreaRegistration : AreaRegistration 
    {
      public override string AreaName {
        get {
          return "API.V2";
        }
      }

      public override void RegisterArea(AreaRegistrationContext context) 
      {

      Route r;

      r = context.Routes.MapHttpRoute(
        "API.V2_default",
        "api/v2/{controller}/{id}",
        new {area = AreaName, id = RouteParameter.Optional },
        null
      );
      if (r.DataTokens == null)
      {
        r.DataTokens = new RouteValueDictionary();
      }
      r.DataTokens["Namespaces"] = new string[] { "XAct.Spikes.WebAPI.I03.Areas.API.V2.Controllers" };

      
      //In the latest api area, do the same, but for a route that has no Version defined.
      r = context.Routes.MapHttpRoute(
        "API.V2_defaultXXX",
        "api/{controller}/{id}",
        new {area = AreaName, id = RouteParameter.Optional },
        null
      );

      if (r.DataTokens == null)
      {
        r.DataTokens = new RouteValueDictionary();
      }
      r.DataTokens["Namespaces"] = new string[] { "XAct.Spikes.WebAPI.I03.Areas.API.V2.Controllers" };
      }
    }

Notice: