REST paixão por 200



Há muito tempo que eu queria escrever este artigo. Fiquei me perguntando em que lado entrar mais corretamente. Mas, de repente, recentemente, um artigo semelhante apareceu em Habré, o que causou uma tempestade em um copo. O que mais me surpreendeu foi o fato de o artigo ter começado a ser menosprezado, embora nem mesmo declarasse algo, mas levantou a questão do uso de códigos de resposta do servidor da web no REST. O debate se intensificou. E a apoteose foi que o artigo entrou em rascunho ... kilobytes de comentários, opiniões, etc. simplesmente desapareceu. Muitos se tornaram vítimas de karmo, considere, por nada :)

Em geral, foi o destino desse artigo que me levou a escrever este. E realmente espero que seja útil e esclareça bastante.

Eu aviso que tudo escrito abaixo é uma experiência real, não um ato de equilíbrio cognitivo. E assim, eles dirigiram.

HTTP


A primeira coisa a fazer é separar muito claramente as camadas. A camada de transporte é http. Bem, na verdade REST. Isso é uma coisa fundamentalmente importante para aceitar tudo e "você mesmo" nele. Vamos falar apenas sobre http primeiro.

Eu usei o termo "camada de transporte". E eu não fiz uma reserva. A questão é que o próprio http implementa as funções de transporte de solicitações para o servidor e o conteúdo para o cliente, independentemente do TCP / IP. Sim, é baseado em TCP / IP. E parece que é necessário considerá-lo como seu transporte. Mas não. E aqui está o porquê - as conexões de soquete não são diretas, ou seja, essa não é uma conexão cliente-servidor. A solicitação e a resposta http podem percorrer um longo caminho através de uma tonelada de serviços. Eles podem ser agregados ou decompostos pelo contrário. Pode ser armazenado em cache, pode ser modificado.

I.e. a solicitação e a resposta http têm sua própria rota. E isso não depende da parte de trás nem da frente. Peço que você preste atenção especial a isso.

As rotas http não são estáticas. Eles podem ser muito complicados. Por exemplo, se um balanceador for incorporado à infraestrutura, ele poderá enviar solicitações recebidas para qualquer um dos nós de back-end. Ao mesmo tempo, o próprio back-end pode implementar sua própria estratégia para trabalhar com solicitações. Alguns deles irão diretamente para os microsserviços, outros serão processados ​​pelo próprio servidor da web, outros serão adicionados e transferidos para outra pessoa e outros serão emitidos a partir do cache etc. É assim que a Internet funciona. Nada de novo.

E aqui é importante entender - por que precisamos de códigos de resposta? O fato é que todo o modelo descrito acima toma decisões com base neles. I.e. esses são códigos que permitem tomar decisões de infraestrutura e transporte durante o roteamento http.

Por exemplo, se o balanceador encontrar um código de resposta do backup 503, ao enviar uma solicitação, ele poderá tomar isso como base para considerar que o nó está temporariamente indisponível. Observo que a resposta com o código 503 fornece um cabeçalho Retry-After. Depois de receber do cabeçalho o intervalo para pesquisas repetidas, o balanceador deixará o nó sozinho pelo período especificado e trabalhará com os disponíveis. Além disso, essas estratégias são implementadas “prontas para uso” pelos servidores da web.

Um pequeno tópico externo para aprofundar o entendimento - e se o nó responder 500? O que o balanceador deve fazer? Mudar para outro? E muitos responderão - é claro, todos os motivos 5xx para desativar um nó. E eles estarão errados. O código 500 é um código de erro inesperado. I.e. um que nunca pode acontecer novamente. E o mais importante, mudar para outro nó pode não mudar nada. I.e. simplesmente desabilitamos os nós sem o menor benefício.

No caso de 500, as estatísticas vêm em nosso auxílio. O servidor WEB local do nó pode converter o próprio nó em status indisponível com um grande número de respostas 500. Nesse caso, o balanceador que entrar em contato com este nó receberá uma resposta 503 e não tocará nele. O resultado é o mesmo, mas agora, essa solução é significativa e elimina respostas "falsas".

Mas isso não é tudo. Nessa situação, o monitoramento permitirá que os administradores se conectem à situação para manter o nó. I.e. obtemos não apenas a implementação de um serviço altamente acessível, com balanceadores, etc., mas também um processo de suporte eficaz.

E tudo isso permite que você crie códigos de resposta do servidor. Qualquer arquitetura de aplicativo WEB deve começar com o design da camada de transporte. Espero que não haja dúvida sobre isso.

REST


Farei uma pergunta retórica - o que é isso? E o que você respondeu a ele? Não darei links para provas óbvias, mas provavelmente não é exatamente o que é de fato :) Esse é apenas um estilo de ideologia. Algumas considerações sobre o tema - a melhor forma de se comunicar com as costas. E não apenas se comunique, mas se comunique na infraestrutura da WEB. I.e. baseado em http. Com todas essas "coisas úteis" sobre as quais escrevi acima. As decisões finais para implementar sua interface são sempre suas .

Você já se perguntou por que um transporte separado para o REST não foi inventado? Por exemplo, para websocket é. Sim, também começa com http, mas depois que a conexão é estabelecida, geralmente é uma música separada. Por que não fazer o mesmo para o REST?

A resposta é simples - por quê? Existe um protocolo bonito, pronto e verificado - http. Escala bem. Permite implementar serviços complexos e altamente acessíveis que podem lidar com uma carga pesada. Tudo o que é necessário é introduzir algumas regras conceituais para que os desenvolvedores se entendam.

A partir daqui segue uma conclusão simples e óbvia - tudo o que é inerente ao http é inerente ao REST. Essas são entidades inseparáveis. Não há cabeçalho REST separado, nem mesmo uma dica de que REST é REST. Para qualquer servidor REST, a solicitação é exatamente igual a qualquer outro. I.e. REST é exatamente o que temos em mente.

Códigos de resposta HTTP REST


Vamos falar sobre qual código seu servidor deve responder a uma solicitação REST? Pessoalmente, parece-me que, de todas as alternativas acima, a resposta já é óbvia, porque O REST não é diferente de qualquer outra solicitação, deve estar sujeito exatamente às mesmas regras. O código de resposta é parte integrante do REST e deve ser relevante para a essência da resposta. I.e. se o objeto não foi encontrado por solicitação, é 404, se o cliente fez uma solicitação incorreta 400, etc. Mas, na maioria das vezes, o debate não termina aí. Portanto, eu continuarei.

É possível responder a tudo com o código 200? E quem irá proibi-lo? Por favor ... código 200 é o mesmo código que os outros. É verdade que a base dessa abordagem é uma tese muito simples - meu sistema é perfeito, não possui erros. Se você é uma pessoa que pode criar esses sistemas - isso só pode ser invejado!

Mas provavelmente ... ela não é perfeita. E erros acontecem. E acontece que eles acontecem devido a circunstâncias fora do nosso controle. E aqui uma solução típica é criar seu próprio sistema de codificação de erros. Isso é ruim? Sim, isso é ruim. Isso é super ruim. Vamos descobrir o porquê.

E assim, tomando o código 200 como o único verdadeiro, assumimos a responsabilidade de desenvolver toda a camada (camada crítica) do sistema - tratamento de erros. I.e. o trabalho de muitas pessoas para desenvolver essa camada é enviado para sucata. E a construção de sua "bicicleta" começa. Mas esse mega-edifício está fadado ao fracasso.

Vamos começar com o código. Se quisermos responder a todos os 200, teremos que lidar com os erros. O método clássico é tentar construções. Cada segmento de código é agrupado com código adicional. Manipuladores que fazem algo útil. Por exemplo, eles colocam algo no log. Algo importante. Isso irá localizar o erro. E se o erro não surgisse onde era esperado? Ou se ocorreu um erro no manipulador de erros? I.e. essa estratégia no nível do código não está funcionando a priori. E no final, o intérprete ou plataforma processará seus erros. OS finalmente. A essência do bug é que você não está esperando por ele. Você não precisa escondê-lo, precisa encontrá-lo e corrigi-lo. Portanto, se o REST responder a algumas solicitações com um erro de 500, isso é normal . E mais, certo .

Vamos voltar à pergunta - por que isso está certo? Porque:

  1. O código 500 é um token de infraestrutura com base no qual o nó no qual o problema ocorre pode ser desativado;
  2. Os códigos 5xx são o que está sendo monitorado e, se esse código surgir, qualquer sistema de monitoramento notificará você imediatamente. E o serviço de suporte a tempo poderá conectar-se à solução do problema;
  3. Você não escreve código adicional. Não perca tempo precioso nisso. Não complique a arquitetura. Você não lida com problemas incomuns para você - você escreve o código do aplicativo. O que eles querem de você. Pelo que eles estão pagando?
  4. Um traço que caia por engano 500 será muito mais útil do que suas tentativas de superá-lo.
  5. Se a solicitação REST retornar o código 500, a frente já no momento do processamento da resposta saberá por qual algoritmo processá-la. Além disso, a essência da questão não muda de maneira alguma, você não recebeu nada sensato de 200 e 500. Mas de 500 você recebeu um lucro - a constatação de que este é um erro INESPERADO.
  6. O código 500 virá com uma garantia. Não importa o quão ruim ou bom você escreveu seu código. Este é o seu ponto de apoio.

Separadamente, vou martelar um prego em todo o "corpo" do código 200:

7. Mesmo se você tentar muito evitar outros códigos de resposta do servidor que não sejam 200 para suas solicitações, não poderá fazer isso. Qualquer servidor intermediário pode responder à sua solicitação com absolutamente qualquer código. E você deve processar essa resposta corretamente.

Total, no nível lógico, a luta pelo código 200 não tem sentido.

Agora vamos voltar ao nível da infraestrutura. Muitas vezes ouço a opinião - o código 5xx não é um nível de aplicativo, não pode ser concedido. Aham, bem ... há uma contradição na própria declaração. Você pode dar. Mas esse código não é um nível de aplicativo. Isso é mais verdade. Para entender isso, proponho considerar o caso:
Você está implementando um gateway. Você tem vários controladores de domínio, cada um com seu próprio canal de comunicação para um determinado serviço privado. Bem, por exemplo, para pagar via VPN. E há um canal de comunicação com a Internet. Você recebe uma solicitação para uma operação com um gateway, mas ... o serviço está indisponível.
E então, o que você deve responder? Para quem? Esse é um problema de infra-estrutura e, especificamente, o backup ocorreu. Obviamente, você precisa responder com ousadia 503. Essas ações levarão ao fato de que o nó será desativado pelo balanceador por algum tempo. Ao mesmo tempo, o balanceador, se configurado corretamente, sem interromper a conexão com o cliente, enviará a solicitação para outro nó. E ... o cliente final, com um alto grau de probabilidade, recebeu 200. E não uma descrição personalizada do erro, que não o ajudará de forma alguma.

Onde e qual código usar


A questão não é simples. Não há resposta definitiva para isso. Para cada sistema, uma camada de transporte é projetada e os códigos nela podem ser específicos.

Existem padrões aceitos. Eles podem ser facilmente encontrados e, novamente, não darei provas óbvias. Mas, darei a você o óbvio - developer.mozilla.org/en/docs/Web/HTTP/Status
Porque ele O problema é que os manipuladores de código podem se comportar de maneira diferente, dependendo da implementação e do contexto de "entender o código". Por exemplo, os navegadores têm uma estratégia de cache com base nos códigos de resposta. E alguns serviços têm códigos personalizados. Por exemplo, CloudFlare.

I.e. Ao tomar decisões sobre o uso de códigos, é necessário basear-se em todos os elementos incluídos na camada de transporte, desde o código na parte traseira até o código no cliente. Esta é a única maneira de encontrar as respostas certas. Nem tentarei dar a todos uma pílula universal aqui.

Raízes do mal


Este é o terceiro projeto que eu sofro do código 200 no REST. Está sofrendo. Não há outra palavra. Se você ler com atenção tudo até o momento atual, já entenderá que, assim que o projeto começa a crescer, é necessário o desenvolvimento de infraestrutura, a sustentabilidade. O código 200 mata todas essas tentativas pela raiz. E a primeira coisa que você precisa fazer é quebrar os estereótipos.

A raiz do mal, parece-me, reside no fato de que o código 500 é a primeira coisa que um desenvolvedor da Web encontra em sua carreira profissional. Pode-se dizer uma lesão na infância. E todos os seus esforços a princípio se resumiram à obtenção do código 200.

A propósito, por algum motivo, no mesmo estágio, desenvolve-se uma forte opinião de que apenas as respostas com o código 200 podem ser fornecidas com um corpo. Obviamente, não é assim, e qualquer resposta pode "vir" com qualquer código. Código é um código. O corpo é o corpo.

Além disso, com o desenvolvimento do desenvolvedor, ele precisa gerenciar os erros de seu próprio aplicativo. Mas ..., ele não sabe usar logs. Não é possível configurar o servidor da web. Ele está estudando E aqueles muito "grandes" nascem. Porque eles estão disponíveis para ele e ele pode fazê-los rapidamente. Além disso, neste "ótimo", ele monta novas rodas, fortalece o quadro, etc. E este grande se torna seu companheiro por um período suficientemente longo, até ... até que ele tenha tarefas realmente complexas e multicomponentes. E aqui, como se costuma dizer - é proibida a entrada no supermercado com o "ótimo" e andar de patins.

PS: O autor do artigo mencionado o restaurou dos rascunhos - habr.com/en/post/440382 , para que você também possa se familiarizar com ele.

PPS: Tentei declarar todas as facetas da necessidade de usar códigos de resposta relevantes no REST. Não vou responder aos comentários, por favor, entenda-me corretamente. Vou lê-los com muita atenção, mas não tenho nada a acrescentar. Muito obrigado por ser paciente o suficiente para ler o artigo!

Source: https://habr.com/ru/post/pt440900/


All Articles