Estratégia de versão de descanso de 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 sempre ser compatível com versões anteriores A remoção de recursos deve ser feita através de 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 nesse 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 todo o tipo de recurso 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 (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 .
Estratégia de versão de descanso de api
Estamos desenvolvendo APIs internas para integrar a lógica do lado do servidor (backend) com frontend (web, mobile etc). Nós temos uma pilha java em nosso backend e o front end é codificado em reagir e reagir nativo. Como ambos são desenvolvidos pela nossa organização, como fornecedores de API, temos controle total sobre os consumidores da API. Qualquer alteração na API poderia ser sincronizada com a alteração nos clientes da API.
Existem cenários / casos de uso em que precisamos pensar em APIs internas / privadas de controle de versão? Precisamos pensar em uma estratégia de versionamento?
Você deve ter uma estratégia de versionamento porque isso é essencial para a evolução independente, mas deve estar vinculada ao Content-Type, não às URLs ou a qualquer outra coisa.
Mesmo em um ambiente fechado, você ainda deve se esforçar para tornar todos os componentes de um sistema independentemente evoluídos (isolados, modularizados) - especialmente um sistema distribuído como um baseado em cliente / servidor. Isso permite que diferentes equipes trabalhem em cada um no seu próprio ritmo e permite diferentes cadências de lançamento.
Por que não em URLs?
URIs identificam coisas abstratas que não podem ser versionadas, como um usuário "Andy" ou uma fatura. Uma representação dessa coisa terá uma serialização específica, que pode ser versionada, application / andys-api-v1 + json.
Sua API (como em qualquer site) é definida por três coisas. Estas são as únicas coisas que você precisa documentar se sua API for RESTful:
A URL raiz O (s) tipo (s) de conteúdo das representações as relações de link entre URIs.
Se um cliente v1 obtiver um link para / users / andy de uma solicitação anterior, ele poderá encaminhar para um cliente v2, que poderá fazer uma solicitação para a mesma URL para obter dados sobre a mesma Coisa, mas em um idioma (conteúdo - tipo) ele pode falar, application / andys-api-v2 + json.
Os clientes v1 e v2 podem ser partes diferentes do mesmo programa, no meio de um ciclo de desenvolvimento. A chave é que os clientes continuem trabalhando por toda parte.
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 muitos 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 tentando finalmente entrar nos 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: construir um 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 formas 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 três abordagens retornará exatamente o mesmo resultado e, o mais importante, elas permanecerão estáveis e não serão alteradas de forma alguma e, no final das contas, será a mais importante. 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 & # x201D; # 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;
Tenho o luxo de controlar tanto a API quanto o principal consumidor dela, sendo o próprio site de 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 contrário, 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, 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" ################################################################### 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 incluindo 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) Pilha Web de Amor: ASP. NET Web API Versioning com Tipos de Mídia (bom passo-a-passo de criar um aplicativo para suportar versionamento 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:
Estratégia de versão de descanso para api
Esta é uma boa e uma pergunta complicada. O tópico de design de URI é ao mesmo tempo a parte mais proeminente de uma API REST e, portanto, um compromisso potencialmente de longo prazo com os usuários dessa API.
Como a evolução de um aplicativo e, em menor extensão, de sua API é um fato da vida e é até similar à evolução de um produto aparentemente complexo como uma linguagem de programação, o design de URI deve ter menos restrições naturais e deve ser preservado ao longo do tempo . Quanto maior o tempo de vida do aplicativo e da API, maior o comprometimento com os usuários do aplicativo e da API.
Por outro lado, outro fato da vida é que é difícil prever todos os recursos e seus aspectos que seriam consumidos por meio da API. Felizmente, não é necessário projetar a API inteira que será usada até o Apocalypse. É suficiente definir corretamente todos os endpoints de recursos e o esquema de endereçamento de cada recurso e instância de recurso.
Com o tempo, você pode precisar 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 ser alterado quando um esquema de endereçamento de recursos se tornar público e, portanto, final.
Este método se aplica à semântica do verbo HTTP (por exemplo, PUT deve sempre atualizar / substituir) e códigos de status HTTP que são suportados em versões anteriores da API (eles devem continuar a funcionar para que os clientes da API que trabalharam sem intervenção humana possam continuar trabalhando) Curtiu isso).
Além disso, como a incorporação da versão da API na URI interromperia o conceito de hipermídia como o mecanismo do estado do aplicativo (declarado na dissertação de doutorado de Roy T. Fieldings) por ter um endereço de recurso / URI que mudaria com o tempo, concluiria que a API As versões não devem ser mantidas em URIs de recursos por um longo tempo, o que significa que os URIs de recursos dos quais os usuários da API podem depender devem ser permalinks.
Claro, é possível incorporar a versão da API no URI de base, mas apenas para usos razoáveis e restritos, como a depuração de um cliente de API que funciona com a nova versão da API. Essas APIs com versão devem ser limitadas no tempo e estar disponíveis apenas para grupos limitados de usuários da API (como durante betas fechados). Caso contrário, você se compromete onde não deveria.
Algumas considerações sobre a manutenção de versões da API com data de expiração. Todas as plataformas / linguagens de programação comumente usadas para implementar serviços da Web (Java,.NET, PHP, Perl, Rails, etc.) permitem uma vinculação fácil do (s) ponto (s) de extremidade do serviço da Web a um URI de base. Dessa forma, é fácil reunir e manter uma coleção de arquivos / classes / métodos separados em diferentes versões da API.
A partir do POV dos usuários da API, também é mais fácil trabalhar e vincular-se a uma determinada versão da API quando isso é óbvio, mas apenas por tempo limitado, ou seja, durante o desenvolvimento.
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 .
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.
Microservice preconditions: what needs to be in place before you decompose that monolith…
One of the main benefits of microservices is that they reduce the cost of change. The problem is that you need to make a significant up-front investment to realise this saving. Your first few microservices are more likely to be an expensive and potentially painful undertaking.
relaxar. There’s no conflict between architecture and agile.
Agile teams still need to make architecture decisions, but in supporting them architects should seek “just enough” architecture over “big design up front” and practical solutions over large-scale abstract thinking.
Automating Docker image deployments using Azure Container Instances.
Azure’s Container Instances provides an easy and quick way to run Docker images without having to learn the various complexities of orchestration platforms such as Kubernetes.
Can TOGAF and Agile really go together?
On the face of it, TOGAF describes a very different world to the agile preference for working software over documentation. That doesn’t mean that TOGAF is incompatible with agile, so long as you’re prepared to adapt its numerous building blocks.
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.
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.
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.
What should a Scaled Agile “architectural runway” actually look like?
The Scaled Agile Framework talks about an “architectural runway” as the main deliverable for agile architecture, yet it’s vague on the detail of what this looks like.
Esta é uma boa e uma pergunta complicada. O tópico de design de URI é ao mesmo tempo a parte mais proeminente de uma API REST e, portanto, um compromisso potencialmente de longo prazo com os usuários dessa API.
Como a evolução de um aplicativo e, em menor extensão, de sua API é um fato da vida e é até similar à evolução de um produto aparentemente complexo como uma linguagem de programação, o design de URI deve ter menos restrições naturais e deve ser preservado ao longo do tempo . Quanto maior o tempo de vida do aplicativo e da API, maior o comprometimento com os usuários do aplicativo e da API.
Por outro lado, outro fato da vida é que é difícil prever todos os recursos e seus aspectos que seriam consumidos por meio da API. Felizmente, não é necessário projetar a API inteira que será usada até o Apocalypse. É suficiente definir corretamente todos os endpoints de recursos e o esquema de endereçamento de cada recurso e instância de recurso.
Com o tempo, você pode precisar 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 ser alterado quando um esquema de endereçamento de recursos se tornar público e, portanto, final.
Este método se aplica à semântica do verbo HTTP (por exemplo, PUT deve sempre atualizar / substituir) e códigos de status HTTP que são suportados em versões anteriores da API (eles devem continuar a funcionar para que os clientes da API que trabalharam sem intervenção humana possam continuar trabalhando) Curtiu isso).
Além disso, como a incorporação da versão da API na URI interromperia o conceito de hipermídia como o mecanismo do estado do aplicativo (declarado na dissertação de doutorado de Roy T. Fieldings) por ter um endereço de recurso / URI que mudaria com o tempo, concluiria que a API As versões não devem ser mantidas em URIs de recursos por um longo tempo, o que significa que os URIs de recursos dos quais os usuários da API podem depender devem ser permalinks.
Claro, é possível incorporar a versão da API no URI de base, mas apenas para usos razoáveis e restritos, como a depuração de um cliente de API que funciona com a nova versão da API. Essas APIs com versão devem ser limitadas no tempo e estar disponíveis apenas para grupos limitados de usuários da API (como durante betas fechados). Caso contrário, você se compromete onde não deveria.
Algumas considerações sobre a manutenção de versões da API com data de expiração. Todas as plataformas / linguagens de programação comumente usadas para implementar serviços da Web (Java,.NET, PHP, Perl, Rails, etc.) permitem uma vinculação fácil do (s) ponto (s) de extremidade do serviço da Web a um URI de base. Dessa forma, é fácil reunir e manter uma coleção de arquivos / classes / métodos separados em diferentes versões da API.
A partir do POV dos usuários da API, também é mais fácil trabalhar e vincular-se a uma determinada versão da API quando isso é óbvio, mas apenas por tempo limitado, ou seja, durante o desenvolvimento.
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 .
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.
Microservice preconditions: what needs to be in place before you decompose that monolith…
One of the main benefits of microservices is that they reduce the cost of change. The problem is that you need to make a significant up-front investment to realise this saving. Your first few microservices are more likely to be an expensive and potentially painful undertaking.
relaxar. There’s no conflict between architecture and agile.
Agile teams still need to make architecture decisions, but in supporting them architects should seek “just enough” architecture over “big design up front” and practical solutions over large-scale abstract thinking.
Automating Docker image deployments using Azure Container Instances.
Azure’s Container Instances provides an easy and quick way to run Docker images without having to learn the various complexities of orchestration platforms such as Kubernetes.
Can TOGAF and Agile really go together?
On the face of it, TOGAF describes a very different world to the agile preference for working software over documentation. That doesn’t mean that TOGAF is incompatible with agile, so long as you’re prepared to adapt its numerous building blocks.
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.
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.
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.
What should a Scaled Agile “architectural runway” actually look like?
The Scaled Agile Framework talks about an “architectural runway” as the main deliverable for agile architecture, yet it’s vague on the detail of what this looks like.
Комментарии
Отправить комментарий