AttributeRouting
AttributeRouting copied to clipboard
Support for versioning (#91)
Works as described in issue #91.
Note for viewing the diff. 7b9396a and 48c9341 are my upstream merges, they do not have any actual effect.
I cannot figure out why git thinks RouteReflector is entirely changed, the actual diff is:
--- RouteReflector.cs.orig Mon Jul 23 16:34:22 2012
+++ RouteReflector.cs Mon Jul 23 16:22:38 2012
@@ -46,6 +46,7 @@
let convention = controllerType.GetCustomAttribute<RouteConventionAttributeBase>(false)
let routeAreaAttribute = controllerType.GetCustomAttribute<RouteAreaAttribute>(true)
let routePrefixAttribute = controllerType.GetCustomAttribute<RoutePrefixAttribute>(true)
+ let routeVersionedAttribute = controllerType.GetCustomAttribute<RouteVersionedAttribute>(true)
from actionMethod in controllerType.GetActionMethods(inheritActionsFromBaseController)
from routeAttribute in GetRouteAttributes(actionMethod, convention)
// precedence is within a controller
@@ -73,7 +74,10 @@
IsAbsoluteUrl = routeAttribute.IsAbsoluteUrl,
UseLowercaseRoute = routeAttribute.UseLowercaseRouteFlag,
PreserveCaseForUrlParameters = routeAttribute.PreserveCaseForUrlParametersFlag,
- AppendTrailingSlash = routeAttribute.AppendTrailingSlashFlag
+ AppendTrailingSlash = routeAttribute.AppendTrailingSlashFlag,
+ IsVersioned = routeVersionedAttribute != null && routeVersionedAttribute.IsVersioned,
+ MinVersion = routeAttribute.MinVersion ?? (routeVersionedAttribute != null ? routeVersionedAttribute.MinVersion : null),
+ MaxVersion = routeAttribute.MaxVersion ?? (routeVersionedAttribute != null ? routeVersionedAttribute.MaxVersion : null)
}).ToList();
}
Hi Greg. Thanks for this. I'll be checking it out in the next few days.
@mccalltd Now updated to current code as I'm starting to use this in another project. FWIW I have now been using the version in my original patch for about a year (and several releases) and have done several API changes with it, and this allows all the old versions to continue functioning with identical surface area.
As a somewhat more concrete example of what we've been doing, let's say we have a model called UserPreferences {pageSize:10, timezone:-5, confirmOnSave:false}
and it is stored as a table with those same column names. Our controller contains:
public class UsersController : ApiController
{
[GET("users/{id}/preferences"), HttpGet]
public Models.UserPreferences UserPrefs(Guid id) { .. }
In 1.2, we switch to a key-value table, and the new UserPreferences model is now more like a dictionary {prefs: [ {key:"pageSize", value:10}, {key:"timezone":-5} .... ] }
(btw, I am not saying this particular change is good, just trying to make a simple example of a breaking change).
What we do in this situation is copy the old Models.UserPreferences into Models.v1_1.UserPreferences, and add the MinVer to the controller action:
[GET("users/{id}/preferences", MinVer="1.2"), HttpGet]
public Models.UserPreferences UserPrefs(Guid id) { .. }
Then we make a new controller:
namespace MyProject.Controllers.v1_1
public class UsersController : ApiController
{
[GET("users/{id}/preferences", MaxVer="1.1"), HttpGet]
public Models.v1_1.UserPreferences UserPrefs(Guid id) {
// call new controller
var prefs = (new Controllers.UsersController).UserPrefs(id);
// map to old model (AutoMapper can come in handy here, but this is just an example)
return new Models.v1_1.UserPreferences() {
PageSize = prefs.First(x => x.Key == "PageSize").Value,
Timezone = prefs.First(x => x.Key == "Timezone").Value,
};
}
}
}
In this way, anyone calling the 1.0 or 1.1 APIs can continue to do so, and after this initial setup it's basically no extra work to maintain, yet we can still completely change the underlying storage model as well as fix bugs. We could also even add new properties to the old model (since that is a non-breaking change).
What do your final url requests look like (for new and old version calls)?