I'm trying to implement both Attribute Routing and the VersionedRoute
from RoutingConstaints Sample but when I use both on a controller, the versioned attribute no longer works.
What would I need to modify on the attribute to get it to play nice with Attribute Routing?
For code example download the sample project (or just look at the few files from the above link) and then modify the routes as such:
// When I use the RoutePrefix, VersionedRoute no longer works (Sending "Api-Version" through http header doesn't route correctly
// If I remove the RoutePrefix I can use VersionedRoute again
// What do I need to change in its code to be able to use both?
[VersionedRoute("api/Customers", 1)] // This route would be used as http://url/api/customers with a header of "api-version: 1"
[RoutePrefix("api/v1/Customers")] // This route would be used purely through url versioning of http://url/api/v1/Customers
public class CustomersV1Controller : ApiController {
/* Other stuff removed */
[VersionedRoute("api/Customer", 1)] // I'd rather not have to use this here at all and just use a single one on the class, but having both nor just one on either works right now.
[Route("")]
public IHttpActionResult Get()
{
return Json(_customers);
}
}
VersionedRoute Code
VersionConstraint Code
Edit: Please let me know if you need more information or even post ideas or things to try :)
Edit2: Here is an example of what I'm trying to do from Troy Hunt's Blog: http://www.troyhunt.com/2014/02/your-api-versioning-is-wrong-which-is.html
Edit3: Here is what I'd like to code to be as close to since it would reduce a lot of the overhead and magic strings.
[VersionedRoute("api/Customers", 1)] // This route would be used as http://url/api/customers with a header of "api-version: 1"
[RoutePrefix("api/v1/Customers")] // This route would be used purely through url versioning of http://url/api/v1/Customers
public class CustomersV1Controller : ApiController {
/* Other stuff removed */
[Route("")]
public IHttpActionResult Get()
{
// Removed
return Ok(customers);
}
[Route("{id:int}")]
public IHttpActionResult GetById(int id)
{
// Removed
return Ok(customer);
}
}
[VersionedRoute("api/Customers", 2)] // This route would be used as http://url/api/customers with a header of "api-version: 2"
[RoutePrefix("api/v2/Customers")] // This route would be used purely through url versioning of http://url/api/v2/Customers
public class CustomersV2Controller : ApiController {
/* Other stuff removed */
[Route("")]
public IHttpActionResult Get()
{
// Removed
return Ok(customersThatAreDifferentThanV1);
}
[Route("{id:int}")]
public IHttpActionResult GetById(int id)
{
// Removed
return Ok(customerThatIsDifferent);
}
}
Edit: Last bump, trying to only have to write the route version information once per route, at the controller attribute level and not per-action.
To enable attribute routing, call MapHttpAttributeRoutes during configuration. This extension method is defined in the System. Web. Http.
Web API 2 supports a new type of routing, called attribute routing. As the name implies, attribute routing uses attributes to define routes. Attribute routing gives you more control over the URIs in your web API.
Web API Versioning is required as the business grows and business requirement changes with the time. As Web API can be consumed by multiple clients at a time, Versioning of Web API will be necessarily required so that Business changes in the API will not impact the client that are using/consuming the existing API.
You can version your Web API in one of the following ways: Use URLs: Version information is specified in the URL as a query string. Use Custom Request Headers: Version information for your controller is specified in the request header sans the need for any changes in the URL.
The Route
and VersionedRoute
attributes are working fine together, but your RoutePrefix
attribute is also applied to your VersionedRoute
(try accessing /api/v1/Customers/api/Customer - you'll get a response when the api-version header is set)
The following code would produce the desired behaviour with regards to the two URLs in your example returning the correct responses, but obviously this does not solve your problem of wanting one VersionedRoute
and one RoutePrefix
at the top of the class. Another approach would be needed for this. You can, however, have separate controllers for different api versions.
[RoutePrefix("api")]
public class CustomersV1Controller : ApiController
{
/* Other stuff removed */
[VersionedRoute("Customers", 1)]
[Route("v1/Customers")]
public IHttpActionResult Get()
{
return Json(_customers);
}
}
An improvement would be to create your own attribute instead of Route
so you wouldn't need to prefix the version every time:
public class CustomVersionedRoute : Attribute, IHttpRouteInfoProvider
{
private readonly string _template;
public CustomVersionedRoute(string route, int version)
{
_template = string.Format("v{0}/{1}", version, route);
}
public string Name { get { return _template; } }
public string Template { get { return _template ; } }
public int Order { get; set; }
}
[RoutePrefix("api")]
public class CustomersV2Controller : ApiController
{
/* Other stuff removed */
[VersionedRoute("Customers", 2)]
[CustomVersionedRoute("Customers", 2)]
public IHttpActionResult Get()
{
return Json(_customers);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With