Práticas recomendadas para o versionamento de API?

Há alguma prática ou melhores práticas conhecidas para o versionamento da API REST de serviço da web?

Percebi que a AWS faz o version control pela URL do terminal . Esta é a única maneira ou existem outras maneiras de alcançar o mesmo objective? Se existem várias maneiras, quais são os méritos de cada maneira?

Esta é uma boa e uma pergunta complicada. O tópico de design de URI é ao mesmo tempo a parte mais proeminente de uma API REST e , portanto, um compromisso potencialmente de longo prazo com os usuários dessa API .

Como a evolução de um aplicativo e, em menor extensão, de sua API é um fato da vida e é até similar à evolução de um produto aparentemente complexo como uma linguagem de programação, o design de URI deve ter menos restrições naturais e deve ser preservado ao longo do tempo . Quanto maior o tempo de vida do aplicativo e da API, maior o comprometimento com os usuários do aplicativo e da API.

Por outro lado, outro fato da vida é que é difícil prever todos os resources e seus aspectos que seriam consumidos por meio da API. Felizmente, não é necessário projetar a API inteira que será usada até o Apocalypse . É suficiente definir corretamente todos os endpoints de resources e o esquema de endereçamento de cada recurso e instância de recurso.

Com o tempo, você pode precisar adicionar novos resources e novos atributos a cada recurso específico, mas o método que os usuários da API seguem para acessar determinados resources não deve ser alterado quando um esquema de endereçamento de resources se tornar público e, portanto, final.

Este método se aplica à semântica do verbo HTTP (por exemplo, PUT deve sempre atualizar / replace) e códigos de status HTTP que são suportados em versões anteriores da API (eles devem continuar a funcionar para que os clientes da API que trabalharam sem intervenção humana possam continuar trabalhando) Curtiu isso).

Além disso, como a incorporação da versão da API na URI interromperia o conceito de hipermídia como o mecanismo do estado do aplicativo (declarado na dissertação de doutorado de Roy T. Fieldings) por ter um endereço de recurso / URI que mudaria com o tempo, concluiria que a API As versões não devem ser mantidas em URIs de resources por um longo tempo, o que significa que os URIs de resources dos quais os usuários da API podem depender devem ser permalinks .

Claro, é possível incorporar a versão da API no URI de base, mas apenas para usos razoáveis ​​e restritos, como a debugging de um cliente de API que funciona com a nova versão da API. Essas APIs com versão devem ser limitadas no tempo e estar disponíveis apenas para grupos limitados de usuários da API (como durante betas fechados). Caso contrário, você se compromete onde não deveria.

Algumas considerações sobre a manutenção de versões da API com data de expiração. Todas as plataformas / linguagens de programação comumente usadas para implementar serviços da Web (Java, .NET, PHP, Perl, Rails, etc.) permitem uma vinculação fácil do (s) ponto (s) de extremidade do serviço da Web a um URI de base. Dessa forma, é fácil reunir e manter uma coleção de arquivos / classs / methods separados em diferentes versões da API .

A partir do POV dos usuários da API, também é mais fácil trabalhar com e vincular-se a uma determinada versão da API quando isso é óbvio, mas apenas por tempo limitado, ou seja, durante o desenvolvimento.

A partir do POV do desenvolvedor da API, é mais fácil manter diferentes versões da API em paralelo usando sistemas de controle de origem que trabalham predominantemente em arquivos como a menor unidade de versão (código-fonte).

No entanto, com as versões da API claramente visíveis no URI, há uma ressalva: também é possível objetar essa abordagem, já que o histórico da API se torna visível / aparente no design da URI e, portanto, está sujeito a mudanças ao longo do tempo que vai contra as diretrizes do REST. Concordo!

A maneira de contornar essa objeção razoável é implementar a versão mais recente da API sob URI de base da API sem versão. Nesse caso, os desenvolvedores de clientes da API podem optar por:

  • desenvolver contra o mais recente (comprometendo-se a manter o aplicativo protegendo-o de eventuais alterações de API que possam quebrar seu cliente de API mal projetado ).

  • ligar a uma versão específica da API (que se torna aparente), mas apenas por um tempo limitado

Por exemplo, se a API v3.0 for a versão mais recente da API, as duas seguintes devem ser aliases (isto é, comportar-se de maneira idêntica a todas as solicitações da API):

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

Além disso, os clientes da API que ainda tentam apontar para a API antiga devem ser informados para usar a versão da API anterior mais recente, caso a versão da API que eles estão usando seja obsoleta ou não seja mais suportada . Então, acessando qualquer um dos URIs obsoletos como estes:

 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

deve retornar qualquer um dos códigos de status HTTP 30x que indicam o redirecionamento usado em conjunto com o header HTTP de Location que redireciona para a versão apropriada do URI de recurso que permanece como esta:

 http: // shonzilla / api / customers / 1234

Existem pelo menos dois códigos de status HTTP de redirecionamento que são apropriados para cenários de versão de API:

  • 301 Movido permanentemente, indicando que o recurso com um URI solicitado é movido permanentemente para outro URI (que deve ser um permalink de instância de recurso que não contém informações sobre a versão da API). Esse código de status pode ser usado para indicar uma versão de API obsoleta / sem suporte, informando ao cliente de API que um URI de recurso com versão foi substituído por um link permanente de recurso .

  • 302 Encontrado indicando que o recurso solicitado está temporariamente localizado em outro local, enquanto o URI solicitado ainda pode ser suportado. Esse código de status pode ser útil quando os URIs sem versão estão temporariamente indisponíveis e que uma solicitação deve ser repetida usando o endereço de redirecionamento (por exemplo, apontando para o URI com a versão APi incorporada) e queremos que os clientes continuem usando (por exemplo, permalinks).

  • outros cenários podem ser encontrados no capítulo Redirecionamento 3xx da especificação HTTP 1.1

O URL NÃO deve conter as versões. A versão não tem nada a ver com “ideia” do recurso que você está solicitando. Você deve tentar pensar na URL como sendo um caminho para o conceito que você gostaria – não como você deseja que o item seja retornado. A versão determina a representação do object, não o conceito do object. Como outros pôsteres disseram, você deve especificar o formato (incluindo a versão) no header da solicitação.

Se você olhar a solicitação HTTP completa para as URLs que têm versões, será semelhante a:

 (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  

O header contém a linha que contém a representação que você está pedindo (“Accept: application / xml”). É aí que a versão deve ir. Todo mundo parece encobrir o fato de que você pode querer a mesma coisa em formatos diferentes e que o cliente deve poder pedir o que deseja. No exemplo acima, o cliente está pedindo para qualquer representação XML do recurso – não é realmente a representação verdadeira do que ele quer. O servidor poderia, em teoria, retornar algo completamente não relacionado ao pedido, desde que fosse XML e teria que ser analisado para perceber que está errado.

A melhor maneira é:

 (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  

Além disso, vamos dizer que os clientes acham que o XML é muito detalhado e agora querem JSON. Nos outros exemplos, você teria que ter um novo URL para o mesmo cliente, assim você acabaria com:

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

(ou algo similar). Quando, na verdade, todas as solicitações HTTP contêm o formato que você está procurando:

 (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"} } 

Usando esse método, você tem muito mais liberdade no design e, na verdade, está aderindo à ideia original do REST. Você pode alterar as versões sem interromper os clientes ou alterar de forma incremental os clientes à medida que as APIs são alteradas. Se você optar por interromper o suporte a uma representação, poderá responder às solicitações com código de status HTTP ou códigos personalizados. O cliente também pode verificar se a resposta está no formato correto e validar o XML.

Há muitas outras vantagens e discuto algumas delas aqui no meu blog: http://thereisnorightway.blogspot.com/2011/02/versioning-and-types-in-resthttp-api.html

Um último exemplo para mostrar como colocar a versão no URL é ruim. Digamos que você queira alguma informação dentro do object, e tenha versionado seus vários objects (os clientes são v3.0, os pedidos são v2.0 e o object shipto é v4.2). Aqui está o URL desagradável que você deve fornecer no cliente:

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

Achamos prático e útil colocar a versão no URL. Isso torna mais fácil dizer o que você está usando em um piscar de olhos. Fazemos alias / foo para / foo / (versões mais recentes) para facilitar o uso, URLs mais curtos / mais limpos, etc, como a resposta aceita sugere.

Manter a compatibilidade retroativa para sempre é muitas vezes proibitivo em termos de custos e / ou muito difícil. Preferimos dar aviso antecipado de reprovação, redirecionamentos como sugerido aqui, documentos e outros mecanismos.

Concordo que versionar a representação de resources segue melhor a abordagem REST … mas, um grande problema com os tipos MIME personalizados (ou tipos MIME que acrescentam um parâmetro de versão) é o baixo suporte para gravar nos headers Accept e Content-Type em HTML e JavaScript.

Por exemplo, não é possível IMO para POST com os seguintes headers em formulários HTML5, para criar um recurso:

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

Isso ocorre porque o atributo de enctype HTML5 é uma enumeração, portanto, qualquer outra coisa que não o application/x-www-formurlencoded usual application/x-www-formurlencoded , multipart/form-data e text/plain são inválidos.

… nem tenho certeza de que ele é suportado em todos os navegadores em HTML4 (que tem um atributo de encytpe mais fraco, mas seria um problema de implementação do navegador se o tipo MIME foi encaminhado)

Por causa disso agora eu sinto que a maneira mais apropriada para a versão é através do URI, mas eu aceito que não é o caminho ‘correto’.

Coloque sua versão no URI. Uma versão de uma API nem sempre suporta os tipos de outra, então o argumento de que os resources são meramente migrados de uma versão para outra é simplesmente errado. Não é o mesmo que mudar o formato de XML para JSON. Os tipos podem não existir ou podem ter mudado semanticamente.

As versões fazem parte do endereço do recurso. Você está roteando de uma API para outra. Não é RESTful ocultar o endereçamento no header.

Existem alguns locais em que você pode fazer o versionamento em uma API REST:

  1. Conforme observado, no URI. Isso pode ser tratável e até mesmo esteticamente agradável se os redirecionamentos e similares forem bem utilizados.

  2. No header Aceita:, a versão está no tipo de arquivo. Curta ‘mp3’ vs ‘mp4’. Isso também funcionará, embora o IMO funcione um pouco menos que …

  3. No recurso em si. Muitos formatos de arquivo têm seus números de versão incorporados neles, normalmente no header; Isso permite que o software mais novo “simplesmente funcione”, compreendendo todas as versões existentes do tipo de arquivo, enquanto o software antigo pode ser contornado se uma versão não suportada (mais recente) for especificada. No contexto de uma API REST, isso significa que seus URIs nunca precisam mudar, apenas sua resposta à versão específica dos dados que você recebeu.

Eu posso ver razões para usar todas as três abordagens:

  1. se você gosta de fazer novas APIs de ‘clean clean’, ou para grandes mudanças de versão onde você quer tal abordagem.
  2. se você quer que o cliente saiba antes de fazer um PUT / POST se vai funcionar ou não.
  3. se estiver tudo bem se o cliente tiver que fazer o seu PUT / POST para descobrir se vai funcionar.

O version control da sua API REST é análogo ao version control de qualquer outra API. Pequenas alterações podem ser feitas no local, alterações importantes podem exigir uma API totalmente nova. O mais fácil para você é começar do zero todas as vezes, ou seja, quando colocar a versão na URL faz mais sentido. Se você quiser tornar a vida mais fácil para o cliente, tente manter a compatibilidade com versões anteriores, o que pode ser feito com reprovação (redirecionamento permanente), resources em várias versões etc. Isso é mais complicado e requer mais esforço. Mas também é o que o REST encoraja em “Cool URIs does not change”.

No final, é como qualquer outro design de API. Pesar o esforço contra a conveniência do cliente. Considere a adoção de versões semânticas para sua API, o que torna claro para seus clientes como a nova versão é compatível com versões anteriores.