SlideShare una empresa de Scribd logo
1 de 96
REST API Versioning Techniques
Arquitecto de Soluciones de Software
Rodrigo Liberoff
with ASP.NET Web API
“Si al principio no tienes
éxito, llámalo versión 1.0.”
― Anónimo - Desconocido
Introducción
Google
Youtube
Facebook
Wikipedia
TwitterLinkedIn
Wordpress
Yahoo
El Mundo
El desarrollo de Web APIs está explotando…
…y las compañías están exigiéndolo.
Los creadores de HTTP pensaron mucho sobre esto y
sobre cómo diseñar con miras a la evolución.
La pregunta no es “si evoluciona”...
…la pregunta es “cómo evolucionará”.
Conceptos
¿QUÉES…?
¿QUÉES…?
¿QUÉES…?
¿QUÉES…?
¿QUÉES…?
Ejemplo
Ejemplo
¿QUÉSON…?
Ejemplos
¿QUÉES…?
http://geek-and-poke.com/geekandpoke/2013/6/14/insulting-made-easy
¿QUÉES…?
¿QUÉES…?
Versionando un Web API
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.
En general, en la filosofía REST, versionar recursos se
considera una mala práctica o evidencia de un pobre
diseño.
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.
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
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
...
Ejemplos
www.facebook.com/v2.7/dialog/oauth
www.facebook.com/v2.7/plugins/
graph.facebook.com/v2.7/
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
Ejemplos
Accept: application/vnd.github.v3+json
Accept: application/vnd.github.loki-preview+json
Implementación
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.
...
}
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
{
...
}
}
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; }
}
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 };
}
}
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) { ... }
}
Versionado a través de Enrutamiento por Convención
Versionado a través de Enrutamiento por Convención
Versionado a través de Enrutamiento por Convención
Versionado a través de Enrutamiento por Convención
Versionado a través de Enrutamiento por Convención
Versionado a través de Enrutamiento por Convención
Versionado a través de Enrutamiento por Convención
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));
...
}
}
}
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 });
...
}
}
}
DEMO 1
Versionado a través de Enrutamiento por Atributos
…Sino que se
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" };
}
}
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.
DEMO 2
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.
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:
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);
}
}
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) }
};
}
}
}
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" };
}
}
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.
DEMO 3
RECOMENDACIONES
Y
BUENAS PRÁCTICAS
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.
http://shonzilla/api/customers/1234
http://shonzilla/api/v3.0/customers/1234
http://shonzilla/api/v3/customers/1234
Literatura y Bibliografía
Preguntas
¡GRACIAS!
@rliberoff
https://es.linkedin.com/in/rliberoff
http://www.commitstrip.com/en/2013/08/20/numeros-de-version/

Más contenido relacionado

La actualidad más candente

Workshop spring session 2 - La persistance au sein des applications Java
Workshop spring   session 2 - La persistance au sein des applications JavaWorkshop spring   session 2 - La persistance au sein des applications Java
Workshop spring session 2 - La persistance au sein des applications Java
Antoine Rey
 
Android notification
Android notificationAndroid notification
Android notification
Krazy Koder
 

La actualidad más candente (20)

ReactJS presentation.pptx
ReactJS presentation.pptxReactJS presentation.pptx
ReactJS presentation.pptx
 
Cours design pattern m youssfi partie 1 introduction et pattern strategy
Cours design pattern m youssfi partie 1 introduction et pattern strategyCours design pattern m youssfi partie 1 introduction et pattern strategy
Cours design pattern m youssfi partie 1 introduction et pattern strategy
 
Android Internals
Android InternalsAndroid Internals
Android Internals
 
An Introduction to ReactJS
An Introduction to ReactJSAn Introduction to ReactJS
An Introduction to ReactJS
 
React Native
React NativeReact Native
React Native
 
Spring Framework Petclinic sample application
Spring Framework Petclinic sample applicationSpring Framework Petclinic sample application
Spring Framework Petclinic sample application
 
Workshop spring session 2 - La persistance au sein des applications Java
Workshop spring   session 2 - La persistance au sein des applications JavaWorkshop spring   session 2 - La persistance au sein des applications Java
Workshop spring session 2 - La persistance au sein des applications Java
 
Theses Soutenues sous Direction et Co-Direction du Pr YOUSSFI
Theses Soutenues sous Direction et Co-Direction du Pr YOUSSFITheses Soutenues sous Direction et Co-Direction du Pr YOUSSFI
Theses Soutenues sous Direction et Co-Direction du Pr YOUSSFI
 
Arquitectura REST
Arquitectura RESTArquitectura REST
Arquitectura REST
 
Angular Directives
Angular DirectivesAngular Directives
Angular Directives
 
React js
React jsReact js
React js
 
Intro to React
Intro to ReactIntro to React
Intro to React
 
React Context API
React Context APIReact Context API
React Context API
 
Support JEE Servlet Jsp MVC M.Youssfi
Support JEE Servlet Jsp MVC M.YoussfiSupport JEE Servlet Jsp MVC M.Youssfi
Support JEE Servlet Jsp MVC M.Youssfi
 
Hibernate in Action
Hibernate in ActionHibernate in Action
Hibernate in Action
 
Broadcast Receiver
Broadcast ReceiverBroadcast Receiver
Broadcast Receiver
 
Cours design pattern m youssfi partie 3 decorateur
Cours design pattern m youssfi partie 3 decorateurCours design pattern m youssfi partie 3 decorateur
Cours design pattern m youssfi partie 3 decorateur
 
Tomcat
TomcatTomcat
Tomcat
 
Android notification
Android notificationAndroid notification
Android notification
 
Nestjs MasterClass Slides
Nestjs MasterClass SlidesNestjs MasterClass Slides
Nestjs MasterClass Slides
 

Destacado (6)

Despliegue de aplicaciones en Azure Web Apps
Despliegue de aplicaciones en Azure Web AppsDespliegue de aplicaciones en Azure Web Apps
Despliegue de aplicaciones en Azure Web Apps
 
¿Qué es un hackathon y por qué deberías asistir?
¿Qué es un hackathon y por qué deberías asistir?¿Qué es un hackathon y por qué deberías asistir?
¿Qué es un hackathon y por qué deberías asistir?
 
Desarrollando un API con REST
Desarrollando un API con RESTDesarrollando un API con REST
Desarrollando un API con REST
 
Construye un backend para tus app móviles con con ASP.NET Web Api
Construye un backend para tus app móviles con con ASP.NET Web ApiConstruye un backend para tus app móviles con con ASP.NET Web Api
Construye un backend para tus app móviles con con ASP.NET Web Api
 
Inventando métricas en Social Media
Inventando métricas en Social MediaInventando métricas en Social Media
Inventando métricas en Social Media
 
Servidor API REST con Node.js
Servidor API REST con Node.jsServidor API REST con Node.js
Servidor API REST con Node.js
 

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

Hands-on Spring 3: The next generation
Hands-on Spring 3: The next generationHands-on Spring 3: The next generation
Hands-on Spring 3: The next generation
Sergi Almar i Graupera
 
Appcircus Academy: Integración de Social Media en Android
Appcircus Academy: Integración de Social Media en AndroidAppcircus Academy: Integración de Social Media en Android
Appcircus Academy: Integración de Social Media en Android
Alberto Ruibal
 

Similar a REST Versioning Architecture with ASP.NET MVC Web API v1.2 (20)

Presentacion #bbmnk
Presentacion #bbmnkPresentacion #bbmnk
Presentacion #bbmnk
 
App engine
App engineApp engine
App engine
 
Servicios web
Servicios webServicios web
Servicios web
 
Aprendiendo AWS Lambda con API Gateway y DynamoDB
Aprendiendo AWS Lambda con API Gateway y DynamoDBAprendiendo AWS Lambda con API Gateway y DynamoDB
Aprendiendo AWS Lambda con API Gateway y DynamoDB
 
Curso de Desarrollo Web de Platzi
Curso de Desarrollo Web de PlatziCurso de Desarrollo Web de Platzi
Curso de Desarrollo Web de Platzi
 
Tecnologías para microservicios
Tecnologías para microserviciosTecnologías para microservicios
Tecnologías para microservicios
 
Hands-on Spring 3: The next generation
Hands-on Spring 3: The next generationHands-on Spring 3: The next generation
Hands-on Spring 3: The next generation
 
Api rest client en Android
Api rest client en AndroidApi rest client en Android
Api rest client en Android
 
Intro a ASP.NET
Intro a ASP.NETIntro a ASP.NET
Intro a ASP.NET
 
Ajax
AjaxAjax
Ajax
 
Charla
CharlaCharla
Charla
 
Desarrollo web con_asp.net 2.1
Desarrollo web con_asp.net 2.1Desarrollo web con_asp.net 2.1
Desarrollo web con_asp.net 2.1
 
App todo gas: WordPress no es solo para web
App todo gas: WordPress no es solo para webApp todo gas: WordPress no es solo para web
App todo gas: WordPress no es solo para web
 
Aplicaciones Web
Aplicaciones WebAplicaciones Web
Aplicaciones Web
 
Desarrollo de Aplicaciones Web 2.0 con GWT
Desarrollo de Aplicaciones Web 2.0 con GWTDesarrollo de Aplicaciones Web 2.0 con GWT
Desarrollo de Aplicaciones Web 2.0 con GWT
 
Servicios web java, php, perl, google
Servicios web java, php, perl, googleServicios web java, php, perl, google
Servicios web java, php, perl, google
 
Desarrollo web
Desarrollo webDesarrollo web
Desarrollo web
 
Gwt III - Avanzado
Gwt III - AvanzadoGwt III - Avanzado
Gwt III - Avanzado
 
Appcircus Academy: Integración de Social Media en Android
Appcircus Academy: Integración de Social Media en AndroidAppcircus Academy: Integración de Social Media en Android
Appcircus Academy: Integración de Social Media en Android
 
WORKSHOP I: Introducción a API REST
WORKSHOP I: Introducción a API RESTWORKSHOP I: Introducción a API REST
WORKSHOP I: Introducción a API REST
 

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

  • 1. REST API Versioning Techniques Arquitecto de Soluciones de Software Rodrigo Liberoff with ASP.NET Web API
  • 2. “Si al principio no tienes éxito, llámalo versión 1.0.” ― Anónimo - Desconocido
  • 4.
  • 6. El desarrollo de Web APIs está explotando…
  • 7. …y las compañías están exigiéndolo.
  • 8.
  • 9. Los creadores de HTTP pensaron mucho sobre esto y sobre cómo diseñar con miras a la evolución.
  • 10.
  • 11. La pregunta no es “si evoluciona”... …la pregunta es “cómo evolucionará”.
  • 12.
  • 13.
  • 25.
  • 26.
  • 27.
  • 28.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38. 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.
  • 39. En general, en la filosofía REST, versionar recursos se considera una mala práctica o evidencia de un pobre diseño.
  • 40.
  • 41. 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.
  • 42.
  • 44. 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 ...
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54. 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. ... }
  • 55. 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 { ... } }
  • 56.
  • 57.
  • 58. 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; } }
  • 59. 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 }; } }
  • 60. 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) { ... } }
  • 61. Versionado a través de Enrutamiento por Convención
  • 62. Versionado a través de Enrutamiento por Convención
  • 63. Versionado a través de Enrutamiento por Convención
  • 64.
  • 65.
  • 66. Versionado a través de Enrutamiento por Convención
  • 67. Versionado a través de Enrutamiento por Convención
  • 68. Versionado a través de Enrutamiento por Convención
  • 69. Versionado a través de Enrutamiento por Convención
  • 70. 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)); ... } } }
  • 71. 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 }); ... } } }
  • 73. Versionado a través de Enrutamiento por Atributos …Sino que se
  • 74. 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" }; } }
  • 75. 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.
  • 77. 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.
  • 78. 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:
  • 79. 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); } }
  • 80. 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) } }; } } }
  • 81. 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" }; } }
  • 82. 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.
  • 85. 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.
  • 86.
  • 87.
  • 88.
  • 89.
  • 92.
  • 93.
  • 94.

Notas del editor

  1. ¿Quién sabe qué es esto? Les presento a INTERNET, tal y como existía en su primera versión en 1977 cuando se llamaba ARPANET. En 1989, Tim Berners-Lee, un científico del CERN, inventó la World Wide Web, un sistema de acceso a los documentos vinculados a través de Internet. El acceso a estos documentos requería un protocolo para acceder (o navegar) hacia y entre ellos. Dicho protocolo se conoce hoy día como el protocolo de transferencia de hipertexto (HTTP). Este protocolo es en el centro de lo que impulsa sitios web y APIs Web.
  2. Este es Internet hoy día… En un principio podemos pensar que es una colección de sitios web, de páginas que podemos visitar, donde ver información útil, donde tenemos acceso a una ventana a la vida – que nos quieran mostrar – los demás. Es mucho más que eso… Todos esos sitios, en su mayoría, nos ofrecen un API para interactuar con ellos. Expanden los límites de su funcionalidad y de lo que ofrecen a través de un API que nos permite explotar aún más sus capacidades. La actual tendencia, en temas de API en Internet, son las Web API, también conocidas como API REST. Piensen en cualquiera de sus sitios web favoritos: Facebook, 9GAG, Twitter… todos exponen alguna forma de API, y actualmente todos son REST.
  3. ¿Saben de qué es la foto? Es de la misión Apollo 13, donde los ingenieros trataban de encontrar la manera de encajar un filtro de aire (CO2) de forma cúbica en un agujero de forma cilíndrica.
  4. El cambio es inevitable. Los cambios en una Web API son inevitables a medida que el conocimiento y la experiencia que se adquiere sobre un sistema y el negocio al que atienden mejora y se incrementa. Gestionar el impacto de estos cambios ciertamente puede ser un reto, sobre todo cuando aparece la amenaza de romper la integración que se tenga con los clientes del Web API.
  5. Estas son las preguntas alrededor de las cuales realizar el diseño de la Web API. A primera vista se podría pensar a un enfoque en cascada, pero no es en absoluto el caso. No se trata de diseñar todo el API antes de su construcción; no es una receta para la parálisis por análisis. Definitivamente, hay decisiones que debe tomar por adelantado, pero que son de alto nivel y se relacionan con el diseño del API general. No requieren entender o predecir todos los aspectos del sistema. Más bien, estas decisiones han de sentar las bases para que el API tenga la capacidad de evolucionar de forma iterativa y orgánica.
  6. De una petición a la siguiente, el estado de los recursos puede cambiar drásticamente, por lo que la representación que se devuelve puede ser muy diferente.
  7. Ejemplos de media types son: text/html text/xml image/png application/json Los media types son identificados con dos componentes: la primera corresponde al nivel superior que describe de forma genérica el tipo de información; y la segunda denominada subtipo y que describe el formato concreto y su estructura de datos esperada.
  8. Si bien el término REST se refiere originalmente a un conjunto de principios de arquitectura, hoy día se usa en un sentido más amplio para describir cualquier interfaz entre sistemas que utilice directamente el protocolo y los verbos HTTP para obtener datos o indicar la ejecución de operaciones sobre los datos, en cualquier formato (XML, JSON, etc) sin las abstracciones adicionales de los protocolos basados en patrones de intercambio de mensajes, como por ejemplo SOAP. A servicios web públicamente disponibles en Internet se les suele denominar como servicios RESTful. En esencia, hablar de Servicios REST o de Interfaces RESTful es hablar de lo mismo. En el mundo .NET solemos llamar a los servicios REST como Web APIs.
  9. En Web APIs se emplea permalinks para apuntar a la versión más reciente.
  10. Por ejemplo, hace unos años la representación estándar era XML, hoy es JSON, mañana puede ser otra cosa totalmente diferente…
  11. Esto en principio no es nada descabellado, ya que es justamente como funcionan los navegadores web.
  12. Desde la perspectiva de desarrollo de sistemas capaces de evolucionar, es útil tener considerar el versionado como el último recurso, cómo una admisión de fracaso. Asignar un “V1” al API inicial es proclamar (casi a gritos) que ya sabe que el API no puede evolucionar, y que surgirán cambios rompedores cuando se publique la versión “V2”. Sin embargo, todos somos humanos, y como tal nos equivocamos. En este caso, el emitir versiones es lo que hacemos cuando hemos agotado todas las demás opciones.
  13. URL  Es el mecanismo más directo y más popular. No obstante es uno de los que más molesta a los puristas de REST, ya que insisten (y tienen razón) que un URI debe referir a un recurso único y distinguido. Custom Header y Media Content  Un custom header permite preservar las URIs entre versiones, a pesar de ser efectivamente una duplicidad del comportamiento ya implementado y existente en HTTP de negociación del contenido (content negotiation). Lo anterior se evita justamente empleando la cabecera Accept de HTTP, con lo cual se evita la mencionada duplicidad. Sin embargo, en ambos casos, se fuerza a los clientes a conocer de ante mano que cabeceras deben enviar en una solicitud para especificar la correcta versión del recurso que desean acceder. Y Ciertamente, en ambos cosas, se permite preservar un conjunto limpio y claro de URLs hacia los recursos, pero agrega la necesidad de lidiar con la complejidad de servir diferentes versiones de contenido en algún punto del código del Web API. A veces, y dependiendo de la tecnología, esta complejidad termina llegando a la implementación del controlador, que se más allá de sólo encargarse del procesamiento y atención del recurso, termina también siendo responsable de determinar que versión del mismo (y su representación) enviar al llamador.
  14. Así, cambios en el código de un ensamblado (por ejemplo por corrección de errores o bugs) implicará un cambio de versión del mismo… …Pero no necesariamente implica un cambio en el API que contiene, ya que no hay cambios en la interfaz pública. Cambios en los recursos, sus representaciones o sus puntos de acceso si implicará un cambio del API, y también de su correspondiente ensamblado.
  15. Planos del proyecto cancelado para la autovía de la Bahía de Seattle.
  16. Coherente es la palabra clave a lo largo de esta parte de la presentación…
  17. El modelo de datos debe estar estabilizado antes de comenzar a diseñar el Web API. Esto ayudará a minimizar la generación espontanea y acelerada de versiones por imponderables o cosas “que no se pensaron o tuvieron en cuenta”. Si los consumidores invierten tiempo (y por ende dinero y esfuerzo) en escribir código que permita emplear nuestro Web API, entonces nosotros debemos hacer el máximo esfuerzo por no romper esa integración.
  18. Siempre manteniendo la línea de la coherencia. Ejemplo: combinar URL con custom media type. En la medida de lo posible evitar “1.1”, “2.2.3”. El software puede evolucionar en otras versiones debido a correcciones de errores o bugs, o mejoras de rendimiento, o cambios en sus tecnologías inherentes (como frameworks o bases de datos). Pero el Web API como tal, sus recursos y representaciones no han cambiado, por lo cual no requieren versionarse de nuevo. En los principios SOLID, el principio “Open\Closed” de Bertrand Meyer sugiere que las entidades y componentes de un software deben ser abiertas (open) para su extensión, y cerradas (closed) para su modificación. Esto llevado al contexto de los Web APIs tiene la implicación de que un recurso no debe cambiar arbitrariamente su firma, sino más bien extenderse (son sobrecargas por ejemplo) para soportar nuevos parámetros o restricciones.
  19. Por ejemplo, Facebook garantiza que cada versión de su Web API durará al menos dos años, y será removida pasados dos años tras la emisión de una versión más nueva. Por ejemplo, si la versión v2.3 es liberada el 25 de marzo, 2015, y la versión de v2.4 se libera el 7 de Agosto de 2015, entonces la versión v2.3 dejará de estar disponible el 7 de Agosto de 2017, exactamente dos años después de la emisión de la versión v2.4. Este tipo de políticas deben estar correctamente documentadas y públicas.
  20. Por ejemplo, si empleamos versionado por URL, cuando un consumidor accede a un recurso viejo, se podría contestar con códigos HTTP 301 o 302 según convenga, para informar del cambio de localización del recurso debido a una nueva versión del mismo. Así: 301 Moved Permanently  para indicar que el recurso en el URI de la solicitud se ha movido permanentemente a otro URI, el cual debería ser un permalink sin información de versión a la instancia de la nueva versión del recurso). Este código de estado puede emplearse para indicar una versión del Web API obsoleta o no soportada, informando al consumidor del reemplazo por la nueva versión. 302 Found  para indicar que el recurso solicitado está temporalmente localizado en otro sitio, y que por lo tanto el URI sigue siendo soportado. Este código de estado puede ser útil cuando URIs sin información de versión (permalinks) no estén disponibles y se desee informar a los consumidores que aún así la URI es correcta, que serán re-direccionados temporalmente a una URI con información de versión, pero que deberían seguir empleando la URI original.