Mejores prácticas para el control de versiones API

¿Hay algún tipo de prácticas conocidas o prácticas recomendadas para el control de versiones de la API REST del servicio web?

Me he dado cuenta de que AWS realiza versiones por la URL del punto final . ¿Es esta la única manera o hay otras formas de lograr el mismo objective? Si hay múltiples formas, ¿cuáles son los méritos de cada camino?

Esta es una pregunta buena y difícil. El tema del diseño de URI es al mismo tiempo la parte más destacada de una API REST y , por lo tanto, un compromiso potencialmente a largo plazo hacia los usuarios de esa API .

Dado que la evolución de una aplicación y, en menor medida, su API es una realidad y que incluso es similar a la evolución de un producto aparentemente complejo como un lenguaje de progtwigción, el diseño de URI debería tener restricciones menos naturales y debería conservarse. con el tiempo Cuanto más larga sea la vida útil de la aplicación y la API, mayor será el compromiso con los usuarios de la aplicación y la API.

Por otro lado, otro hecho de la vida es que es difícil prever todos los recursos y sus aspectos que se consumirían a través de la API. Afortunadamente, no es necesario diseñar toda la API que se utilizará hasta Apocalypse . Es suficiente para definir correctamente todos los puntos finales de los recursos y el esquema de direccionamiento de cada instancia de recursos y recursos.

Con el tiempo, puede necesitar agregar nuevos recursos y nuevos atributos a cada recurso en particular, pero el método que los usuarios de la API sigan para acceder a recursos particulares no debería cambiar una vez que un esquema de direccionamiento de recursos se vuelva público y, por lo tanto, definitivo.

Este método se aplica a la semántica de verbos HTTP (por ejemplo, PUT siempre debe actualizar / reemplazar) y códigos de estado HTTP que son compatibles con versiones anteriores de API (deben seguir funcionando para que los clientes API que han trabajado sin intervención humana puedan continuar trabajando como eso).

Además, dado que la incrustación de la versión API en el URI alteraría el concepto de hipermedia como motor del estado de la aplicación (establecido en la disertación de doctorado Roy T. Fieldings) al tener una dirección / URI de recurso que cambiaría con el tiempo, concluiría que API las versiones no deben mantenerse en los URI de recursos durante mucho tiempo, lo que significa que los URI de recursos de los que pueden depender los usuarios de API deben ser enlaces permanentes .

Claro, es posible incorporar la versión de la API en el URI base, pero solo para usos razonables y restringidos, como la depuración de un cliente API que funciona con la nueva versión de la API. Tales API versionadas deberían tener un límite de tiempo y estar disponibles solo para grupos limitados de usuarios API (como durante betas cerradas). De lo contrario, te comprometes donde no deberías.

Un par de consideraciones sobre el mantenimiento de las versiones de API que tienen fecha de caducidad. Todas las plataformas de progtwigción / lenguajes utilizados comúnmente para implementar servicios web (Java, .NET, PHP, Perl, Rails, etc.) permiten una fácil unión de los puntos finales del servicio web a un URI base. De esta forma, es fácil recostackr y mantener una colección de archivos / clases / métodos separados en diferentes versiones de API .

Desde el punto de vista de los usuarios de la API, también es más fácil trabajar con una versión específica de la API y unirse a ella cuando es así de obvio, pero solo por un tiempo limitado, es decir, durante el desarrollo.

Desde el punto de vista del desarrollador de API, es más fácil mantener diferentes versiones de API en paralelo mediante el uso de sistemas de control de fuente que trabajan predominantemente en archivos como la unidad más pequeña de versiones (código fuente).

Sin embargo, con las versiones de API claramente visibles en URI hay una advertencia: también se puede objetar este enfoque ya que el historial de API se vuelve visible / aparent en el diseño de URI y por lo tanto es propenso a cambios con el tiempo que va en contra de las pautas de REST. ¡Estoy de acuerdo!

La forma de evitar esta objeción razonable es implementar la última versión de API bajo URI base de API sin versión. En este caso, los desarrolladores de cliente API pueden elegir entre:

  • desarrollar contra el último (comprometerse a mantener la aplicación protegiéndola de eventuales cambios API que podrían romper su mal diseñado API cliente ).

  • enlazar a una versión específica de la API (que se vuelve aparente) pero solo por un tiempo limitado

Por ejemplo, si API v3.0 es la última versión de API, los siguientes dos deben ser alias (es decir, se comportan de forma idéntica a todas las solicitudes de API):

 http: // shonzilla / api / clientes / 1234
 http: // shonzilla / api /v3.0 / customers / 1234
 http: // shonzilla / api / v3 / clientes / 1234

Además, los clientes API que todavía intentan apuntar a la antigua API deben ser informados para usar la última versión API anterior, si la versión API que están utilizando ya no es compatible o ya no está soportada . Para acceder a cualquiera de los URI obsoletos como estos:

 http: // shonzilla / api /v2.2 / customers / 1234
 http: // shonzilla / api /v2.0 / customers / 1234
 http: // shonzilla / api / v2 / customers / 1234
 http: // shonzilla / api /v1.1 / customers / 1234
 http: // shonzilla / api / v1 / customers / 1234

debe devolver cualquiera de los 30x códigos de estado HTTP que indican la redirección que se utilizan junto con el encabezado HTTP de Location que redirige a la versión apropiada del URI de recursos que sigue siendo este:

 http: // shonzilla / api / clientes / 1234

Existen al menos dos códigos de estado HTTP de redireccionamiento que son apropiados para los escenarios de versiones API:

  • 301 Se movió permanentemente indicando que el recurso con un URI solicitado se mueve de forma permanente a otro URI (que debería ser un enlace permanente de instancia de recurso que no contiene información de versión de API). Este código de estado se puede utilizar para indicar una versión de API obsoleta / no compatible, informando al cliente de la API que un URI de recursos versionados ha sido reemplazado por un enlace permanente de recursos .

  • 302 Encontrado que indica que el recurso solicitado se encuentra temporalmente en otra ubicación, mientras que el URI solicitado aún puede ser compatible. Este código de estado puede ser útil cuando los URI sin versión están temporalmente no disponibles y una solicitud debe repetirse usando la dirección de redirección (por ejemplo, apuntando al URI con la versión APi incrustada) y queremos decirle a los clientes que sigan usándolo (es decir, permalinks).

  • otros escenarios se pueden encontrar en el capítulo Redirección 3xx de la especificación HTTP 1.1

La URL NO debe contener las versiones. La versión no tiene nada que ver con la “idea” del recurso que está solicitando. Debería intentar pensar que la URL es una ruta hacia el concepto que le gustaría, no cómo desea que se devuelva el elemento. La versión dicta la representación del objeto, no el concepto del objeto. Como han dicho otros carteles, debe especificar el formato (incluida la versión) en el encabezado de la solicitud.

Si observa la solicitud HTTP completa para las URL que tienen versiones, se ve así:

 (BAD WAY TO DO IT): http://company.com/api/v3.0/customer/123 ====> GET v3.0/customer/123 HTTP/1.1 Accept: application/xml < ==== HTTP/1.1 200 OK Content-Type: application/xml  Neil Armstrong  

El encabezado contiene la línea que contiene la representación que está solicitando (“Aceptar: aplicación / xml”). Ahí es donde debería ir la versión. Todo el mundo parece pasar por alto el hecho de que puede querer lo mismo en diferentes formatos y que el cliente debe poder pedir lo que quiere. En el ejemplo anterior, el cliente está solicitando CUALQUIER representación XML del recurso, no realmente la representación real de lo que quiere. El servidor podría, en teoría, devolver algo completamente ajeno a la solicitud, siempre que fuera XML y tendría que ser analizado para darse cuenta de que está mal.

Una mejor manera es:

 (GOOD WAY TO DO IT) http://company.com/api/customer/123 ===> GET /customer/123 HTTP/1.1 Accept: application/vnd.company.myapp.customer-v3+xml < === HTTP/1.1 200 OK Content-Type: application/vnd.company.myapp-v3+xml  Neil Armstrong  

Además, digamos que los clientes piensan que el XML es demasiado detallado y ahora quieren JSON. En los otros ejemplos, debería tener una nueva URL para el mismo cliente, por lo que terminaría con:

 (BAD) http://company.com/api/JSONv3.0/customers/123 or http://company.com/api/v3.0/customers/123?format="JSON" 

(o algo similar). Cuando, de hecho, cada solicitud HTTP contiene el formato que está buscando:

 (GOOD WAY TO DO IT) ===> GET /customer/123 HTTP/1.1 Accept: application/vnd.company.myapp.customer-v3+json < === HTTP/1.1 200 OK Content-Type: application/vnd.company.myapp-v3+json {"customer": {"name":"Neil Armstrong"} } 

Al usar este método, tiene mucha más libertad en el diseño y en realidad se adhiere a la idea original de REST. Puede cambiar las versiones sin interrumpir a los clientes o cambiar de forma incremental los clientes a medida que cambian las API. Si elige dejar de admitir una representación, puede responder a las solicitudes con código de estado HTTP o códigos personalizados. El cliente también puede verificar que la respuesta esté en el formato correcto y validar el XML.

Hay muchas otras ventajas y analizo algunas de ellas aquí en mi blog: http://thereisnorightway.blogspot.com/2011/02/versioning-and-types-in-resthttp-api.html

Un último ejemplo para mostrar cómo poner la versión en la URL es malo. Supongamos que quiere información dentro del objeto y ha versionado sus diversos objetos (los clientes son v3.0, los pedidos son v2.0 y el objeto shipto es v4.2). Aquí está la URL desagradable que debe proporcionar en el cliente:

 (Another reason why version in the URL sucks) http://company.com/api/v3.0/customer/123/v2.0/orders/4321/ 

Nos pareció práctico y útil poner la versión en la URL. Hace que sea fácil decir lo que estás usando de un vistazo. Hacemos alias / foo a / foo / (últimas versiones) para facilitar el uso, URL más cortas / más limpias, etc., como lo sugiere la respuesta aceptada.

Mantener la compatibilidad hacia atrás para siempre suele ser prohibitivo y / o muy difícil. Preferimos dar aviso anticipado de desaprobación, redirecciones como las sugeridas aquí, documentos y otros mecanismos.

Acepto que versionar la representación de recursos sigue mejor el enfoque REST … pero, un gran problema con los tipos MIME personalizados (o los tipos MIME que añaden un parámetro de versión) es la falta de soporte para escribir en Aceptar y encabezados de tipo de contenido en HTML y JavaScript.

Por ejemplo, no es posible que IMO PUBLIQUE con los siguientes encabezados en formularios HTML5, para crear un recurso:

 Accept: application/vnd.company.myapp-v3+json Content-Type: application/vnd.company.myapp-v3+json 

Esto se debe a que el atributo HTML5 enctype es una enumeración, por lo que cualquier otra cosa que no sea la application/x-www-formurlencoded habitual application/x-www-formurlencoded , multipart/form-data y text/plain no es válida.

… ni estoy seguro de que sea compatible con todos los navegadores en HTML4 (que tiene un atributo de encytpe más laxo, pero sería un problema de implementación del navegador en cuanto a si se reenvió el tipo MIME)

Debido a esto, ahora siento que la forma más adecuada para la versión es a través del URI, pero acepto que no es la forma “correcta”.

Pon tu versión en el URI. Una versión de una API no siempre admite los tipos de otra, por lo que el argumento de que los recursos simplemente migran de una versión a otra es simplemente incorrecto. No es lo mismo que cambiar el formato de XML a JSON. Es posible que los tipos no existan o que hayan cambiado semánticamente.

Las versiones son parte de la dirección del recurso. Está enrutando de una API a otra. No es RESTful ocultar el direccionamiento en el encabezado.

Hay algunos lugares donde puedes hacer versiones en una API REST:

  1. Como se señaló, en el URI. Esto puede ser tratable e incluso estéticamente agradable si se usan bien los redireccionamientos y similares.

  2. En el encabezado Acepta: para que la versión esté en el tipo de archivo. Como ‘mp3’ vs ‘mp4’. Esto también funcionará, aunque IMO funciona un poco menos bien que …

  3. En el recurso en sí. Muchos formatos de archivo tienen sus números de versión incrustados en ellos, generalmente en el encabezado; esto permite que el software más reciente ‘simplemente funcione’ mediante la comprensión de todas las versiones existentes del tipo de archivo, mientras que el software anterior puede puntualizar si se especifica una versión no compatible (más reciente). En el contexto de una API REST, significa que sus URI nunca tienen que cambiar, solo su respuesta a la versión particular de los datos que le entregaron.

Puedo ver razones para usar los tres enfoques:

  1. si te gusta hacer nuevas API de “barrido limpio”, o para cambios de versiones principales en los que desees tal enfoque.
  2. si quiere que el cliente lo sepa antes de hacer un PUT / POST, ya sea que funcione o no.
  3. si está bien si el cliente tiene que hacer su PUT / POST para averiguar si va a funcionar.

La versión de su API REST es análoga a la versión de cualquier otra API. Se pueden realizar cambios menores en su lugar, los cambios importantes pueden requerir una API completamente nueva. Lo más fácil para usted es comenzar desde cero cada vez, que es cuando poner la versión en la URL tiene más sentido. Si desea hacer la vida más fácil para el cliente, intente mantener la compatibilidad con versiones anteriores, lo que puede hacer con deprecation (redirección permanente), recursos en varias versiones, etc. Esto es más complicado y requiere más esfuerzo. Pero también es lo que REST fomenta en “Los URI geniales no cambian”.

Al final es como cualquier otro diseño de API. Pesar el esfuerzo contra la conveniencia del cliente. Considere la posibilidad de adoptar versiones semánticas para su API, lo que deja en claro para sus clientes qué tan compatible es su nueva versión.