4

I'm having a bit of a problem trying to version my Web API:

[ApiVersion("1.0")]
[ApiVersion("2.0")]
[System.Web.Http.Route("api/v{version:apiVersion}/monitors")]
[ControllerName("Monitors")]
public sealed class MonitorsController : ApiController
{
   /* 
     /monitors/get?heartbeat=foo
   */
   [System.Web.Http.Route("api/v{version:apiVersion}/monitors/get")]
   [MapToApiVersion("1.0")]
   public JsonResult GetHeartbeatStatusV1(string heartbeat)
   {
      var x = new JsonResult {Data = "heartbeat v1"};
      return x;
   }
   [System.Web.Http.Route("api/v{version:apiVersion}/monitors/get")]
   [MapToApiVersion("2.0")]
   public JsonResult GetHeartbeatStatusV2(string heartbeat)
   {
      var x = new JsonResult { Data = "heartbeat v1" };
      return x;
   }
   /* 
      /monitors/get?alert=foo 
   */
   [System.Web.Http.Route("api/v{version:apiVersion}/monitors/get")]
   [MapToApiVersion("2.0")]
   public JsonResult GetAlertStatus(string alert)
   {
      var x = new JsonResult {Data = "alerts"};
      return x;
   }
   /* 
      /monitors/get?oDataQuery=foo 
   */
   [System.Web.Http.Route("monitors/get")]
   public JsonResult GetODataQuery(string oDataQuery)
   {
      var x = new JsonResult {Data = "oDataQuery"};
      return x;
   }
}

The above is my controller. I have a bunch of actions which I want to be able to call based on version.

The problem is I also need them all to be /get then decide on which method to call based on the parameter name, this is why I have:

[System.Web.Http.Route("api/v{version:apiVersion}/monitors/get")]

I thought I could decorate the method with [MapToApiVersion("1.0")] and [MapToApiVersion("2.0")] to discriminate which method is called. However in the exmaple posted if I call api/v1.0/monitors/get?heartbeat=foo I get an error about multiple actions. If I call v2.0 it says it can't find any method at all.

?

I'd like to be able to do

http://foo:blah/api/v1.0/monitors/get?heartbeat=foo

http://foo:blah/api/v2.0/monitors/get?heartbeat=foo

http://foo:blah/api/v2.0/monitors/get?alert=foo

Is this possible?

EDIT


One thing I tried was splitting them up into 2 different controllers, in different namespaces.

[ApiVersion("2.0")]
[System.Web.Http.Route("api/v{version:apiVersion}/monitors")]
[ControllerName("Monitors")]
public sealed class MonitorsController : ApiController
{
    /* 
        /monitors/get?heartbeat=foo
    */
    [System.Web.Http.Route("api/v{version:apiVersion}/monitors/get")]
    [MapToApiVersion("2.0")]
    public JsonResult GetHeartbeatStatusV2(string heartbeat)
    {
        var x = new JsonResult {Data = "heartbeat v2"};
        return x;
    }
    /* 
        /monitors/get?alert=foo 
    */
    [System.Web.Http.Route("api/v{version:apiVersion}/monitors/get")]
    public JsonResult GetAlertStatus(string alert)
    {
        var x = new JsonResult {Data = "alerts"};
        return x;
    }

}

This has the same error of finding multiple actions.

My WebApiConfig.cs looks like this:

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 constraintResolver = new DefaultInlineConstraintResolver
                                 {
                                     ConstraintMap =
                                     {
                                         ["apiVersion"] = typeof(ApiVersionRouteConstraint)
                                     }
                                 };
        config.MapHttpAttributeRoutes(constraintResolver);
        config.AddApiVersioning();
        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/v{version:apiVersion}/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
    }
}
0

1 Answer 1

3

The way you are doing it is right but instead of using the ApiVersion attribute you can make it more simple and readable Just let MapHttpRoute as default and you can let your controller in the same namespace and just change the name MonitorsControllerV2

[RoutePrefix("api/V2.0/monitors")] 
public sealed class MonitorsControllerV2 : ApiController
{
    /* 
        /monitors/get?heartbeat=foo
    */

     [Route("GetHeartbeat")]
    public JsonResult GetHeartbeatStatusV2(string heartbeat)
    {
        var x = new JsonResult {Data = "heartbeat v2"};
        return x;
    }
    /* 
        /monitors/get?alert=foo 
    */
      [Route(" GetAlertStatus")]
    public JsonResult GetAlertStatus(string alert)
    {
        var x = new JsonResult {Data = "alerts"};
        return x;
    }

}
Sign up to request clarification or add additional context in comments.

2 Comments

I tried this, when I go to http://localhost:53713/api/v2.0/monitors/Getheartbeat?heartbeat=555 I now get an error that a version is was required but not specified.
Ok, got it working by changing [RoutePrefix("api/V2.0/monitors")] to ` [RoutePrefix("api/v{version:apiVersion}/monitors")]

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.