Routing in ASP NET Web API - For different version

2019-03-16 09:28发布

问题:

I am reading about Attribute Routing in Web API 2 from here

The article says,

Here are some other patterns that attribute routing makes easy.

API versioning

In this example, “/api/v1/products” would be routed to a different controller than “/api/v2/products”.

/api/v1/products
/api/v2/products

How come?

EDIT: I would do this in Normal Routing:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
         config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/v2/products",
            defaults: new { controller = V2_Products }
        );

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

Could anyone explain me how to do this in Attribute Routing way ? And how come using Attribute routing is easier and convenient for this example (according to the article) ?

回答1:

There are many ways to implement versionning with attribute routing ; A really basic way is to use RoutePrefix attribute for each version of your ApiController

[RoutePrefix("v1")]
public class V1_ProductsController : ApiController
{
    [Route("products")]
    public IEnumerable<string> Get()
    {
        return new string[] { "v1-product1", "v1-product2" };
    }
     //....
}


[RoutePrefix("v2")]
public class V2_ProductsController : ApiController
{
     [Route("products")]
    public IEnumerable<string> Get()
    {
        return new string[] { "v2-product1", "v2-product2" };
    }
    //....
}

/v1/products goes to the first version of /v2/products goes to the second one.



回答2:

You can do it by overriding DefaultHttpControllerSelector

there you override method to selectcontroller

public override HttpControllerDescriptor SelectController(HttpRequestMessage request)
            {
                HttpControllerDescriptor controllerDescriptor = null;
                IDictionary<string, HttpControllerDescriptor> controllers = GetControllerMapping();

                IHttpRouteData routeData = request.GetRouteData();

                if (routeData == null)
                {
                    throw new HttpResponseException(HttpStatusCode.NotFound);
                }

                object apiVersion;
                if (!routeData.Values.TryGetValue("Version", out apiVersion))
                {
                    apiVersion = "1";
                }


                object controllerName;
                if (!routeData.Values.TryGetValue("controller", out controllerName))
                {
                    controllerName = string.Empty;
                }
                if (controllerName == null)
                {
                    throw new HttpResponseException(HttpStatusCode.NotFound);
                }

                string newControllerName = String.Concat(controllerName.ToString(), "V", apiVersion);

                if (controllers.TryGetValue(newControllerName, out controllerDescriptor))
                {
                    return controllerDescriptor;
                }
                if (controllers.TryGetValue(controllerName.ToString(), out controllerDescriptor))
                {
                    return controllerDescriptor;
                }
                throw new HttpResponseException(HttpStatusCode.NotFound);

            }

Then you are adding routes webapiconfig

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

and register controller selector in webapiconfig

config.Services.Replace(typeof(IHttpControllerSelector), new ApiVersioningSelector(config));

So from now if you name controller ProductsV1Controller it will reffer /api/v1/products. Also please note that my example also support routes without version so if v1 is not found it will try to check if ProductsController exists

PS. Code is update one bug was there :(



回答3:

Another simple way is configuring your route as api/{folder}/{controller}/{action} where in to folder you can give name as V1 or V2.

A good way can be implementing your own Controller selector. You can use this link for more information.

The interface that Web API uses to select a controller is IHttpControllerSelector. The important method on this interface is SelectController, which selects a controller for a given HttpRequestMessage.