简体   繁体   中英

Web API versioning URL

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 }
        );
    }
}

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;
    }

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM