If you need API Versioning in ASP.NET Web API, the Asp.Versioning.WebApi (formerly Microsoft.AspNet.WebApi.Versioning) is the most straight forward and robust option. You can version your API any way that you like including:
- By query string
- By header
- By URL segment
- By media type
- Custom
- Any combination of the above methods
Convention-based Routing, and even ASP.NET Web API itself, were not designed to support multiple controllers with the same name. This is actually necessary for proper collation. Disambiguation comes from API versions, not controller names. To address this issue, you have several options:
- Use the same name, but different namespaces
V1.ProductsController vs V2.ProductsController
- There is a provided convention for deriving API version from namespace
- Use the provided
ControllerNameAttribute
- ex:
[ControllerName("Products")]
- Rely on the build-in convention:
<name><version>Controller
- This is defined by
IControllerNameConvention
- Replace the default
IControllerNameConvention with one of the 3 built-in methods or roll your own.
Using the OP, the entire solution is:
[ApiVersion(1.0)]
public class ProductsController : ApiController
{
public IHttpActionResult Get(string id = default) =>
Ok(new { message = "From V1" });
}
[ApiVersion(2.0)]
public class Products2Controller : ApiController
{
public IHttpActionResult Get(string id = default) =>
Ok(new { message = "From V2" });
}
Products2Controller satisfies the naming convention so it will have the controller name Products and be collated with ProductsController. ApiVersion is just metadata. Using attributes tends to be the most common way to apply version metadata, but they can also be applied by convention.
public void Configuration(HttpConfiguration configuration)
{
// reporting api versions will return the headers
// "api-supported-versions" and "api-deprecated-versions"
configuration.AddApiVersioning(options => options.ReportApiVersions = true);
// the api version can't be just 'anything'. the ApiVersionRouteConstraint
// ensures the value well-formed long before any other processing. this
// also lets you control where in the template the api version resides.
// there is no 'magic string' parsing or regex. you even define if there
// should be a leading 'v'
configuration.Routes.MapHttpRoute(
name: "api",
routeTemplate: "tables/v{apiVersion}/{controller}/{id}",
defaults: new { id = RouteParameter.Optional },
constraints: new { apiVersion = new ApiVersionRouteConstraint() } );
}