Práticas recomendadas para controle de versão da API? [fechadas]

877

Existem instruções ou práticas recomendadas para o versionamento da API REST de serviço da web?

Percebi que a AWS faz controle de versão pela URL do terminal . Essa é a única maneira ou existem outras maneiras de atingir o mesmo objetivo? Se existem várias maneiras, quais são os méritos de cada maneira?

Swaroop CH
fonte

Respostas:

682

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

Desde a evolução de um aplicativo e, em menor grau, sua API é um fato da vida e é ainda semelhante à evolução de um produto aparentemente complexo como uma linguagem de programação, o design do URI deve ter menos restrições naturais e deve ser preservado ao longo do tempo . Quanto maior a vida útil 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 recursos e seus aspectos que seriam consumidos por meio da API. Felizmente, não é necessário projetar toda a API que será usada até o Apocalypse . É suficiente definir corretamente todos os pontos finais de recursos e o esquema de endereçamento de cada recurso e instância de recurso.

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

Esse método se aplica à semântica de verbos HTTP (por exemplo, a PUT sempre deve atualizar / substituir) e aos códigos de status HTTP suportados nas 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 no 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 ao longo do tempo, eu concluiria que a API as versões não devem ser mantidas nos URIs de recursos por um longo período de tempo, o que significa que os URIs de recursos dos quais os usuários da API podem confiar devem ter links permanentes .

Claro, é possível incorporar a versão da API no URI base, mas apenas para usos razoáveis ​​e restritos, como depurar um cliente da API que funcione com a nova versão da API. Essas APIs com versão devem ter um tempo limitado 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 reflexões sobre a manutenção de versões da API com data de validade. Todas as plataformas / linguagens de programação comumente usadas para implementar serviços da Web (Java, .NET, PHP, Perl, Rails, etc.) permitem a ligação fácil dos pontos finais dos serviços da Web a um URI base. Dessa forma, é fácil reunir e manter uma coleção de arquivos / classes / métodos separados em diferentes versões da API .

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

No ponto de vista do mantenedor da API, é mais fácil manter versões diferentes da API em paralelo, usando sistemas de controle de origem que funcionam 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, pois o histórico da API se torna visível / aparente no design do URI e, portanto, está sujeito a alterações ao longo do tempo, o que contraria as diretrizes do REST. Concordo!

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

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

  • vincular 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 é a versão mais recente da API, os dois seguintes devem ter aliases (ou seja, comportar-se de forma idêntica a todas as solicitações de 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 a usar a versão anterior mais recente da API, se a versão da API que eles estão usando estiver obsoleta ou não for mais suportada . Portanto, acesse 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 Locationcabeçalho HTTP que redireciona para a versão apropriada do URI do recurso que permanece como este:

http: // shonzilla / api / customers / 1234

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

  • 301 Movido permanentemente, indicando que o recurso com um URI solicitado é movido permanentemente para outro URI (que deve ser um link permanente de instância de recurso que não contém informações da versão da API). Esse código de status pode ser usado para indicar uma versão obsoleta / não suportada da API, informando ao cliente da 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 dizer aos clientes para continuar usando (ou seja, o permalinks).

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

Shonzilla
fonte
142
Usar um número de versão na URL não deve ser considerado uma prática ruim quando a implementação subjacente é alterada. "Quando a interface para um serviço muda de uma maneira não compatível com versões anteriores, na realidade um serviço totalmente novo é criado ... Do ponto de vista do cliente, um serviço não passa de uma interface e algumas qualidades não funcionais. .se a interface de um serviço mudar de maneira não compatível com versões anteriores, ela não representa mais uma instância do serviço original, mas é um serviço completamente novo ". ibm.com/developerworks/webservices/library/ws-version
benvolioT
7
Você pensa em adicionar um cabeçalho com o número da versão para que possa ser verificado por clientes ou desenvolvedores?
Webclimber 18/10/10
11
Veja também o uso de um cabeçalho Accept para indicar a versão que o cliente espera: blog.steveklabnik.com/2011/07/03/…
Weston Ruter
52
Para a última parte: eu diria que uma API obsoleta e que não é mais suportada deve retornar 410 Gone, pois um redirecionamento pode indicar que o novo local é compatível quando não é. Se a API for apenas obsoleta, mas ainda existir, um Warningcabeçalho HTTP na resposta pode ser uma opção.
Michael Stum
22
Como você lida com clientes que já estão usando a URL estável como shonzilla / api / customers / 1234 e deseja atualizar para uma nova versão? como você pode forçá-los a adicionar o V2 ​​(o antigo) ao URL?
Dejell
273

O URL NÃO deve conter as versões. A versão não tem nada a ver com a "idéia" do recurso que você está solicitando. Você deve tentar pensar no URL como um caminho para o conceito que deseja - não como deseja que o item seja devolvido. A versão determina a representação do objeto, não o conceito do objeto. Como outros pôsteres disseram, você deve especificar o formato (incluindo a versão) no cabeçalho da solicitação.

Se você olhar para a solicitação HTTP completa dos URLs que possuem versões, será semelhante a este:

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

O cabeçalho contém a linha que contém a representação que você está solicitando ("Accept: application / xml"). É para onde a versão deve ir. Todo mundo parece encobrir o fato de que você pode querer a mesma coisa em diferentes formatos e que o cliente deve poder pedir o que deseja. No exemplo acima, o cliente está solicitando QUALQUER representação XML do recurso - e não a verdadeira representação do que deseja. O servidor poderia, em teoria, retornar algo completamente não relacionado à solicitação, desde que fosse XML e precisaria ser analisado para perceber que está errado.

Uma maneira melhor é:

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

Além disso, digamos que os clientes pensem que o XML é muito detalhado e agora desejam o JSON. Nos outros exemplos, você precisaria ter um novo URL para o mesmo cliente, para acabar com:

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

(ou algo semelhante). 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 está realmente aderindo à ideia original do REST. Você pode alterar as versões sem interromper os clientes ou alterar gradualmente os clientes à medida que as APIs são alteradas. Se você optar por parar de oferecer 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 na URL é ruim. Digamos que você queira alguma informação dentro do objeto e você fez a versão de vários objetos (os clientes são v3.0, os pedidos são v2.0 e o objeto 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/
Jeremyh
fonte
10
O tratamento da versão do contrato de dados independente e das versões do contrato de serviço no cabeçalho Accept parece tão confuso quanto no URL. Existem outras opções ? Além disso, se eu tiver vários pontos de extremidade (sabão, descanso), isso também deve ser indicado em Aceita e permite que o serviço de roteamento no servidor final decida a direção para o ponto de extremidade correto OU é aceitável que o ponto de extremidade seja codificado na URL?
ideafountain
117
Não posso concordar com isso, pelo menos até o ponto de sua última razão. Parece estar dizendo que as diferentes partes do URI têm versões diferentes. Mas esse não é o objetivo de uma versão da API. O ponto é ter uma versão para o recurso INTEIRO. Se você alterar as versões, é um recurso de API diferente. É por isso que não faz sentido ver company.com/api/v3.0/customer/123/v2.0/orders/4321, mas sim company.com/api/v3.0/customer/123/orders/4321 Você não está fazendo o controle de versão de nenhuma parte do recurso, está fazendo o controle de versão como um todo.
fool4jesus
90
Semmanticamente, usar o número da versão no cabeçalho parece melhor. Mas é muito mais prático usar o URL: menos propenso a erros, melhor depurado, visto facilmente pelos desenvolvedores, facilmente modificável nos demais clientes de teste.
Daniel Cerecedo
7
Acho que o MAU / BOM simplifica a questão. API significa "Interface de programação de aplicativos" e as interfaces de versão parecem ser uma ideia muito boa. APIs não são realmente apenas sobre servir recursos. O que precisa ser separado é que algumas pessoas estão falando sobre interfaces e outras sobre recursos. Se você olhar atentamente a API do Google Maps na guia rede, verá que eles incluem o número da versão da API na URL. Por exemplo: maps.google.com/maps/api/jsv2 durante a autenticação. O jsv2 é o número da API.
22613 Tom Gruner
6
@ Gili: Na verdade, você não deve mais usar -x, pois está obsoleto pelo RFC6648 .
Jonathan W
98

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

Manter a compatibilidade com versões anteriores para sempre é muitas vezes proibitivo em termos de custos e / ou muito difícil. Preferimos avisar com antecedência sobre reprovação, redirecionamentos, como sugerido aqui, documentos e outros mecanismos.

Yoav Shapira
fonte
5
A resposta aceita pode ser a correta e a mais pura. No entanto, para o desenvolvedor e o usuário diário das APIs, esse é certamente o mais fácil de usar e configurar. A abordagem mais pragmática. Conforme indicado por outros Google e Amazon, também use essa abordagem.
Muhammad Rehan Saeed
46

Concordo que o controle de versão da representação de recursos 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 pouco suporte para gravar nos cabeçalhos Accept e Content-Type em HTML e JavaScript.

Por exemplo, não é possível que o IMO POST com os seguintes cabeçalhos nos 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 enctypeatributo HTML5 é uma enumeração, portanto, qualquer coisa que não seja o habitual application/x-www-formurlencoded, multipart/form-datae text/plainé inválido.

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

Por causa disso, agora sinto que a maneira mais apropriada de versão é via URI, mas aceito que não seja a maneira 'correta'.

Kevsy
fonte
14
Assumindo a rota em que o controle de versão foi definido nos cabeçalhos, pode-se dizer que os formulários HTML que usam o envio nativo de formulários sempre usariam a versão mais recente da API, pois não passariam a versão específica que desejam aderir. No entanto, as solicitações XHR de fato permitem alterar as aceitações e ler os cabeçalhos do tipo de conteúdo. Portanto, formas básicas são realmente o único problema.
Kyle Hayes
Não sei se concordo que o URI é mais apropriado, mas o fato de o Content-Type não funcionar com formulários é muito importante.
Wprl
2
@ Kyle, vi um blog em algum lugar dizer que, se você não especificar uma versão no cabeçalho da solicitação, é melhor retornar com a primeira versão da API, não a mais recente, para a melhor compatibilidade.
andy
Isso realmente faz muito sentido para mim agora que penso.
Kyle Hayes
@KyleHayes não se esqueça de iframes, video / embed e outras tags do tipo "src / href".
pllee
21

Coloque sua versão no URI. Uma versão de uma API nem sempre suporta os tipos de outra, portanto, o argumento de que os recursos são apenas migrados de uma versão para outra é simplesmente errado. Não é o mesmo que mudar de formato de XML para JSON. Os tipos podem não existir ou podem ter sido alterados 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 cabeçalho.

Sean O'Dell
fonte
13

Existem alguns lugares em que você pode fazer controle de versão em uma API REST:

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

  2. No cabeçalho Aceita:, portanto, a versão está no tipo de arquivo. Como 'mp3' vs 'mp4'. Isso também funcionará, embora o IMO funcione um pouco menos do que ...

  3. No próprio recurso. Muitos formatos de arquivo têm seus números de versão incorporados, normalmente no cabeçalho; isso permite que o software mais novo 'apenas funcione' entendendo todas as versões existentes do tipo de arquivo, enquanto o software mais antigo pode funcionar se uma versão não suportada (mais recente) for especificada. No contexto de uma API REST, isso significa que seus URIs nunca precisam ser alterados, 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 "varredura limpa" ou para alterações importantes na versão onde você deseja essa abordagem.
  2. se você quiser que o cliente saiba antes de executar um PUT / POST se vai funcionar ou não.
  3. se estiver tudo bem se o cliente tiver que fazer o PUT / POST para descobrir se ele vai funcionar.
pjz
fonte
8

A versão da API REST é análoga à versão de qualquer outra API. Pequenas alterações podem ser feitas, grandes mudanças podem exigir uma API totalmente nova. O mais fácil para você é começar do zero toda vez, ou seja, colocar a versão no URL faz mais sentido. Se você deseja facilitar a vida do cliente, tente manter a compatibilidade com versões anteriores, o que pode ser feito com a descontinuação (redirecionamento permanente), recursos em várias versões etc. Isso é mais complicado e exige mais esforço. Mas é também o que o REST incentiva em "URIs legais não mudam".

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

Alexander Torstling
fonte