Tuesday, 29 May 2018

Estratégia de versão da web api


Formas de versão da sua API, parte 2.
Esta é a segunda parte de uma série sobre o versionamento de API. A Parte Um cobriu os métodos predominantes para versionar sua API e permitir que os clientes codificassem contra uma versão específica. Eu sugiro que você leia esse post (pelo menos dê uma olhada na introdução!) Antes deste, pois ele fornece algum contexto necessário. Nele, insinuei que outro método que utiliza alguns conceitos pouco conhecidos da especificação do cabeçalho Accepts pode permitir que você crie uma API sem versão. Eu vou admitir na frente, no entanto, é um pouco errado, mas eu acho que ainda é importante. Você ainda pode atualizar as coisas na API, mas não está fazendo o versionamento da interface em si. Você está meramente controlando as representações de recursos.
A alteração mais comum em qualquer API arquitetada REST é adicionar, alterar ou remover campos dos recursos. Novas informações são adicionadas ou podem estar em um formato diferente. Informações podem se tornar indisponíveis. Você também pode adicionar, mover ou remover recursos. Dependendo da arquitetura da API, os verbos HTTP podem significar coisas ligeiramente diferentes. Tudo isso normalmente exigiria um incremento de versão da API se eles causassem a quebra de aplicativos existentes. Com um pouco de previsão, no entanto, sua API pode mudar sem exigir uma estratégia de versão de API inteira.
Arquiteto de uma interface sem versão:
Use esquemas de URI sane: / api / collection_name / resource_id Siga as definições dos métodos HTTP A inclusão de recursos deve ser sempre compatível com versões anteriores A remoção de recursos teria que ser feita entre versões de qualquer maneira Os recursos móveis podem ser manipulados por códigos HTTP de redirecionamento.
Representações de recurso de versão:
Usar o cabeçalho Aceitar A adição de informações deve sempre ser compatível com versões anteriores A remoção de informações teria que ser feita em versões de qualquer maneira. A movimentação de informações pode ser feita com a opção Aceitar o controle de versão.
Arquitetando uma API sem versão.
Deixe-me começar dizendo que reconheço que uma API sem versão é um pouco ideal, e uma API com uma estratégia de versão bem pensada é preferível a uma API com uma tentativa mal pensada de não usar a versão. Não há vergonha em dizer que você acha que precisa de versionamento, nem todas as APIs possíveis podem se encaixar nessa estratégia sem versão. Ele realmente funciona apenas para APIs baseadas em REST-architected, manipulação de recursos que seguem os padrões vigentes. Dito isso, uma grande quantidade de APIs pode ser retrabalhada para se encaixar neste estilo. Além disso, isso é destinado a APIs de back-end, e não a APIs que fazem interface com seu front-end da web. Na minha opinião, essas devem ser quase sempre APIs separadas, para que você possa atualizar a funcionalidade de front-end da web no seu próprio ritmo, sem se preocupar com os usuários da API. Eu sei que, pessoalmente, sou muito mais negligente na minha sequência de padrões para APIs de front-end.
O primeiro passo para entender como criar uma API sem versão é saber que você não quer fazer a versão da própria versão. Isso significa que você não deseja alterar como e onde as informações são armazenadas e acessadas. Em termos práticos, esta é a estrutura da URI. Até mesmo essa regra pode ser distorcida, é claro - embutida na especificação HTTP são redirecionamentos! Se você moveu informações, basta fornecer um redirecionamento permanente para o novo URI. Agora, o que acontece se você remover um recurso?
Há dois motivos pelos quais um determinado URI pode ser removido de uma API típica: o próprio tipo de recurso foi removido ou, mais comumente, essa representação particular do recurso não é mais suportada. Neste último caso, você não está realmente aderindo à arquitetura REST: A representação de recursos deve ser dissociada da URI. No primeiro caso, se o tipo de recurso inteiro não estiver mais acessível, provavelmente é por um motivo comercial, e nesse caso você também o removerá de todas as versões anteriores da API. Nesse caso, o controle de versão não lhe dava nada, não é mesmo?
Adicionar recursos ou informações a recursos deve sempre ser uma alteração compatível com versões anteriores. Se você estiver usando o JSON, adicionar elementos aos recursos existentes deve ser fácil. Mesmo se você tiver um esquema XML, basta adicioná-los ao esquema definido e alertar os clientes que podem ter baixado sua própria cópia do esquema para atualizá-lo. Se você quiser adicionar um recurso totalmente novo, basta adicioná-lo. Ninguém deve ser afetado.
Provavelmente, a coisa mais difícil de fazer sem versão é qualquer alteração no que acontece quando você executa um método específico em uma API específica. Por exemplo, se você costumava POSTAR para / api / foo / e criar um novo recurso foo para você, mas agora deseja que ele edite um recurso foo existente. O melhor que posso lhe dizer é que não siga as definições do método de especificação HTTP recomendadas explicitamente:
GET / api / foo / ou GET / api / foo / 1 recupera recursos ou coleções de recursos e é idempotente (repetitivo com os mesmos resultados) POST / api / foo / cria novos recursos e NÃO é idempotente (a repetição cria mais recursos) PUT / api / foo / 1 atualiza ou cria um recurso específico inteiro e é idempotente PATCH / api / foo / 1 (padrão proposto) atualiza campos de um recurso específico e é idempotente DELETE / api / foo / 1 exclui um recurso (às vezes, coleções de recursos) e é idempotente.
Tecnicamente, acho que DELETE deveria notificar de alguma forma se o recurso não existia em primeiro lugar e uma maneira correta de fazer isso seria um erro, mas eu vou adiar isso. OPÇÕES e CABEÇA são usadas menos e se você as estiver usando, você provavelmente saberá o que você está fazendo de qualquer maneira. Se você estiver usando o PATCH, saiba que ele não é um padrão bem suportado e muitas APIs aceitam solicitações PUT incompletas e atualizam apenas os campos alterados. Acho que isso é bom, desde que seja um comportamento bem documentado e bem documentado, dado um suporte PATCH irregular, pelo menos até que seja mais amplamente aceitável.
Muitas vezes, os recursos são modificados pelas solicitações POST. Um formulário é enviado, você interpreta os dados e altera um recurso. Esse é um padrão comum em APIs de front-end e, como mencionei acima, tudo bem. Não é perfeito, mas está bem. Em uma API de back-end, isso não deveria acontecer! Use uma solicitação PUT ou PATCH e defina explicitamente o que você deseja que o novo recurso seja. Uma desculpa comum é que as versões antigas do IE não suportam PUT ou PATCH, mas esta é uma API de back-end, está bem! Todas as grandes bibliotecas que conheço, pelo menos, suportam o PUT - se você estiver usando um que não o faça, provavelmente deve procurar em outro lugar.
Em suma, um pré-requisito para o versionlessness é que todos os recursos que você tem devem poder ser acessados ​​e manipulados em uma fasion consistente, com as únicas diretivas sendo o URI para o recurso, o método HTTP e os dados que representam o próprio recurso. Se você estiver manipulando um único recurso lógico - digamos, um perfil de usuário - de vários URIs, provavelmente encontrará situações em que precisa de uma versão.
Representações de recursos de controle de versão.
Como mencionei na introdução, as próprias representações de recursos transferidas para o cliente podem e provavelmente devem ser versionadas. A beleza dessa abordagem é que cada recurso pode ser versionado de forma independente. Se você alterar um recurso e, em seguida, um mês depois, decidir alterar um recurso diferente, não será necessário incrementar um contador de versões da API duas vezes. Cada versão de recurso é incrementada individualmente. Observe que não estou falando sobre a versão dos recursos em si, apenas a representação do recurso. Se você tiver um recurso com versão, por exemplo, um documento que tenha revisões anteriores disponíveis, elas devem ser acessadas separadamente do que o método que estou descrevendo aqui.
Familiarizar-se com a especificação do cabeçalho Accept provavelmente ajudará você a entender a verdadeira profundidade de quão longe as especificações podem ir para a proteção do futuro. Quase todo mundo sabe que o cabeçalho Accepts especifica que tipo de MIME-Type o solicitante espera, como application / json. Poucos sabem que ele pode não apenas especificar um tipo, mas também especificar vários tipos aceitáveis, bem como parâmetros em cada tipo. Para representar representações de recursos de versão, vamos aproveitar os parâmetros de tipo.
Eu pulo e considero o seguinte:
Mesmo sem entender completamente o cabeçalho Aceita, você provavelmente pode adivinhar que essa cadeia implica que ele espera o tipo application / vnd. example. foo no formato json, versão 2, se disponível, e qualquer versão, caso contrário. Analisar o cabeçalho Aceita de maneira consistente com a especificação pode ser difícil somente porque muitas bibliotecas não o analisam imediatamente.
Então, quando você deve representar representações de recursos? Como mencionei acima, se você estiver removendo informações sobre um recurso, provavelmente é por motivo comercial que você não quer mais que ele seja exposto. Na idade atual de transmissão compactada, há pouco ganho para remover informações simplesmente porque você não acha que é mais útil. A adição de informações deve sempre ser possível de ser feita de maneira compatível com versões anteriores. Você pode querer a versão em casos de alterar o nome e / ou o tipo de um campo, por exemplo, se você quiser (o exemplo do mundo real!) Redirecionar um campo booleano rotulado & # 8220; ativado & # 8221; para um mais genérico & # 8220; status & # 8221; tipo enum.
Agora, como faço isso?
Infelizmente, muito do que eu tenho discutido aqui tem pouco ou nenhum apoio amplo na comunidade de pessoas que estão construindo APIs amplamente usadas. Eu suspeito que nenhuma pequena parte disso seja devido à dificuldade de implementá-las em um aplicativo do mundo real. Dependendo da sua plataforma, pode ser fácil ou difícil, e algumas bibliotecas suportarão uma estratégia de controle de versões como essa. O mais próximo que eu sei é se o node-restify, que suporta roteamento versionado baseado em um cabeçalho de versão aceita.
Eu vou estar passando por algumas bibliotecas e tentar estendê-las para dar suporte a versões no futuro. Possivelmente tentando minha própria biblioteca que faz muito disso de graça. Quanto mais fácil para um desenvolvedor escrever código compatível com os padrões, mais provável será adotá-lo, porque no final se trata de facilidade de desenvolvimento versus adesão aos padrões, acho que todos nós sabemos que a facilidade vencerá toda vez .

Seu versionamento de API está errado, e é por isso que decidi fazer isso de três formas diferentes e erradas.
No final, decidi que a maneira mais justa e equilibrada era irritar todo mundo igualmente. É claro que estou falando sobre versionamento de APIs e não desde as ótimas guias # x201C versus espaços & # x201D; debate tenho visto tantas crenças fortes em campos totalmente diferentes.
Isso foi ótimo. Quando eu construí Eu fui pwned? (HIBP) no final de novembro, foi concebido para ser um serviço simples e rápido que algumas pessoas usariam. Eu acho que é justo dizer que os dois primeiros pontos foram alcançados, mas não o último. Não era um "número um", na verdade, no final da primeira semana, era mais do que o Google Analytics poderia suportar. O ponto é que você não pode sempre prever o futuro quando você escreve sua API e em algum momento você pode precisar mudar algo que as pessoas já dependem.
Mas aqui está o problema & # x2013; toda vez que você começa a falar sobre qualquer coisa relacionada a APIs via HTTP, isso acontece:
Todo caminho que você vira, há diferentes filosoficos sobre o caminho certo & # x201D; e muito para trás e para frente no REST, o que é RESTful, o que não é e se é importante. Vamos falar sobre as alterações da API, o impacto sobre as versões, por que há tantas idéias divergentes sobre como isso deve ser feito e, por fim, por que nenhuma das brincadeiras é tão importante quanto realmente fazer as coisas.
Puxando mais dados de violação.
Tendo em mente que a mesma API é usada para o recurso de pesquisa no site e agora também por terceiros criando tudo, de aplicativos de smartphone a ferramentas de teste de penetração, a resposta acima funcionou bem no começo, mas foi limitada. Por exemplo, esta resposta não funciona tão bem:
Por quê? Porque & # x201C; BattlefieldHeroes & # x201D; é o Pascal-cased que é ótimo para combinar com classes CSS codificadas (embora provavelmente não seja uma boa abordagem a longo prazo) e por ter um & # x201C; stable & # x201D; nome para se referir a (eu não vou alterá-lo, mesmo que haja uma segunda violação), mas não é adequado para exibição como um título. Tudo isso sai do Armazenamento de Tabelas do Azure e eu entro no SQL Azure para extrair dados relacionais que realmente descrevem a violação. Um dos atributos nesse armazenamento relacional é o nome que você vê acima.
O que eu realmente queria fazer era algo mais assim:
Pegue? Para o ponto anterior sobre o nome da violação, que ainda está lá no atributo name, mas agora temos um título também. Isto é o que você mostra para as pessoas & # x2013; & # x201C; Battlefield Heroes & # x201D; & # x2013; mas mais importante, se o Gawker for penhorado novamente, posso nomear a violação de algo como Gawker2014 e o título pode ser algo amigável ao longo das linhas de Gawker (Ataque Eletrônico do Exército Sírio) & # x201D ;. Ele segmenta o que é estável e previsível daquilo que não é e significa que as pessoas podem criar dependências, como imagens ou outros recursos, no atributo name.
Os outros dados devem ser bem claros: a data da violação, quando foi adicionada ao sistema, o número de contas pwned, a descrição da violação (novamente, isso pode mudar se o palavreado precisar ser ajustado) e & # x201C; DataClasses & # x201D ;. Uma das coisas que muitas pessoas estavam pedindo era uma descrição do que estava comprometido na brecha, então agora há um monte de atributos que podem ser adicionados através de uma coleção sobre a violação em si. Eu já estou mostrando isso abaixo de cada violação na página de sites da Pwned (essa é outra razão pela qual eu posso agora ajustar algumas das descrições).
Esta é uma mudança urgente. Enquanto o sentimento da API é o mesmo & # x2013; forneça um nome de conta, receba de volta uma lista de violações & # x2013; não há mais uma matriz de strings de nomes de violações. Se eu simplesmente substituísse a API antiga por essa, as coisas iriam quebrar. APIs. Devo. Evoluir.
O software evolui, as APIs devem ser versionadas.
Vamos ser sinceros sobre isso: o mundo segue em frente. A API para o HIBP durou cerca de 2 meses, não porque foi mal projetada, mas porque o serviço se tornou insano e inesperadamente bem-sucedido. Eu gosto desse tipo de problema, e você também deveria.
Agora eu tive uma escolha; ou eu poderia me contentar com o que eu tinha e privar as pessoas de uma maneira melhor, eu poderia adicionar ao serviço existente de uma forma não violenta ou eu poderia criar uma nova versão (embora expondo a mesma entidade de uma maneira diferente) e construir É a melhor maneira de saber como; sem bytes desnecessários, modelados corretamente (até que eu decida que uma nova versão é mais correta) e uma boa representação da entidade que eu estou finalmente tentando entrar em consumidores & # x2019; apps.
Não há nada de errado em introduzir uma nova versão de uma API quando é a coisa mais sensata a ser feita. Por todos os meios, faça o seu melhor para obtê-lo & # x201C; direita & # x201D; desde o primeiro dia, mas fazê-lo com a expectativa de que "certo" & # x201D; é um estado temporário. É por isso que precisamos estar aptos para a versão.
Os vários campos de versionamento.
Certo, então quão difícil pode ser esse negócio de versionamento? Quero dizer, deve ser um exercício simples, certo? O problema é que isso é muito filosófico, mas em vez de ficar atolado nisso por enquanto, deixe-me delinear as três escolas comuns de pensamento em termos de como elas são praticamente implementadas:
URL: basta digitar a versão da API no URL, por exemplo: haveibeenpwned / api / v2 / breachedaccount / foo Cabeçalho de solicitação personalizada: você usa o mesmo URL de antes, mas adiciona um cabeçalho como & # x201C; api-version: 2 & # x201D; Aceitar cabeçalho: você modifica o cabeçalho de aceitação para especificar a versão, por exemplo, & # x201C; Accept: application / vnd. haveibeenpwned. v2 + json & # x201D;
Tem havido muitas, muitas coisas escritas sobre isso e eu vou linkar para eles no final do post, mas aqui está a versão abreviada:
Os URLs são uma droga porque devem representar a entidade: na verdade, eu concordo com isso na medida em que a entidade que estou recuperando é uma conta violada, não uma versão da conta violada. Semanticamente, não é realmente correto, mas é fácil de usar! Cabeçalhos de pedidos personalizados são uma droga porque não é realmente uma forma semântica de descrever o recurso: A especificação HTTP nos dá um meio de solicitar a natureza que gostaríamos do recurso representado por meio do cabeçalho de aceitação, por que reproduzir? esta? Aceitar cabeçalhos chupados porque são mais difíceis de testar: não posso mais apenas fornecer a alguém um URL e dizer: "##201C; aqui, clique em" & # x201D ;, em vez disso, eles devem construir cuidadosamente a solicitação e configurar o cabeçalho de aceitação adequadamente .
Os vários argumentos a favor e contra cada abordagem tendem a ir de & # x201C; este é o & # x2018; certo & # x2019; maneira de fazer isso, mas é menos prático & # x201D; através de & # x201C; Esta é a maneira mais fácil de criar algo consumível que, portanto, faz com que seja "# & # x2018; right & # x2019; & # x201D ;. Há muita discussão sobre hipermídia, negociação de conteúdo, o que é & nbsp; REST & # x201D; e todo tipo de outras questões. Infelizmente, isso muitas vezes é filosófico e perde a visão de qual deve ser o objetivo real: criar software que funcione e particularmente para uma API, tornando-a facilmente consumível.
É sobre ter um contrato estável, estúpido!
Mais importante do que todas as reclamações e delírios sobre como fazer isso dessa maneira ou daquela maneira é dar estabilidade às pessoas. Se eles investem seu esforço suado escrevendo código para consumir sua API, então é melhor que você não a interrompa mais adiante.
Honestamente, os debates sobre o que é o & # X201C; RESTful & # x201D; contra o que não é como se o próprio termo ditasse seu sucesso é apenas louco. Transforme essa discussão em "Aqui estão as razões práticas pelas quais isso faz sentido, e é isso que pode acontecer se você não o fizer", e eu farei de tudo. O problema é que até mesmo as vozes da razão dentro das discussões barulhentas deixam dúvidas quanto ao que realmente é a melhor abordagem e, portanto, eu alcancei um compromisso & # x2026;
Aqui estão 3 maneiras erradas de consumir a API de HIBP que você pode escolher agora.
Ok, agora que estamos claramente estabelecidos, mas você está errado, eu gostaria de dar a você a opção de escolher qualquer uma das três formas erradas. Espere & # x2013; o que?! É assim: no entanto, eu implemento a API, ela será muito difícil de consumir, muito acadêmica, muito provavelmente falha no proxy ou algo do tipo. Em vez de escolher um caminho errado, decidi dar-lhe todas as 3 formas erradas e pode escolher aquele que é o menos errado para você.
Caminho errado 2 - cabeçalho de solicitação personalizada:
Inevitavelmente, alguém vai me dizer que fornecer 3 maneiras erradas é a coisa errada a fazer. Não significaria mais código kludge para manter? Não, isso significa simplesmente que a implementação da API da Web subjacente é decorada com dois atributos:
O primeiro é simplesmente uma restrição de roteamento que implementa o RouteFactoryAttribute. Eu passo na rota e passo a versão que pode mapear para aquela rota, então a implementação procura a presença de um & # x201C; api-version & # x201D; cabeçalho ou um cabeçalho de aceitação correspondente a esse padrão:
Se a versão especificada em uma dessas combina com a especificada na restrição de roteamento, então, é o método que será invocado. Esta é uma adaptação simples desta amostra no CodePlex.
O segundo atributo que decora o método GetV2 acima é cortesia da Web API 2 e do recurso de roteamento de atributos. É claro que sempre poderíamos fazer roteamento na API da Web, mas isso geralmente era definido globalmente. O roteamento de atributos como esse traz a definição de rota para o contexto em que ela é aplicada e facilita a visualização da ação do controlador que será chamada por qual rota. Isso também significa que as implementações de todas as três formas erradas de chamar a API estão reunidas em um único local.
Então, em suma, não, isso não cria um monte de kludge e é muito fácil de manter. Cada uma das 3 abordagens retornará exatamente o mesmo resultado e, o mais importante, elas permanecerão estáveis ​​e não serão alteradas de nenhuma maneira e, no final das contas, será a mais importante, independentemente de qual opção você escolher. Toda a implementação agora também está claramente documentada na página da API do site.
Mas e se você não especificar uma versão?
Você sabe o pouco onde eu disse que você não pode quebrar o que já está lá fora? Sim, isso significa que se você fizer o que faz agora, # x2013; não especifique uma versão & # x2013; então você começa o que você recebe agora. Em outras palavras, nenhum pedido para uma versão específica significa que você obtém a versão 1.
Estou bem com isso, independentemente de ter atingido este ponto por padrão. Eu sei que algumas pessoas sempre gostam de retornar a versão mais recente se um número não for especificado, mas IMHO que quebra todo o contrato estável & # x201; & # x201D; objetivo; o que você obtém da API hoje pode ser completamente diferente do que você recebe amanhã se eu revisá-lo. Isso seria uma droga e quebraria as coisas.
Você tem 3 opções, mas minha preferência pessoal é & # x2026;
Eu tenho o luxo de controlar tanto a API quanto o consumidor primário do site da HIBP. Dado que eu forneci 3 opções para consumir a API, qual delas eu mesmo uso?
Eu fui com o favorito filosófico que é especificá-lo através do cabeçalho de aceitação. Eu não acho que isso é certo e os outros estão errados, ao invés disso eu acho que isso faz mais sentido por duas razões principais:
Concordo que o URL não deve mudar: se concordarmos que o URL representa o recurso, a menos que estejamos tentando representar versões diferentes do próprio recurso, não, não acredito que o URL deva mudar. As brechas para foo são sempre as brechas para foo e eu não acho que só porque eu mudo os dados retornados para foo que a localização de foo deve mudar. Concordo que os cabeçalhos de aceitação descrevem como você deseja os dados: Esta é uma semântica da especificação de HTTP e assim como a semântica dos verbos de solicitação faz muito sentido (isto é, estamos obtendo ou colocando ou excluindo ou postando), O mesmo acontece com a maneira como o cliente gostaria que o conteúdo fosse representado.
De maneira nenhuma isso significa que eu acho que os outros dois estão errados e, francamente, não há melhor maneira de compartilhar a API com alguém do que dizer: "Aqui, clique aqui", mas quando eu puder facilmente construir o pedido e gerenciar os cabeçalhos, eu fui com esta rota.
Na verdade, pensando nisso, eu também uso a versão na rota do domínio. Por quê? Apenas através do processo de escrever esta API eu estava constantemente me comunicando com as pessoas sobre as formas de consultá-las (mais sobre isso mais tarde) e os atributos que ela retorna. Ser capaz de passar por um e-mail e dizer "Ei, aqui está o que eu estou pensando" ##201D; e eles simplesmente clicam e obtêm resultados é inestimável. Este é o ponto que os proponentes da abordagem de versionamento de URLs fazem com toda a razão: você simplesmente não pode fazer isso quando você está dependente de cabeçalhos.
Ah, e no caso de você estar me checando, no momento em que escrevo, eu ainda não rolei o site para a v2 da API. Agora que os dados de violação são extraídos na API quando ocorre uma pesquisa, isso significa que eu tenho o luxo de não carregar todas as violações na origem na carga inicial (isso nunca será sustentável à medida que o conjunto de dados se expande). Isso salvará um monte de tráfego de saída e acelerará as coisas para as pessoas em termos de obter o site carregado, mas isso também significa um pouco mais de trabalho do meu jeito. Fique ligado.
No fechamento.
Claramente, eu tenho sido um pouco irônico aqui com relação a tudo estar errado, mas honestamente, quanto mais você lê sobre isso e quanto mais perguntas você faz, mais errado todo caminho parece de uma maneira ou de outra. Na verdade, eu sei muito bem que existem aspectos da minha implementação que serão referidos como "errados" e "x201D". (Eu posso pensar em pelo menos um par) e, naturalmente, eu estou me preparando para o potencial ataque de feedback para esse efeito. A coisa é, porém, cada uma dessas opções funciona e, francamente, para todos os efeitos práticos, eles funcionam tão bem quanto os outros.
Se eu puder deixar outras pessoas pensando em como atualizar suas APIs com um pensamento final: ninguém usará sua API até que você a tenha criado. Pare de procrastinar. Nenhuma dessas opções é "ruim", "# x201C; ruim & # x201D; em qualquer sentido tangível, eles são apenas diferentes. Eles são todos facilmente consumíveis, todos eles retornam o mesmo resultado e nenhum deles é susceptível de ter qualquer impacto real sobre o sucesso do seu projeto.
Referências.
Stack Overflow: Práticas recomendadas para o versionamento de API? (ótima pergunta, ótimas respostas, fechada como "não construtiva", eu assumo porque "Bill the Lizard & # x201D; saiu do lado errado da cama naquela manhã) Blog do Lexical Scope: How are REST APIs com versão? (boa comparação de práticas de controle de versão entre serviços, ainda que alguns anos atrás) CodePlex: Exemplo de restrição de roteamento (vinculado na página da API da Web da Microsoft como um exemplo de APIs de controle de versão adicionando um cabeçalho personalizado) CodeBetter: Versionamento RESTful Serviços (muito pragmáticos e uma boa descrição das várias maneiras pelas quais uma API pode mudar) Blog de Vinay Sahni: Práticas recomendadas para projetar uma API RESTful pragmática (ele está argumentando sobre o versionamento de URL por causa de & # x201C ; explorabilidade do navegador & # x201D;) Lans Pivotal: versionamento da API (boa visão das opiniões conflitantes existentes) Web Stack of Love: Versões da API da Web ASP com Tipos de Mídia (bom passo-a-passo para criar um aplicativo para dar suporte a versões por negociação de conteúdo)
Oi, sou Troy Hunt, escrevo este blog, crio cursos para a Pluralsight e sou diretor regional da Microsoft e MVP que viaja pelo mundo falando em eventos e treinando profissionais de tecnologia.
Oi, sou Troy Hunt, escrevo este blog, crio cursos para a Pluralsight e sou diretor regional da Microsoft e MVP que viaja pelo mundo falando em eventos e treinando profissionais de tecnologia.
Próximos eventos.
Eu normalmente faço workshops particulares em torno destes, eis os próximos eventos públicos em que estarei:
Não tem a Pluralsight? Que tal um teste gratuito de 10 dias? Isso vai te dar acesso a milhares de cursos, dentre os quais dezenas de meus incluem:
“O Cloud Never Goes Down”, os SLAs do Azure e outras trivialidades de disponibilidade.
Veja como Bell foi hackeado - injeção de SQL, golpe a golpe.
Inscreva-se agora!
Copyright 2018, caça de Troy.
Esta obra está licenciada sob uma licença Creative Commons Atribuição 4.0 Internacional. Em outras palavras, compartilhe generosamente, mas forneça atribuição.
Aviso Legal.
As opiniões expressas aqui são minhas e podem não refletir as das pessoas com quem trabalho, meus amigos, minha esposa, as crianças etc. A menos que eu esteja citando alguém, elas são apenas minhas opiniões.
Publicado com o Ghost.
Este site funciona inteiramente no Ghost e é possível graças ao seu apoio gentil. Leia mais sobre porque eu escolhi usar o Ghost.

Versioning & para;
Uma boa API é versionada: alterações e novos recursos são implementados em novas versões da API, em vez de alterar continuamente apenas uma versão. Ao contrário dos aplicativos da Web, com os quais você tem controle total do código do lado do cliente e do lado do servidor, as APIs devem ser usadas por clientes além do seu controle. Por esse motivo, a compatibilidade com versões anteriores (BC) das APIs deve ser mantida sempre que possível. Se uma alteração que pode quebrar o BC for necessária, você deverá introduzi-la na nova versão da API e aumentar o número da versão. Os clientes existentes podem continuar usando a versão antiga e funcional da API. e os clientes novos ou atualizados podem obter a nova funcionalidade na nova versão da API.
Dica: consulte Versão semântica para obter mais informações sobre como criar números de versão da API.
Uma maneira comum de implementar o controle de versão da API é incorporar o número da versão nas URLs da API. Por exemplo, example / v1 / users representa o terminal / users da versão 1 da API.
Outro método de versionamento de API, que ganhou força recentemente, é colocar o número da versão nos cabeçalhos de solicitação HTTP. Isso geralmente é feito através do cabeçalho Accept:
Ambos os métodos têm seus prós e contras, e há muitos debates sobre cada abordagem. Abaixo, você verá uma estratégia prática para o versionamento de APIs, que é uma combinação desses dois métodos:
Coloque cada versão principal da implementação da API em um módulo separado cujo ID é o número da versão principal (por exemplo, v1, v2). Naturalmente, os URLs da API contêm números de versão principais. Dentro de cada versão principal (e, portanto, dentro do módulo correspondente), use o cabeçalho de solicitação Accept HTTP para determinar o número da versão secundária e escrever o código condicional para responder às versões secundárias de acordo.
Para cada módulo que atende uma versão principal, o módulo deve incluir as classes de recurso e controlador que atendem a essa versão específica. Para separar melhor a responsabilidade do código, você pode manter um conjunto comum de classes de recurso e controlador de base e subclassá-las em cada módulo de versão individual. Dentro das subclasses, implemente o código concreto, como Model :: fields ().
Seu código pode ser organizado da seguinte forma:
Sua configuração de aplicativo seria semelhante a:
Como resultado do código acima, o exemplo / v1 / users retornará a lista de usuários na versão 1, enquanto o exemplo / v2 / users retornará os usuários da versão 2.
Graças aos módulos, o código para diferentes versões principais pode ser bem isolado. Mas os módulos tornam ainda possível reutilizar o código nos módulos através de classes base comuns e outros recursos compartilhados.
Para lidar com números de versão secundários, você pode aproveitar o recurso de negociação de conteúdo fornecido pelo comportamento contentNegotiator. O comportamento contentNegotiator definirá a propriedade yii \ web \ Response :: $ acceptParams quando determinar qual tipo de conteúdo será suportado.
Por exemplo, se uma solicitação for enviada com o cabeçalho HTTP Accept: application / json; version = v1, após a negociação do conteúdo, yii \ web \ Response :: $ acceptParams conterá o valor ['version' = & gt; 'v1'].
Com base nas informações de versão em acceptParams, você pode escrever código condicional em locais como ações, classes de recursos, serializadores, etc. para fornecer a funcionalidade apropriada.
Como as versões secundárias, por definição, exigem a manutenção da compatibilidade com versões anteriores, esperamos que não haja muitas verificações de versão no seu código. Caso contrário, é provável que você precise criar uma nova versão principal.
Página gerada em Sex, 23 fev 2018 21:01:58 +0100.

Culbertson Exchange.
Discussões sobre tópicos de interesse e desenvolvimento de aplicativos & # 8230;
API da Web do Versioning ASP.
Uma das coisas importantes a considerar ao criar uma API é uma estratégia para controlar a versão da API para gerenciar mudanças. Existem várias razões pelas quais isso é importante:
Para suportar usuários (desenvolvedores) da API que estão usando uma versão existente para não forçar a quebra de alterações neles. Para impedir a quebra de versões existentes dos aplicativos clientes que estão usando uma versão existente da API.
Previous versions of the API will normally be maintained for either a period to allow developers and client applications to upgrade or indefinitely.
Strategies for Versioning.
There are multiple ways to indicate the version of the API:
Specify the version as part of the URI address or URL query parameters, e. g. Specify the version as part of the Accept request header, for example: Specify the version as a special request header, for example:
There are pros and cons of each approach, and have been extensively discussed on the Internet with religious fervor. I recently attended a nice talk by Michael Pratt on how to version or not version your API (be sure to use down arrows for pros and cons for each strategy). The best approach is to pick a strategy that best fits your use cases and to stick with it for consistency.
Adding Versioning to ASP Web API.
There are no versioning capabilities provided by the ASP Web API out of the box. However there is a nice Nuget package called SDammann. WebApi. Versioning by Sebastiaan Dammann that extends the Web API with a versioning framework. The package is flexible in that it allows the API developer to choose one of the strategies mentioned above for the API versioning. This article will show how to set up the framework on a project.
Setting Up the Project.
The first step is to get the WebApi. Versioning from Nuget by bringing up the Package Manager Console and getting the Nuget package:
In this example we are going to use the the URI address strategy to indicate the versioning.
The next step is to indicate in the Global. Asax. cs\Application_Start method to use the versioning package for the controller selector and to provide the strategy to use for the controller selection:
In this case the new RouteVersionControllerSelector(…) indicates to the package which strategy to use (in this case selecting by routes in the URI). For that to work the default route (and likewise any custom routes) must be modified to include the version in App_Start\WebApiConfig. cs :
The integer variable after the “v” will be utilized by the package to determine which controller to use based on the value. For example “v1” will indicate to use the controllers that are in the namespace Appname. Controllers. Version1 .
Managing Controllers to Avoid Duplication.
One of the downsides of this approach is that it could cause a lot of code duplication if the entire controllers are copied to a new namespace each time a version is added. One approach to reduce besides keeping the controllers “skinny” is to add a base class for each controller and implement overrides for the version where needed. For example the Controllers folder would look something like this:
The base controllers would contain the initial implementation for version 1 with virtual methods:
Then for the Version1 implementation it would not need to override any methods but only provide services such as dependency injection and to expose the methods to the Version1 class:
Then for Version2 we can override the methods that will be affected by a change and ignore the other methods that will be inherited from the base class. For example say we need to add a new Boolean property to the Project model called “IsUrgent”. We don’t want it to affect Version1 users but we do want it to be mandatory for Version2. We can add this property to the model as a nullable Boolean property and not add any Required attributes so as not to disturb Version1 usage:
This change will affect the “Post” and “Put” verbs for the Projects controller and we will need to override those methods. For example to override the “Post” method and to ensure the required flag is present we can do the following:
Conclusão.
The SDammann. WebApi. Versioning package provides an easy and flexible framework for versioning ASP Web API. It covers the most common strategies to version a web API and could be extended to support less common methods as it is open source.
In the next blog post I will discuss strategies for testing the versioning of the Web API.

Web api versioning strategy


Are there any known how-tos or best practices for web service REST API versioning?
I have noticed that AWS does versioning by the URL of the endpoint. Is this the only way or are there other ways to accomplish the same goal? If there are multiple ways, what are the merits of each way?
closed as primarily opinion-based by templatetypedef, amon, George Stocker ♦ Mar 6 '14 at 13:22.
Many good questions generate some degree of opinion based on expert experience, but answers to this question will tend to be almost entirely based on opinions, rather than facts, references, or specific expertise. Se essa questão puder ser reformulada para se ajustar às regras da Central de Ajuda, edite a pergunta.
locked by animuson ♦ Mar 8 '16 at 3:40.
Esta questão existe porque tem significado histórico, mas não é considerada uma boa pergunta sobre o tema para este site, então, por favor, não a use como prova de que você pode fazer perguntas semelhantes aqui. Esta questão e suas respostas estão congeladas e não podem ser alteradas. Mais informações: centro de ajuda.
This is a good and a tricky question. The topic of URI design is at the same time the most prominent part of a REST API and , therefore, a potentially long-term commitment towards the users of that API .
Since evolution of an application and, to a lesser extent, its API is a fact of life and that it's even similar to the evolution of a seemingly complex product like a programming language, the URI design should have less natural constraints and it should be preserved over time . The longer the application's and API's lifespan, the greater the commitment to the users of the application and API.
On the other hand, another fact of life is that it is hard to foresee all the resources and their aspects that would be consumed through the API. Luckily, it is not necessary to design the entire API which will be used until Apocalypse. It is sufficient to correctly define all the resource end-points and the addressing scheme of every resource and resource instance.
Over time you may need to add new resources and new attributes to each particular resource, but the method that API users follow to access a particular resources should not change once a resource addressing scheme becomes public and therefore final.
This method applies to HTTP verb semantics (e. g. PUT should always update/replace) and HTTP status codes that are supported in earlier API versions (they should continue to work so that API clients that have worked without human intervention should be able to continue to work like that).
Furthermore, since embedding of API version into the URI would disrupt the concept of hypermedia as the engine of application state (stated in Roy T. Fieldings PhD dissertation) by having a resource address/URI that would change over time, I would conclude that API versions should not be kept in resource URIs for a long time meaning that resource URIs that API users can depend on should be permalinks .
Sure, it is possible to embed API version in base URI but only for reasonable and restricted uses like debugging a API client that works with the the new API version. Such versioned APIs should be time-limited and available to limited groups of API users (like during closed betas) only. Otherwise, you commit yourself where you shouldn't.
A couple of thoughts regarding maintenance of API versions that have expiration date on them. All programming platforms/languages commonly used to implement web services (Java, , PHP, Perl, Rails, etc.) allow easy binding of web service end-point(s) to a base URI. This way it's easy to gather and keep a collection of files/classes/methods separate across different API versions .
From the API users POV, it's also easier to work with and bind to a particular API version when it's this obvious but only for limited time, i. e. during development.
From the API maintainer's POV, it's easier to maintain different API versions in parallel by using source control systems that predominantly work on files as the smallest unit of (source code) versioning.
However, with API versions clearly visible in URI there's a caveat: one might also object this approach since API history becomes visible/aparent in the URI design and therefore is prone to changes over time which goes against the guidelines of REST. Concordo!
The way to go around this reasonable objection, is to implement the latest API version under versionless API base URI. In this case, API client developers can choose to either:
develop against the latest one (committing themselves to maintain the application protecting it from eventual API changes that might break their badly designed API client ).
bind to a specific version of the API (which becomes apparent) but only for a limited time.
For example, if API v3.0 is the latest API version, the following two should be aliases (i. e. behave identically to all API requests):
In addition, API clients that still try to point to the old API should be informed to use the latest previous API version, if the API version they're using is obsolete or not supported anymore . So accessing any of the obsolete URIs like these:
should return any of the 30x HTTP status codes that indicate redirection that are used in conjunction with Location HTTP header that redirects to the appropriate version of resource URI which remain to be this one:
There are at least two redirection HTTP status codes that are appropriate for API versioning scenarios:
301 Moved permanently indicating that the resource with a requested URI is moved permanently to another URI (which should be a resource instance permalink that does not contain API version info). This status code can be used to indicate an obsolete/unsupported API version, informing API client that a versioned resource URI been replaced by a resource permalink .
302 Found indicating that the requested resource temporarily is located at another location, while requested URI may still supported. This status code may be useful when the version-less URIs are temporarily unavailable and that a request should be repeated using the redirection address (e. g. pointing to the URI with APi version embedded) and we want to tell clients to keep using it (i. e. the permalinks).
The URL should NOT contain the versions. The version has nothing to do with "idea" of the resource you are requesting. You should try to think of the URL as being a path to the concept you would like - not how you want the item returned. The version dictates the representation of the object, not the concept of the object. As other posters have said, you should be specifying the format (including version) in the request header.
If you look at the full HTTP request for the URLs which have versions, it looks like this:
The header contains the line which contains the representation you are asking for ("Accept: application/xml"). That is where the version should go. Everyone seems to gloss over the fact that you may want the same thing in different formats and that the client should be able ask for what it wants. In the above example, the client is asking for ANY XML representation of the resource - not really the true representation of what it wants. The server could, in theory, return something completely unrelated to the request as long as it was XML and it would have to be parsed to realize it is wrong.
A better way is:
Further, lets say the clients think the XML is too verbose and now they want JSON instead. In the other examples you would have to have a new URL for the same customer, so you would end up with:
(or something similar). When in fact, every HTTP requests contains the format you are looking for:
Using this method, you have much more freedom in design and are actually adhering to the original idea of REST. You can change versions without disrupting clients, or incrementally change clients as the APIs are changed. If you choose to stop supporting a representation, you can respond to the requests with HTTP status code or custom codes. The client can also verify the response is in the correct format, and validate the XML.
One last example to show how putting the version in the URL is bad. Lets say you want some piece of information inside the object, and you have versioned your various objects (customers are v3.0, orders are v2.0, and shipto object is v4.2). Here is the nasty URL you must supply in the client:
We found it practical and useful to put the version in the URL. It makes it easy to tell what you're using at a glance. We do alias /foo to /foo/(latest versions) for ease of use, shorter / cleaner URLs, etc, as the accepted answer suggests.
Keeping backwards compatibility forever is often cost-prohibitive and/or very difficult. We prefer to give advanced notice of deprecation, redirects like suggested here, docs, and other mechanisms.
I agree that versioning the resource representation better follows the REST approach. but, one big problem with custom MIME types (or MIME types that append a version parameter) is the poor support to write to Accept and Content-Type headers in HTML and JavaScript.
For example, it is not possible IMO to POST with the following headers in HTML5 forms, in order to create a resource:
This is because the HTML5 enctype attribute is an enumeration, therefore anything other than the usual application/x-www-formurlencoded , multipart/form-data and text/plain are invalid.
. nor am I sure it is supported across all browsers in HTML4 (which has a more lax encytpe attribute, but would be a browser implementation issue as to whether the MIME type was forwarded)
Because of this I now feel the most appropriate way to version is via the URI, but I accept that it is not the 'correct' way.
Put your version in the URI. One version of an API will not always support the types from another, so the argument that resources are merely migrated from one version to another is just plain wrong. It's not the same as switching format from XML to JSON. The types may not exist, or they may have changed semantically.
Versions are part of the resource address. You're routing from one API to another. It's not RESTful to hide addressing in the header.
There are a few places you can do versioning in a REST API:
As noted, in the URI. This can be tractable and even esthetically pleasing if redirects and the like are used well.
In the Accepts: header, so the version is in the filetype. Like 'mp3' vs 'mp4'. This will also work, though IMO it works a bit less nicely than.
In the resource itself. Many file formats have their version numbers embedded in them, typically in the header; this allows newer software to 'just work' by understanding all existing versions of the filetype while older software can punt if an unsupported (newer) version is specified. In the context of a REST API, it means that your URIs never have to change, just your response to the particular version of data you were handed.
I can see reasons to use all three approaches:
if you like doing 'clean sweep' new APIs, or for major version changes where you want such an approach. if you want the client to know before it does a PUT/POST whether it's going to work or not. if it's okay if the client has to do its PUT/POST to find out if it's going to work.
Versioning your REST API is analogous to the versioning of any other API. Minor changes can be done in place, major changes might require a whole new API. The easiest for you is to start from scratch every time, which is when putting the version in the URL makes most sense. If you want to make life easier for the client you try to maintain backwards compatibility, which you can do with deprecation (permanent redirect), resources in several versions etc. This is more fiddly and requires more effort. But it's also what REST encourages in "Cool URIs don't change".
In the end it's just like any other API design. Weigh effort against client convenience. Consider adopting semantic versioning for your API, which makes it clear for your clients how backwards compatible your new version is.

REST APIs don’t need a versioning strategy – they need a change strategy.
Change in an API is inevitable as your knowledge and experience of a system improves. Managing the impact of this change can be quite a challenge when it threatens to break existing client integrations.
Developers often try to decide on a versioning strategy as soon as they start work on an API. This is understandable but it’s not always the smartest way of looking at the problem of managing change. Brandon Byers summed this up by borrowing Jamie Zawinski’s dig at regular expressions:
Some people, when confronted with a problem, think “I know, I’ll use versioning.” Now they have 2.1.0 problems.
How can you version resources in REST?
REST doesn’t provide for any specific versioning but the more commonly used approaches fall into three camps: putting it on the URI, using a custom request header or a adding it to the HTTP Accept header.
Using the URI is the most straightforward approach though it does upset REST advocates who insist that a URI should refer to a unique resource. You are also guaranteed to break client integrations when a version is updated no matter how heavily you invest in creative routing and client communication.
A custom header allows you to preserve your URIs between versions though it is effectively a duplicate of the content negotiation behaviour implemented by the existing Accept header. A client can use this header to send a list of supported versions while the server responds with the version used in the Content-Type header.
Content negotiation may let you to preserve a clean set of URLs but you still have to deal with the complexity of serving different versions of content somewhere . This burden tends to be moved up the stack to your API controllers which become responsible for figuring out which version of a resource to send. The end result tends to be a more complex API as clients have to know which headers to specify before requesting a resource.
The version number isn’t the problem.
Given the contentious nature of REST, you’ll always be wrong in somebody’s eyes no matter what approach you take. The point is that version numbering itself is a red herring.
The real challenge here is in managing a code base that can serve up multiple versions of resources. If you keep all versions in the same code base then older versions become vulnerable to unexpected changes. If you separate the code bases then the operational and support overhead escalates. In both cases, code bloat and increased complexity are an inevitable consequence.
A strict approach to versioning does give you much-needed certainty over the contract but it does tend to undermine a system’s capacity to change . Versioning can become a barrier to improvement as any requirements that lead to version changes are resisted. I have seen APIs with strict versioning policies stuck on the same version for years due to legitimate concerns over the amount of work and risk involved in change.
What’s the alternative to versioning?
A coherent version strategy should address how you will manage change in your API whilst providing a stable contract to clients. This doesn’t have to include issuing new versions in response to changes.
One approach is to build in the possibility of change by making provision for backwards compatibility in API changes. This approach does carry significant risk as you cannot be sure that a change will not break existing clients even with exhaustive regression testing.
You can even take backwards compatibility a step further by adding features such as optional parameters and wildcard properties that anticipate future changes. This kind of “ forwards compatibility ” tends to produce a coarse contract that places a considerable burden of validation onto the client. The end result is often a messy set of switches and codes required for each call.
Bertrand Meyer’s Open\Closed principle suggests that software entities should be “open for extension, but closed for modification”. When applied to APIs the implication is that you can augment your resources but not change them.
This approach could offer the certainty of stricter versioning without the regression risks involved in backwards compatibility. Augmentation is not without its problems though as it can give rise to bloated contracts. Without careful discipline an API can become littered with duplicate methods or resources that provide several slightly different ways of achieving the same thing.
Can you share the responsibility?
You could do more to share the burden of change between API and client. Postel’s law, often referred to as the Robustness principle, states that you should be “liberal in what you accept and conservative in what you send”. In terms of APIs this implies a certain tolerance in consuming services.
For example, strict serialization techniques can be unnecessarily intolerant of change. A more tolerant reader should only be concerned with data that it needs and ignore every other part of the response. This means that the majority of changes are unlikely to break the integration.
Another approach could be for the consumer to declare the data they are interested in as part of a request. This consumer-driven contract pattern does not specify the form that these consumer assertions should take, but an implementation could allow an API to detect when a request is out of date.
Unfortunately, these approaches can only be applied to relatively closed communities of services. Public-facing APIs rarely have the luxury of being able to dictate the style of client integration. The only enforceable contract you have between service and client is made up of the data and protocol.
This is why careful discipline is at the heart of any sensible change strategy. A good API doesn’t come into being by accident. It has to be curated . Whatever approach you take to managing change you will need consistent and active governance over the evolving contract.
I am a London-based technical architect who has spent more than twenty years leading development across start-ups, digital agencies, software houses and corporates. Over the years I have built a lot of stuff including web sites and services, multi-screen applications, systems integrations and middleware.
My current focus is on enabling scalable SaaS delivery and providing architectural leadership in agile environments. I currently work for SaaS provider Fourth leading them to enterprise heaven, one service at a time.
You can follow me on Twitter or check me out on LinkedIn.
The poor man’s Cassandra: scaling Azure Table Storage to maximise throughput.
Azure Table Storage can be tuned for high throughput workloads, but you’ll probably be faced with some difficult trade-offs.
Managing and throttling serverless scaling with Azure Functions.
The serverless promise of unlimited scale-out can be a curse when your downstream processes and data stores have strict limits on throughput. With Azure Functions your options for mitigating this are limited, though the new durable functions may provide an answer…
Entity services: when microservices are worse than monoliths.
Finely-grained, entity-based services seem to be advocated by some pretty authoritative sources. This is unfortunate as they are something of an anti-pattern that can undermine many of the benefits of decomposing an monolith into micoservices.
Forget code coverage – test design should be driven by behaviours.
Test coverage statistics are much loved by management teams and code quality tools. They tend to associate a high level of coverage with robust, well-managed code bases. Wrongly, as it turns out.
Events, sagas and workflows: managing long-running processes between services.
An event-driven architecture can give rise to complex chains of events that are difficult to manage. These problems can be mitigated through careful design rather than resorting to shared state databases or workflow engines.
Technical debt is an overused and lazy metaphor.
Technical debt may be a useful metaphor for describing how bad code design undermines productivity to non-technical audiences, but it does not help in understanding the longer term problems that affect code bases.
How can Domain Driven Design help with large scale agile development?
Agile teams spend time modelling software whether they are prepared to admit it or not. Adopting a technique like Domain Driven Design can help to make this more efficient, particularly at scale.
Running a Core console application as a Windows Service.
Although Core does not directly support creating Windows Services there are several different ways of creating applications that can be registered and run as services.

No comments:

Post a Comment