¿Mejores prácticas para el control de versiones de API? [cerrado]

877

¿Se conocen procedimientos o mejores prácticas para el control de versiones de la API REST del servicio web?

Me di cuenta de que AWS realiza el control de versiones por la URL del punto final . ¿Es esta la única forma o hay otras formas de lograr el mismo objetivo? Si hay varias formas, ¿cuáles son los méritos de cada forma?

Swaroop CH
fuente

Respuestas:

682

Esta es una buena y una pregunta 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 es incluso similar a la evolución de un producto aparentemente complejo como un lenguaje de programación, el diseño de URI debería tener menos restricciones naturales y debería preservarse tiempo extraordinario . 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 definir correctamente todos los puntos finales de recursos y el esquema de direccionamiento de cada recurso y instancia de recurso.

Con el tiempo, es posible que deba agregar nuevos recursos y nuevos atributos a cada recurso en particular, pero el método que siguen los usuarios de API para acceder a recursos en particular no debería cambiar una vez que un esquema de direccionamiento de recursos se haga público y, por lo tanto, final.

Este método se aplica a la semántica de verbos HTTP (p. Ej., PUT siempre debe actualizarse / reemplazarse) y los códigos de estado HTTP admitidos en versiones API anteriores (deben continuar funcionando para que los clientes API que han trabajado sin intervención humana puedan continuar trabajando) como eso).

Además, dado que la incorporación de la versión API en el URI interrumpiría el concepto de hipermedia como el motor del estado de la aplicación (indicado en la tesis doctoral de Roy T. Fieldings) al tener una dirección de recursos / URI 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 los usuarios de API pueden depender deben ser enlaces permanentes .

Claro, es posible incrustar la versión 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 API. Dichas API versionadas deberían tener un tiempo limitado y estar disponibles solo para grupos limitados de usuarios de API (como durante las versiones beta cerradas). De lo contrario, te comprometes donde no deberías.

Un par de reflexiones sobre el mantenimiento de versiones de API que tienen fecha de vencimiento. Todas las plataformas / lenguajes de programación comúnmente utilizados para implementar servicios web (Java, .NET, PHP, Perl, Rails, etc.) permiten vincular fácilmente los puntos finales del servicio web a un URI base. De esta manera, es fácil reunir 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 API, también es más fácil trabajar y vincularse a una versión de API en particular cuando es obvio, pero solo por un tiempo limitado, es decir, durante el desarrollo.

Desde el punto de vista del mantenedor 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 principalmente 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 podría objetar este enfoque ya que el historial de API se vuelve visible / aparente en el diseño de URI y, por lo tanto, es propenso a cambios en el tiempo que van en contra de las pautas de REST. ¡Estoy de acuerdo!

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

  • desarrollarse contra el último (comprometiéndose a mantener la aplicación protegiéndola de eventuales cambios de API que podrían dañar su cliente API mal diseñado ).

  • vincularse a una versión específica de la API (que se hace evidente) pero solo por un tiempo limitado

Por ejemplo, si API v3.0 es la última versión de API, los siguientes dos deberían ser alias (es decir, comportarse de manera idéntica a todas las solicitudes de API):

http: // shonzilla / api / clients / 1234 
http: // shonzilla / api /v3.0 / / 1234
http: // shonzilla / api / v3 / clients / 1234

Además, los clientes de API que todavía tratan de punto a la antigua API deben ser informados a utilizar la última versión de la API anterior, si la versión de la API que están usando es obsoleto o no soportado más . Entonces, acceda a cualquiera de los URI obsoletos como estos:

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

debería devolver cualquiera de los 30 códigos de estado HTTP que indican la redirección que se utiliza junto con el Locationencabezado HTTP que redirige a la versión adecuada del URI de recursos que sigue siendo este:

http: // shonzilla / api / clients / 1234

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

  • 301 Se movió permanentemente, lo que indica que el recurso con un URI solicitado se mueve permanentemente 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 usar para indicar una versión API obsoleta / no compatible, informando al cliente API que un URI de recurso versionado ha sido reemplazado por un enlace permanente de recursos .

  • 302 Se encontró 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 no están disponibles temporalmente y se debe repetir una solicitud utilizando la dirección de redireccionamiento (por ejemplo, apuntando al URI con la versión APi incrustada) y queremos decirles a los clientes que sigan usándolo (es decir, el enlaces permanentes).

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

Shonzilla
fuente
142
El uso de un número de versión en la URL no debe considerarse una mala práctica cuando cambia la implementación subyacente. "Cuando la interfaz de un servicio cambia de una manera no compatible con versiones anteriores, en realidad se ha creado un servicio completamente nuevo ... Desde la perspectiva del cliente, un servicio no es más que una interfaz y algunas cualidades no funcionales. .si la interfaz de un servicio cambia de una manera no compatible con versiones anteriores, ya no representa una instancia del servicio original, sino que es un servicio completamente nuevo ". ibm.com/developerworks/webservices/library/ws-version
benvolioT
77
¿Tiene alguna idea sobre agregar un encabezado con el número de versión para que los clientes o desarrolladores puedan verificarlo?
webclimber
11
Consulte también el uso de un encabezado Aceptar para indicar la versión que el cliente espera: blog.steveklabnik.com/2011/07/03/…
Weston Ruter
52
Para la última parte: diría que una API que está obsoleta y ya no es compatible debería regresar 410 Gone, ya que una redirección podría indicar que la nueva ubicación es compatible cuando no lo es. Si la API es simplemente obsoleta pero todavía existe, un WarningEncabezado HTTP en la Respuesta podría ser una opción.
Michael Stum
22
¿Cómo trata con los clientes que ya están usando la URL estable como shonzilla / api / clients / 1234 y desea actualizar a una nueva versión? ¿Cómo puede obligarlos a agregar el V2 (el antiguo) a la URL?
Dejell el
273

La URL NO debe contener las versiones. La versión no tiene nada que ver con la "idea" del recurso que está solicitando. Debe intentar pensar en la URL como una ruta hacia el concepto que desea, 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 de 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
<customer version="3.0">
  <name>Neil Armstrong</name>
</customer>

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 es posible que desee lo mismo en diferentes formatos y que el cliente pueda pedir lo que quiere. En el ejemplo anterior, el cliente solicita 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 analizarse para darse cuenta de que está equivocado.

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
<customer>
  <name>Neil Armstrong</name>
</customer>

Además, digamos que los clientes piensan que el XML es demasiado detallado y ahora quieren JSON en su lugar. En los otros ejemplos, tendría que 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"}
}

Con este método, tiene mucha más libertad en el diseño y se está adhiriendo a la idea original de REST. Puede cambiar las versiones sin interrumpir a los clientes, o cambiar gradualmente los clientes a medida que cambian las API. Si elige dejar de admitir una representación, puede responder a las solicitudes con un 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 discuto 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 desea alguna 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/
jeremyh
fuente
10
El manejo de versiones independientes de contrato de datos y versiones de contrato de servicio en el encabezado Aceptar parece un tanto desordenado como desordenado en la URL. Hay más opciones ? Además, si tengo varios puntos finales (jabón, descanso), ¿esto también debería indicarse en Acepta y dejar que el servicio de enrutamiento en el extremo del servidor decida la dirección al punto final correcto O es aceptable tener el punto final codificado en la URL?
ideafountain
117
No puedo estar de acuerdo con esto, al menos hasta el punto de su última razón. Esto parece estar diciendo que las diferentes partes del URI tienen diferentes versiones. Pero ese no es el punto de una versión API. El punto es tener UNA versión para TODO el recurso. Si cambia las versiones, es un recurso API diferente. Es por eso que no tiene sentido ver company.com/api/v3.0/customer/123/v2.0/orders/4321, sino company.com/api/v3.0/customer/123/orders/4321 No está versionando ninguna parte dada del recurso, está versionando el recurso como un todo.
fool4jesus
90
Usar semánticamente el número de versión en el encabezado parece mejor. Pero es mucho más práctico usar la URL: menos propenso a errores, mejor depurado, visto fácilmente por los desarrolladores, fácilmente modificable en clientes de prueba en reposo.
Daniel Cerecedo
77
Creo que lo MALO / BUENO simplifica la pregunta. API significa "interfaz de programación de aplicaciones" y las interfaces de versiones parecen ser una muy buena idea. Las API no se tratan solo de servir recursos. Lo que debe separarse es que algunas personas están hablando de interfaces y otras personas están hablando de recursos. Si observa detenidamente la API de Google Maps en la pestaña de red, verá que incluyen el número de versión de la API en la URL. Por ejemplo: maps.google.com/maps/api/jsv2 durante la autenticación. El jsv2 es el número de API.
Tom Gruner
66
@Gili: En realidad, ya no deberías usarlo, -xya que está obsoleto por RFC6648 .
Jonathan W
98

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

Mantener la compatibilidad con versiones anteriores para siempre suele ser costoso y / o muy difícil. Preferimos dar aviso avanzado de desuso, redireccionamientos como los sugeridos aquí, documentos y otros mecanismos.

Yoav Shapira
fuente
55
La respuesta aceptada puede ser la correcta y la más pura. Sin embargo, para el desarrollador y el usuario diario de las API, esta es seguramente la forma más fácil de usar y configurar. El enfoque más pragmático. Como lo indican otros Google y Amazon también utilizan este enfoque.
Muhammad Rehan Saeed
46

Estoy de acuerdo en 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 agregan un parámetro de versión) es el escaso soporte para escribir en los encabezados Aceptar y Tipo de contenido en HTML y JavaScript

Por ejemplo, no es posible IMO POSTAR 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 es porque el HTML5 enctypeatributo es una enumeración, por lo tanto, otra cosa que no sea la habitual application/x-www-formurlencoded, multipart/form-datay text/plainno son válidos.

... ni tampoco estoy seguro de que sea compatible con todos los navegadores en HTML4 (que tiene un atributo de tipo de letra 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".

Kevsy
fuente
14
Suponiendo la ruta donde se definió el control de versiones en los encabezados, se podría decir que los formularios HTML que usan el envío de formularios nativos siempre usarían la última versión de la API, ya que no pasarían la versión específica a la que desean adherirse. Sin embargo, las solicitudes XHR sí le permiten cambiar las aceptaciones y leer los encabezados de tipo contenido. Entonces las formas básicas son realmente el único problema.
Kyle Hayes
No estoy seguro de estar de acuerdo con que el URI es el más apropiado, pero el hecho de que Content-Type no funcione con formularios es muy importante.
wprl
2
@Kyle, vi un blog en alguna parte que decía que si no especificas una versión en el encabezado de la solicitud, es mejor regresar con la primera versión de la API, no la última, para la mejor compatibilidad.
Andy
Eso realmente tiene mucho sentido para mí ahora que lo pienso.
Kyle Hayes
@KyleHayes no olvide los iframes, video / embed y otras etiquetas de tipo "src / href".
favor
21

Pon tu versión en el URI. Una versión de una API no siempre admitirá los tipos de otra, por lo que el argumento de que los recursos simplemente se migran de una versión a otra es simplemente erróneo. 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ás enrutando de una API a otra. No es RESTful ocultar el direccionamiento en el encabezado.

Sean O'Dell
fuente
13

Hay algunos lugares donde puede realizar el control de versiones en una API REST:

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

  2. En el encabezado Acepta: 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 mismo. 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 nuevo 'simplemente funcione' al comprender todas las versiones existentes del tipo de archivo, mientras que el software más antiguo puede despejar si se especifica una versión no admitida (más nueva). 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 recibió.

Puedo ver razones para usar los tres enfoques:

  1. si le gusta hacer nuevas API de 'barrido limpio', o para cambios importantes de versión donde desea tal enfoque.
  2. si desea que el cliente sepa antes de hacer un PUT / POST si va a funcionar o no.
  3. si está bien si el cliente tiene que hacer su PUT / POST para averiguar si va a funcionar.
pjz
fuente
8

El control de versiones de su API REST es análogo al control de versiones de cualquier otra API. Se pueden realizar cambios menores, los cambios importantes pueden requerir una API completamente nueva. Lo más fácil para ti es comenzar desde cero cada vez, que es cuando poner la versión en la URL tiene más sentido. Si desea facilitarle la vida al cliente, intente mantener la compatibilidad con versiones anteriores, lo que puede hacer con desaprobación (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. Considere el esfuerzo contra la conveniencia del cliente. Considere la posibilidad de adoptar versiones semánticas para su API, lo que deja en claro a sus clientes cuán compatible es su nueva versión con versiones anteriores.

Alexander Torstling
fuente