Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

REST Versioning Architecture with ASP.NET MVC Web API v1.2

359 views

Published on

  • Login to see the comments

  • Be the first to like this

REST Versioning Architecture with ASP.NET MVC Web API v1.2

  1. 1. REST API Versioning Techniques Arquitecto de Soluciones de Software Rodrigo Liberoff with ASP.NET Web API
  2. 2. “Si al principio no tienes éxito, llámalo versión 1.0.” ― Anónimo - Desconocido
  3. 3. Introducción
  4. 4. Google Youtube Facebook Wikipedia TwitterLinkedIn Wordpress Yahoo El Mundo
  5. 5. El desarrollo de Web APIs está explotando…
  6. 6. …y las compañías están exigiéndolo.
  7. 7. Los creadores de HTTP pensaron mucho sobre esto y sobre cómo diseñar con miras a la evolución.
  8. 8. La pregunta no es “si evoluciona”... …la pregunta es “cómo evolucionará”.
  9. 9. Conceptos
  10. 10. ¿QUÉES…?
  11. 11. ¿QUÉES…?
  12. 12. ¿QUÉES…?
  13. 13. ¿QUÉES…?
  14. 14. ¿QUÉES…?
  15. 15. Ejemplo
  16. 16. Ejemplo
  17. 17. ¿QUÉSON…?
  18. 18. Ejemplos
  19. 19. ¿QUÉES…?
  20. 20. http://geek-and-poke.com/geekandpoke/2013/6/14/insulting-made-easy
  21. 21. ¿QUÉES…?
  22. 22. ¿QUÉES…?
  23. 23. Versionando un Web API
  24. 24. En general, se considera que la combinación de descubrimiento de recursos en tiempo de ejecución, mensajes auto-descriptivos y clientes reactivos son críticos para conseguir un Web API evolucionable. También se considera que, aunque son conceptos difíciles de entender e implementar, la flexibilidad y escalabilidad que aportan supera con creces su costo.
  25. 25. En general, en la filosofía REST, versionar recursos se considera una mala práctica o evidencia de un pobre diseño.
  26. 26. TIPO DESCRIPCIÓN Payload La versión es parte del mensaje. Query String La versión es un parámetro que forma parte de la URI del recurso. URL Suffix La versión es parte del URI, y se anexa al final del mismo. Custom Header La versión es suministrada en una cabecera propia no estándar. URL La versión es parte indirecta del URI del recurso. Media Type Content Type La versión es suministrada como el media type esperado de la representación del recurso. TIPO DESCRIPCIÓN Payload La versión es parte del mensaje. Query String La versión es un parámetro que forma parte de la URI del recurso. URL Suffix La versión es parte del URI, y se anexa al final del mismo.
  27. 27. Ejemplos <xml> <version>2</version> ... </xml> http://MyCoolSite/api/recurso?v=2 http://MyCoolSite/api/recurso?v=1_1 http://MyCoolSite/api/recurso.v2 http://MyCoolSite/api/recurso.v1_1
  28. 28. Ejemplos GET http://MyCoolSite/api/recurso HTTP/1.1 ... api-version: 2 ... http://MyCoolSite/api/v2/recurso http://MyCoolSite/api/v1.1/recurso GET http://MyCoolSite/api/recurso HTTP/1.1 ... Accept: application/vnd.company.site.api.resource.v2+json ...
  29. 29. Ejemplos www.facebook.com/v2.7/dialog/oauth www.facebook.com/v2.7/plugins/ graph.facebook.com/v2.7/
  30. 30. Ejemplos https://api.twitter.com/1.1/statuses/user_timeline.json https://api.twitter.com/1.1/direct_messages/destroy.json https://api.twitter.com/1.1/users/report_spam.json
  31. 31. Ejemplos Accept: application/vnd.github.v3+json Accept: application/vnd.github.loki-preview+json
  32. 32. Implementación
  33. 33. Version del Controlador por Nombre public class DummyController : ApiController { ... } public class DummyV2Controller : ApiController { ... } public class V2DummyController : ApiController { ... } public class DummyControllerV2 : ApiController { ... } public class DummyControllerV2 : ApiController { // Este controlador no es válido, porque no cumple con la convención de ASP.NET MVC y // ASP.NET Web API sobre nombres para controladores. ... }
  34. 34. Versión de Controlador por Espacio de Nombres namespace My.Cool.Api.V1 { public class DummyController : ApiController { ... } } namespace My.Cool.Api.V2 { public class DummyController : ApiController { ... } }
  35. 35. Código para los Ejemplos public class Console { public int Id { get; set; } public string Name { get; set; } } public class NexGenConsole : Console { public bool IsNexGen { get; set; } }
  36. 36. Código para los Ejemplos public class ConsoleController : ApiController { public virtual IEnumerable<Console> GetConsoles { get { return ... } } public virtual Console GetConsole(int id) { return new Console { Id = id, Name = @"PS3" }; } } public class ConsoleV2Controller : ConsoleController { public override Console GetConsole(int id) { return new NexGenConsole { Id = id, Name = @"PS4", IsNexGen = true }; } }
  37. 37. Código para los Ejemplos internal static class VersionFinder { public static int GetVersionFromRequestData(HttpRequestMessage request) {...} private static bool NeedsUriVersioning(HttpRequestMessage request, out string version) { ... } private static bool NeedsHeaderVersioning(HttpRequestMessage request, out string version) { ... } private static bool NeedsAcceptVersioning(HttpRequestMessage request, out string version) { ... } private static int VersionToInt(string versionString) { ... } }
  38. 38. Versionado a través de Enrutamiento por Convención
  39. 39. Versionado a través de Enrutamiento por Convención
  40. 40. Versionado a través de Enrutamiento por Convención
  41. 41. Versionado a través de Enrutamiento por Convención
  42. 42. Versionado a través de Enrutamiento por Convención
  43. 43. Versionado a través de Enrutamiento por Convención
  44. 44. Versionado a través de Enrutamiento por Convención
  45. 45. Versionado a través de Enrutamiento por Convención public static class WebApiConfig { public static void Register(HttpConfiguration config, IDependencyResolver dependencyResolver) { if (config != null) { ... config.Services.Replace(typeof(IHttpControllerSelector), new VersionAwareControllerSelector(config)); ... } } }
  46. 46. Versionado a través de Enrutamiento por Convención public static class WebApiConfig { public static void Register(HttpConfiguration config, IDependencyResolver dependencyResolver) { if (config != null) { ... config.Routes.MapHttpRoute(name: @"VersionedApi", routeTemplate: @"api/v{version}/{controller}/{id}", defaults: new { id = RouteParameter.Optional }, constraints: new { version = @"[1-9]+[d]*" }); config.Routes.MapHttpRoute(name: @"DefaultApi", routeTemplate: @"api/{controller}/{id}", defaults: new { id = RouteParameter.Optional }); ... } } }
  47. 47. DEMO 1
  48. 48. Versionado a través de Enrutamiento por Atributos …Sino que se
  49. 49. Versionado a través de Enrutamiento por Atributos Uno de los primeros pasos es decorar los controladores con los atributos de enrutamiento apropiados. [RoutePrefix(@"api/v1")] public class ConsoleAttributeController : ApiController { [Route(@"consoles")] public virtual IEnumerable<Console> Console RetrieveConsoles() { return ... } [Route(@"consoles/{id:int}")] public virtual Console RetrieveConsole(int id) { return new Console { Id = id, Name = @"PS3" }; } }
  50. 50. Versionado a través de Enrutamiento por Atributos [RoutePrefix(@"api/v2")] public class ConsoleAttributeV2Controller : ConsoleAttributeController { [Route(@"consoles/{id:int}")] public override Console RetrieveConsole(int id) { return new NexGenConsole { Id = id, Name = @"PS4", IsNexGen = true }; } } Y ya sólo con esto, se tendría versionado mediante URL, ya que la declaración de la ruta directamente en el controlador o la acción, es la naturaleza misma del enrutamiento por atributo.
  51. 51. DEMO 2
  52. 52. Versionado a través de Enrutamiento por Atributos Soportar las modalidades de custom header y custom media type, simplemente requiere identificar el origen del dato correspondiente a la versión.
  53. 53. Versionado a través de Enrutamiento por Atributos Sin embargo, es necesario informar a los atributos de enrutamiento de la existencia de una restricción por versión, para lo cual:
  54. 54. Versionado a través de Enrutamiento por Atributos internal class VersionConstraint : IHttpRouteConstraint { private readonly int allowedVersion; public VersionConstraint(int version) { this.allowedVersion = version; } public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary<string, object> values, HttpRouteDirection routeDirection) { return request == null ? false : this.allowedVersion == VersionFinder.GetVersionFromRequestData(request); } }
  55. 55. Versionado a través de Enrutamiento por Atributos internal class VersionedRouteAttribute : RouteFactoryAttribute { public VersionedRouteAttribute(string template) : base(template) { this.Order = -1; } public int Version { get; set; } public override IDictionary<string, object> Constraints { get { return new HttpRouteValueDictionary { { string.Empty, new VersionConstraint(this.Version) } }; } } }
  56. 56. Versionado a través de Enrutamiento por Atributos Finalmente, decoramos los controladores con el nuevo atributo VersionedRouteAttribute. [RoutePrefix(@"api")] public class ConsoleAttributeController : ApiController { [HttpGet, Route(@“v1/consoles")] [VersionedRoute(@"consoles", Version = 1)] public virtual IEnumerable<Console> Console Get() { return ... } [HttpGet, Route(@“v1/consoles/{id:int}")] [VersionedRoute(@“consoles/{id:int}", Version = 1)] public virtual Console Get(int id) { return new Console { Id = id, Name = @"PS3" }; } }
  57. 57. Versionado a través de Enrutamiento por Atributos [RoutePrefix(@"api")] public class ConsoleAttributeV2Controller : ConsoleAttributeController { [HttpGet, Route(@"v2/consoles")] [VersionedRoute(@"consoles", Version = 2)] public override IEnumerable<Console> RetrieveConsoles() { return base.RetrieveConsoles(); } [HttpGet, Route(@"v2/consoles/{id:int}")] [VersionedRoute(@"consoles/{id:int}", Version = 2)] public override Console Get(int id) { return new NexGenConsole { Id = id, Name = @"PS4", IsNexGen = true }; } } Y ahora se cuenta con versionado por URL, plus versionado por custom header y custom media type.
  58. 58. DEMO 3
  59. 59. RECOMENDACIONES Y BUENAS PRÁCTICAS
  60. 60. Encontraremos diferentes posiciones filosóficas sobre la “manera correcta” de alcanzar REST, sobre qué es RESTful, y qué no lo es. Desafortunadamente, lo filosófico pasa a lo religioso y se pierde el foco sobre cuál debería ser el objetivo real: construir software que funcione y un API coherente que permita consumirle e integrarle fácilmente.
  61. 61. http://shonzilla/api/customers/1234 http://shonzilla/api/v3.0/customers/1234 http://shonzilla/api/v3/customers/1234
  62. 62. Literatura y Bibliografía
  63. 63. Preguntas
  64. 64. ¡GRACIAS! @rliberoff https://es.linkedin.com/in/rliberoff http://www.commitstrip.com/en/2013/08/20/numeros-de-version/

×