Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for versioning (#91) #93

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open

Conversation

gregmac
Copy link
Contributor

@gregmac gregmac commented Jul 23, 2012

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

… delegate to avoid triggering exception on property access
Conflicts:
	src/AttributeRouting/Extensions/HttpRequestBaseExtensions.cs
Initial support for versioning, as described on
mccalltd#91
New AddVersions() method to make configuration of versioning (mccalltd#91)
simpler.
@mccalltd
Copy link
Owner

mccalltd commented Aug 2, 2012

Hi Greg. Thanks for this. I'll be checking it out in the next few days.

Conflicts:
	src/AttributeRouting.Web.Http/Framework/HttpAttributeRoute.cs
	src/AttributeRouting.Web.Http/HttpRouteAttribute.cs
	src/AttributeRouting.Web.Mvc/Framework/AttributeRoute.cs
	src/AttributeRouting/AttributeRouting.csproj
	src/AttributeRouting/ConfigurationBase.cs
	src/AttributeRouting/Framework/IAttributeRoute.cs
	src/AttributeRouting/Framework/RouteBuilder.cs
	src/AttributeRouting/Framework/RouteReflector.cs
	src/AttributeRouting/Framework/RouteSpecification.cs
	src/AttributeRouting/IRouteAttribute.cs
… names), and add example of different controller actions for different versions of the same URL
@gregmac
Copy link
Contributor Author

gregmac commented Jan 16, 2013

@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).

@waynebrantley
Copy link

What do your final url requests look like (for new and old version calls)?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants