Versionando uma API REST
1. O problema
Evolving a REST API é um problema difícil - um para o qual muitas opções estão disponíveis. Este artigo discute algumas dessas opções.
Leitura adicional:
Tutorial de inicialização do Spring - Bootstrap uma aplicação simples
É assim que você começa a entender o Spring Boot.
Explorando o Spring Boot TestRestTemplate
Aprenda a usar o novo TestRestTemplate no Spring Boot para testar uma API simples.
API REST com Jersey e Spring
Criando serviços da Web repousantes usando Jersey 2 e Spring.
2. O que está no contrato?
Antes de mais nada, precisamos responder a uma pergunta simples:What is the Contract between the API and the Client?
2.1. URIs fazem parte do contrato?
Vamos primeiro considerarthe URI structure of the REST API - isso faz parte do contrato? Os clientes devem marcar, codificar permanentemente e geralmente confiar nos URIs da API?
Nesse caso, a interação do Cliente com o Serviço REST não seria mais orientada pelo Serviço em si, mas por quais informaçõesRoy Fielding callsout-of-band:
Uma API REST deve ser inserida sem conhecimento prévio além do URI inicial (marcador) e conjunto de tipos de mídia padronizados que são apropriados para o público-alvo ... A falha aqui implica que informações fora de banda estão gerando interação em vez de hipertexto.
Então, claramenteURIs are not part of the contract! O cliente deve conhecer apenas um único URI - o ponto de entrada da API. Todos os outros URIs devem ser descobertos ao consumir a API.
2.2. Tipos de mídia parte do contrato?
E as informações do Tipo de mídia usadas para as representações de Recursos - fazem parte do contrato entre o Cliente e o Serviço?
In order to successfully consume the API, the Client must have prior knowledge of these Media Types. De fato, a definição desses tipos de mídia representa todo o contrato.
Portanto, é aqui que o serviço REST deve se concentrar mais:
Uma API REST deve gastar quase todo o seu esforço descritivo na definição dos tipos de mídia usados para representar recursos e direcionar o estado do aplicativo, ou na definição de nomes de relações estendidas e / ou marcação ativada por hipertexto para os tipos de mídia padrão existentes.
Portanto osMedia Type definitions are part of the contracte devem ser de conhecimento prévio para o cliente que consome a API. É aqui que entra a padronização.
Agora temos uma boa ideia do que é o contrato, vamos prosseguir para como realmente resolver o problema de versão.
3. Opções de alto nível
Vamos agora discutir as abordagens de alto nível para o controle de versão da API REST:
-
URI Versioning - versão o espaço URI usando indicadores de versão
-
Media Type Versioning - versão da representação do recurso
When we introduce the version in the URI space, the Representations of Resources are considered immutable. Portanto, quando mudanças precisam ser introduzidas na API, um novo espaço de URI precisa ser criado.
Por exemplo, digamos que uma API publique os seguintes recursos - usuários e privilégios:
http://host/v1/users
http://host/v1/privileges
Agora, vamos considerar que uma alteração significativa na APIusers requer a introdução de uma segunda versão:
http://host/v2/users
http://host/v2/privileges
When we version the Media Type and extend the language, we go through Content Negotiation based on this header. A API REST usariavendor MIME media types personalizado em vez de tipos de mídia genéricos, comoapplication/json. Vamos criar uma versão desses tipos de mídia em vez dos URIs.
Por exemplo:
===>
GET /users/3 HTTP/1.1
Accept: application/vnd.myname.v1+json
<===
HTTP/1.1 200 OK
Content-Type: application/vnd.myname.v1+json
{
"user": {
"name": "John Smith"
}
}
Podemos verificar este‘Custom Media Types for Rest APIs' article para mais informações e exemplos sobre este assunto.
O que é importante entender aqui é quethe client makes no assumptions about the structure of the response além do que está definido no tipo de mídia.
É por isso que tipos de mídia genéricos não são ideais. Essesdo not provide enough semantic information e forçam o cliente a usar exigem dicas adicionais para processar a representação real do recurso.
Uma exceção a isso é usar outra maneira de identificar exclusivamente a semântica do conteúdo - como um esquema XML.
4. Vantagens e desvantagens
Agora que temos um conceito claro do que faz parte do Contrato entre o Cliente e o Serviço, bem como uma visão geral de alto nível das opções de versão da API, vamos discutir as vantagens e desvantagens de cada abordagem.
Em primeiro lugar,introducing version identifiers in the URI leads to a very large URI footprint. Isso se deve ao fato de que qualquer alteração significativa em qualquer uma das APIs publicadas introduzirá uma nova árvore de representações para toda a API. Com o tempo, isso se torna um fardo para manter e também um problema para o cliente - que agora tem mais opções para escolher.
Version identifiers in the URI ARE also severely inflexible. Não há como simplesmente evoluir a API de um único Recurso ou um pequeno subconjunto da API geral.
Como mencionamos anteriormente, essa é uma abordagem de tudo ou nada. Se parte da API for movida para a nova versão, a API inteira precisará seguir com ela. Isso também torna a atualização dos clientes da v1 para a v2 uma tarefa importante - o que leva a atualizações mais lentas e períodos de inatividade muito mais longos para as versões antigas.
Cache HTTP também é uma grande preocupação quando se trata de controle de versão.
Da perspectiva dos caches de proxy no meio, cada abordagem tem vantagens e desvantagens. Se o URI for versionado, o cache precisará manter várias cópias de cada Recurso - uma para cada versão da API. Isso coloca uma carga no cache e diminui a taxa de acertos do cache, pois diferentes clientes usarão versões diferentes.
Além disso, alguns mecanismos de invalidação de cache não funcionarão mais. Se o tipo de mídia for aquele com versão, o cliente e o serviço precisam oferecer suporte aVary HTTP header para indicar que há várias versões sendo armazenadas em cache.
No entanto, a partir deperspective of client caching, a solução que modifica o tipo de mídia envolve um pouco mais de trabalho do que aquela em que os URIs contêm o identificador de versão. Isso ocorre porque é simplesmente mais fácil armazenar algo em cache quando sua chave é um URL do que um tipo de mídia.
Vamos terminar esta seção definindo alguns objetivos (direto deAPI Evolution):
-
manter alterações compatíveis fora dos nomes
-
evitar novas versões principais
-
faz alterações compatíveis com versões anteriores
-
pense em compatibilidade com versões anteriores
5. Possíveis mudanças na API
A seguir, vamos considerar os tipos de mudanças na API REST - eles são apresentados aqui:
-
mudanças no formato de representação
-
mudanças de recursos
5.1. Adicionando à representação de um recurso
A documentação do formato do tipo de mídia deve ser projetada com a compatibilidade avançada em mente. Especificamente, um cliente deve ignorar informações que não entende (que JSON faz melhor do que XML).
Agora,adding information in the Representation of a resource will not break existing clients if these are correctly implemented.
Para continuar nosso exemplo anterior, adicionaramount na representação deuser não será uma alteração significativa:
{
"user": {
"name": "John Smith",
"amount": "300"
}
}
5.2. Removendo ou Alterando uma Representação Existente
Removing, renaming or generally restructuring information in the design of existing representations is a breaking change for clients. Isso ocorre porque eles já entendem e contam com o formato antigo.
É aqui que entra a Negociação de Conteúdo. Para tais mudanças,we can add a new vendor MIME media type.
Vamos continuar com o exemplo anterior. Digamos que queremos quebrar oname deuser emfirstnameelastname:
===>
GET /users/3 HTTP/1.1
Accept: application/vnd.myname.v2+json
<===
HTTP/1.1 200 OK
Content-Type: application/vnd.myname.v2+json
{
"user": {
"firstname": "John",
"lastname": "Smith",
"amount": "300"
}
}
Como tal, isso representa uma alteração incompatível para o Cliente - que precisará solicitar a nova Representação e entender a nova semântica. No entanto, o espaço do URI permanecerá estável e não será afetado.
5.3. Principais Mudanças Semânticas
These are changes in the meaning of the Resources, the relations between them or what the map to in the backend. Esse tipo de mudança pode exigir um novo tipo de mídia ou pode exigir a publicação de um novo recurso irmão próximo ao antigo e fazer uso de links para apontá-lo.
Embora isso pareça usar identificadores de versão no URI novamente, a distinção importante é que o novo Resourceis published independently of any other Resources in the APIe não bifurcará a API inteira na raiz.
The REST API should adhere to the HATEOAS constraint. De acordo com isso, a maioria dos URIs devem ser DESCOBERTOS pelos clientes, não codificados. Alterar esse URI não deve ser considerado uma alteração incompatível. O novo URI pode substituir o antigo e os Clientes poderão descobrir novamente o URI e ainda funcionar.
É importante notar, no entanto, que, embora o uso de identificadores de versão no URI seja problemático por todos esses motivos,it is not un-RESTful de qualquer forma.
6. Conclusão
Este artigo tentou fornecer uma visão geral do problema muito diverso e difícil deevolving a REST Service. Discutimos as duas soluções comuns, vantagens e desvantagens de cada uma, e maneiras de argumentar sobre essas abordagens no contexto do REST.
O artigo conclui defendendo a segunda solução -versioning the media types enquanto examina as possíveis alterações em uma API RESTful.
A implementação completa deste tutorial pode ser encontrada emGitHub project.
7. Leitura adicional
Geralmente, esses recursos de leitura são vinculados ao longo do artigo, mas, neste caso, existem simplesmente muitos bons: