Como criar versões de URIs REST

111

Qual é a melhor maneira de criar versão de URIs REST? Atualmente, temos uma versão # no próprio URI, ou seja.

http://example.com/users/v4/1234/

para a versão 4 desta representação.

A versão pertence ao queryString? ie.

http://example.com/users/1234?version=4

Ou o versionamento é melhor realizado de outra maneira?

Mike Pone
fonte

Respostas:

34

Eu diria que torná-lo parte do próprio URI (opção 1) é melhor porque a v4 identifica um recurso diferente da v3. Parâmetros de consulta, como na segunda opção, podem ser mais bem usados ​​para passar informações adicionais (consulta) relacionadas à solicitação , em vez do recurso .

Zef Hemel
fonte
11
A questão é: é um RECURSO diferente que estamos discutindo? Ou uma representação diferente desse recurso? O REST faz distinção entre a representação e o recurso?
Cheeso
1
@Cheeso - O OP indica que é uma representação diferente ao invés de um recurso diferente, daí minha resposta.
Greg Beech
Isso foi respondido com mais detalhes antes aqui stackoverflow.com/q/389169/104261
Taras Alenin
+1 para "Parâmetros de consulta como em sua segunda opção podem ser mais bem usados ​​para passar informações adicionais (consulta) relacionadas à solicitação, em vez do recurso"
andy
Para representações diferentes, acho que você deve usar cabeçalhos como "Aceitar", então o cliente pode especificar para o servidor "Aceito apenas a versão 4" e o servidor pode responder com essa representação. Se nenhum aceite for enviado, a última versão será fornecida.
Carlos Verdes
190

Não crie versões de URLs, porque ...

  • você quebra os permalinks
  • As alterações de url se espalharão como uma doença por meio de sua interface. O que você faz com representações que não mudaram, mas apontam para a representação que mudou? Se você alterar o url, você quebrará clientes antigos. Se você deixar o url, seus novos clientes podem não funcionar.
  • O controle de versões de tipos de mídia é uma solução muito mais flexível.

Supondo que seu recurso esteja retornando alguma variante de application / vnd.yourcompany.user + xml, tudo o que você precisa fazer é criar suporte para um novo tipo de mídia application / vnd.yourcompany.userV2 + xml e através da magia da negociação de conteúdo seu v1 e Os clientes v2 podem coexistir pacificamente.

Em uma interface RESTful, a coisa mais próxima que você tem de um contrato é a definição dos tipos de mídia que são trocados entre o cliente e o servidor.

Os URLs que o cliente usa para interagir com o servidor devem ser fornecidos pelo servidor embutido nas representações recuperadas anteriormente. O único URL que precisa ser conhecido pelo cliente é o URL raiz da interface. Adicionar números de versão a urls só tem valor se você construir urls no cliente, o que não deve ser feito com uma interface RESTful.

Se você precisar fazer uma mudança em seus tipos de mídia que irão quebrar seus clientes existentes, crie um novo e deixe seus urls em paz!

E para aqueles leitores que estão dizendo que isso não faz sentido se eu estiver usando application / xml e application / json como tipos de mídia. Como devemos fazer a versão deles? Você não é. Esses tipos de mídia são praticamente inúteis para uma interface RESTful, a menos que você os analise usando download de código, ponto no qual o controle de versão é um ponto discutível.

Darrel Miller
fonte
66
Para abordar os pontos de bala. 1. você não quebra os links permanentes, porque os links permanentes são vinculados a uma versão específica 2. Se tudo for versionado, isso não é um problema. URLs antigos ainda podem funcionar. Idealmente, você não gostaria que um URL da versão 4 retornasse uma associação a um recurso da versão 3. 3. Talvez
Mike Pone
10
Imagine se quando você atualizasse para uma nova versão de um navegador da web, todos os seus favoritos marcados quebrassem! Lembre-se de que, conceitualmente, o usuário está salvando um link para um recurso, não para uma versão da representação de um recurso.
Darrel Miller
11
@Gili Para satisfazer o requisito de uma api REST ser autodescritiva, é necessário que o cabeçalho do tipo de conteúdo forneça a descrição semântica completa da mensagem. Em outras palavras, seu tipo de mídia é seu contrato de dados. Se você entregar application / xml ou application / json, não estará dizendo ao cliente nada sobre o que está contido naquele XML / Json. No instante em que um aplicativo cliente alcança uma retirada / Cliente / Nome, você está criando um acoplamento baseado em informações que não estão na mensagem. Eliminar o acoplamento out-of-band é fundamental para alcançar RESTfulness.
Darrel Miller
6
@Gili O cliente não deve ter conhecimento prévio dos URLs da API além do URL raiz. Você não deve vincular formatos de representação a URLs específicos. Quando se trata de escolher os tipos de mídia, você realmente precisa escolher entre um formato específico como application / vnd.mycompany.myformat + xml ou um padronizado como XHtml, Atom, RDF, etc.
Darrel Miller
4
Faz sentido colocar a versão da API em um campo de cabeçalho separado? Assim: Aceitar: application / com.example.myapp + json; versão = 1.0
Erik
21

Ah, estou colocando meu velho chapéu mal-humorado de novo.

Do ponto de vista do ReST, isso não importa. Não é uma salsicha.

O cliente recebe um URI que deseja seguir e o trata como uma string opaca. Coloque o que quiser nele, o cliente não tem conhecimento de algo como um identificador de versão nele.

O que o cliente sabe é que ele pode processar o tipo de mídia, e aconselho seguir o conselho de Darrel. Também, pessoalmente, sinto que a necessidade de alterar o formato usado em uma arquitetura repousante 4 vezes deve trazer enormes e massivos sinais de aviso de que você está fazendo algo seriamente errado e ignorando completamente a necessidade de projetar seu tipo de mídia para resiliência à mudança.

Mas de qualquer maneira, o cliente só pode processar um documento com um formato que ele possa entender e seguir os links nele. Ele deve saber sobre os relacionamentos de link (as transições). Portanto, o que está no URI é completamente irrelevante.

Eu pessoalmente votaria em http: // localhost / 3f3405d5-5984-4683-bf26-aca186d21c04

Um identificador perfeitamente válido que evitará que qualquer outro desenvolvedor de cliente ou pessoa toque no sistema para questionar se deve colocar v4 no início ou no final de um URI (e eu sugiro que, da perspectiva do servidor, você não deveria ter 4 versões, mas 4 tipos de mídia).

SerialSeb
fonte
E se a representação precisar mudar significativamente e não for compatível com versões anteriores?
Mike Pone
1
Ao projetar seu tipo de mídia de forma extensível, como usando namespaces e um xsd extensível, ou formatos xml existentes ike atom, isso deve ser evitável. Se você realmente precisar, outro tipo de mídia é o caminho a percorrer.
SerialSeb
1
Eu gosto dessa resposta completamente válida, mas acho que o URI proposto é mais para demonstrar o ponto do que para um cenário real no qual você deseja URIs 'hackáveis'.
Dave Van den Eynde
10

Você NÃO deve colocar a versão no URL, você deve colocar a versão no cabeçalho de aceitação da solicitação - veja minha postagem neste tópico:

Práticas recomendadas para controle de versão de API?

Se você começar a colar versões no URL, acabará com URLs bobos como este: http://company.com/api/v3.0/customer/123/v2.0/orders/4321/

E há vários outros problemas que também aparecem - veja meu blog: http://thereisnorightway.blogspot.com/2011/02/versioning-and-types-in-resthttp-api.html

jeremyh
fonte
11
Desculpe, mas não acho que você termine com URLs idiotas como este. Você está associando os números de versão a um recurso específico ou (pior) a uma representação específica. Isso seria bobagem, IMO. Em vez disso, você está controlando a versão da API, portanto, nunca terá mais de uma versão no URI.
silly4jesus
3

Existem 4 abordagens diferentes para o controle de versão da API:

  • Adicionando versão ao caminho URI:

    http://example.com/api/v1/foo
    
    http://example.com/api/v2/foo
    

    Quando houver alterações significativas, você deve incrementar a versão como: v1, v2, v3 ...

    Você pode implementar um controlador em seu código como este:

    @RestController
    public class FooVersioningController {
    
    @GetMapping("v1/foo")
    public FooV1 fooV1() {
        return new FooV1("firstname lastname");
    }
    
    @GetMapping("v2/foo")
    public FooV2 fooV2() {
        return new FooV2(new Name("firstname", "lastname"));
    }
    
  • Solicitar versão do parâmetro:

    http://example.com/api/v2/foo/param?version=1
    http://example.com/api/v2/foo/param?version=2
    

    O parâmetro de versão pode ser opcional ou obrigatório, dependendo de como você deseja que a API seja usada.

    A implementação pode ser semelhante a esta:

    @GetMapping(value = "/foo/param", params = "version=1")
    public FooV1 paramV1() {
        return new FooV1("firstname lastname");
    }
    
    @GetMapping(value = "/foo/param", params = "version=2")
    public FooV2 paramV2() {
        return new FooV2(new Name("firstname", "lastname"));
    }
    
  • Passando um cabeçalho personalizado:

    http://localhost:8080/foo/produces
    

    Com cabeçalho:

    headers[Accept=application/vnd.company.app-v1+json]
    

    ou:

    headers[Accept=application/vnd.company.app-v2+json]
    

    A maior vantagem desse esquema é principalmente semântica: você não está sobrecarregando o URI com nada a ver com o controle de versão.

    Implementação possível:

    @GetMapping(value = "/foo/produces", produces = "application/vnd.company.app-v1+json")
    public FooV1 producesV1() {
        return new FooV1("firstname lastname");
    }
    
    @GetMapping(value = "/foo/produces", produces = "application/vnd.company.app-v2+json")
    public FooV2 producesV2() {
        return new FooV2(new Name("firstname", "lastname"));
    }
    
  • Alterar nomes de host ou usar gateways de API:

    Basicamente, você está movendo a API de um nome de host para outro. Você pode até mesmo chamar isso de construção de uma nova API para os mesmos recursos.

    Além disso, você pode fazer isso usando API Gateways.

Javier C.
fonte
2

Se os serviços REST exigirem autenticação antes do uso, você poderá associar facilmente a chave / token da API a uma versão da API e fazer o roteamento internamente. Para usar uma nova versão da API, pode ser necessária uma nova chave de API, vinculada a essa versão.

Infelizmente, esta solução só funciona para APIs baseadas em autenticação. No entanto, ele mantém versões fora dos URIs.

UberSteve
fonte
2

Eu queria criar APIs com versão e achei este artigo muito útil:

http://blog.steveklabnik.com/posts/2011-07-03-nobody-understands-rest-or-http

Há uma pequena seção sobre "Quero que minha API seja versionada". Achei simples e fácil de entender. O ponto crucial é usar o campo Aceitar no cabeçalho para passar informações de versão.

Asma Zubair
fonte
1

Eu incluiria a versão como um valor opcional no final do URI. Pode ser um sufixo como / V4 ou um parâmetro de consulta como você descreveu. Você pode até redirecionar o / V4 para o parâmetro de consulta para oferecer suporte a ambas as variações.

Paul Morgan
fonte
1

Se você usar URIs para controle de versão, o número da versão deve estar no URI da raiz da API, para que cada identificador de recurso possa incluí-lo.

Tecnicamente, uma API REST não é interrompida por alterações de URL (o resultado da restrição de interface uniforme). Ele é interrompido apenas quando a semântica relacionada (por exemplo, um vocabulário RDF específico da API) muda de uma maneira não compatível com versões anteriores (raro). Atualmente muitos ppl não usam links para navegação (restrição HATEOAS) e vocabs para anotar suas respostas REST (restrição de mensagem autodescritiva) é por isso que seus clientes quebram.

Tipos MIME personalizados e controle de versão de tipo MIME não ajudam, porque colocar os metadados relacionados e a estrutura da representação em uma string curta não funciona. Claro. os metadados e a estrutura mudam com frequência, e assim o número da versão também ...

Portanto, para responder à sua pergunta, a melhor maneira de anotar seus pedidos e respostas com vocabs ( Hydra , dados vinculados ) e esquecer o versionamento ou usá-lo apenas para alterações de vocabulário não compatíveis com versões anteriores (por exemplo, se você quiser substituir um vocabulário por outro).

inf3rno
fonte
0

Eu voto por fazer isso em tipo MIME, mas não em URL. Mas o motivo não é o mesmo dos outros caras.

Acho que o URL deve ser exclusivo (exceto aqueles redirecionamentos) para localizar o recurso exclusivo. Então, se você aceita /v2.0em URLs, por que não é /ver2.0ou /v2/ou /v2.0.0? Ou mesmo -alphae -beta? (então torna-se totalmente o conceito de sempre )

Portanto, a versão em tipo mime é mais aceitável do que a URL.

Yarco
fonte