Como escrever uma API incorreta

Escreva o código como se fosse acompanhado por um psicopata violento que sabe onde você mora.


Olá pessoal!


Trabalho como líder da equipe de desenvolvimento de integração no serviço de reserva de hotéis online Ostrovok.ru e hoje gostaria de compartilhar minha experiência com várias APIs.



Como desenvolvedor de um sistema que trabalha com provedores externos, frequentemente encontro várias APIs - na maioria das vezes é SOAP / REST ou algo semelhante a elas. No entanto, trabalhar com muitos deles deixa a impressão de que foram escritos, não guiados por regras técnicas ou bom senso - como se fossem do livro “Bad Advice” de Grigory Oster. Neste artigo, tentarei descrever esses casos no estilo de "maus conselhos" e considerarei exemplos relacionados ao XML. Comentários e discussões são bem-vindos.


Antecedentes históricos

SOAP (do inglês. Simple Object Access Protocol - um protocolo simples para acessar objetos) - um protocolo para troca de mensagens estruturadas em um ambiente de computação distribuído. O SOAP foi originalmente destinado principalmente à implementação de RPC (chamada de procedimento remoto). Agora, o protocolo é usado para trocar mensagens arbitrárias no formato XML, e não apenas para chamar procedimentos.


Vá para os exemplos


1. Passando XML para URL


O que os usuários da API mais desejam? Claro, simplicidade, confiabilidade e concisão. Portanto, não vamos ler o corpo da solicitação, mas aceitar o XML como informações codificadas por URL como parâmetro do caminho da solicitação! O que poderia ser melhor:


http://exapmple.com/xml/form.jsp?RequestName%3DHotelRequest2%26XML%3D%3C%3Fxml%2Bversion%3D%221.0%22%2Bencoding%3D%22UTF-8%22%3F%3E%0A%3CHotelRequest2%2BBuyerId%3D%22test%22%2BUserId%3D%22test%22%2BPassword%3D%22test%22%2BLanguage%3D%22en%22%2BHotel%3D%22-100%22%2BProductCode%3D%221--%22%2BArrivalDate%3D%2223.12.2018%22%2BDepartureDate%3D%2224.12.2018%22%2BArrivalTime%3D%22%22%2BDepartureTime%3D%22%22%2BCurrency%3D%222%22%2BWhereToPay%3D%223%22%2BNumberOfGuests%3D%220%22%2BNumberOfExtraBedsAdult%3D%220%22%2BNumberOfExtraBedsChild%3D%220%22%2BNumberOfExtraBedsInfant%3D%220%22%2B%2F%3E 

Tudo se torna simples e não há necessidade de subtrair algum corpo da consulta - você nunca sabe quais problemas podem ocorrer com ela.


Spoiler

Não sei por que isso foi feito. Os problemas aqui são os seguintes: muitos servidores têm um limite no comprimento do caminho da solicitação que pode passar por eles. Se o XML for grande no volume de dados, você poderá causar o erro 413 Entity Too Large como um dos cenários. Além disso, a quantidade de informações está aumentando, pois fazemos a codificação de URL antes do envio.


2. Transferência de informações através do aninhamento excessivo de objetos de dados


Vamos pensar em como tornar a informação nas respostas o mais difícil possível? Vamos usar estruturas aninhadas, e mesmo em diferentes formatos! Mal disse o que fez -


 <Request> <InnerRequest> <RQ>[{"someInfo":"base64Data"}] </RQ> </InnerRequest> </Request> 

De fato, o xml de nível superior, dentro dele, é outro xml, e dentro dele é json, no qual os dados são apresentados em base64, e novamente json nele, e já conterá as informações que precisamos! Uma ótima solução, quase como um conto de fadas sobre a morte de Koshchei, escondido em um ovo.


Spoiler

Uma das desvantagens mais visíveis é a desaceleração na análise da resposta até que todas as estruturas aninhadas sejam percorridas e, posteriormente, pode acontecer que o código de erro seja conectado ao json, e não a níveis mais altos. Entendo que a codificação de dados binários na base64 dentro do xml / json é uma prática comum, mas a codificação de um formato diferente dentro de um formato diferente já está além do bem e do mal.


3. Adicionando informações que não estão relacionadas aos dados da solicitação e não são válidas no formato de dados


Suponha que o XML chegue até nós no corpo da solicitação, nós o processemos e damos uma resposta. Parece muito complicado para um sistema bem projetado e com muita carga. Vamos obrigar os usuários a enviar tipo de dados no corpo da solicitação. Como fazer isso? Bem no corpo da solicitação, é claro.


 XML= <Request> ... </Request> 

De uma maneira tão simples, sempre saberemos que recebemos uma solicitação no formato XML.


Spoiler

Acontece que devemos adicionar mais bytes iniciais ao corpo já formado da solicitação e somente depois disso será possível fazer uma solicitação. Sorte se você não precisar alterar os bytes iniciais, dependendo do tipo de dados da solicitação. Nesse caso, seria melhor usar o cabeçalho http para indicar o tipo de dados e não alterar o corpo da solicitação.


4. Duplicação de dados sem a necessidade


Suponha que tenhamos informações muito, muito importantes na estrutura da resposta XML. Como mostrar isso ao usuário? O mais óbvio - vamos mostrá-lo várias vezes como parte da resposta, então ele definitivamente prestará atenção a ela.


 <Response> <Obj Info="Important"> <ObjSetting Info="Important"/> <Name>SomeName</Name> <Info>Important</Info> </Obj> </Response> 

Depois disso, o usuário final definitivamente prestará atenção no campo Informações.


Spoiler

Nesse caso, pensei nisso e até perguntei à empresa que forneceu a API sobre o significado do campo Informações e se as informações nas tags em diferentes níveis seriam diferentes. A resposta foi: não, não - eles se duplicam. Por que enganar os usuários e dificultar a resposta se isso não for necessário?


5. Passando parâmetros do mesmo tipo individualmente, não uma matriz


Em uma das APIs que usamos para pesquisar hotéis, existem campos indicando a idade dos convidados. Qual formato de apresentação é melhor usado para transmitir essas informações? Deixe o formato ser assim: cada idade será uma tag XML obrigatória separada e consideraremos o valor 0 como a ausência desse parâmetro.


 <Request> <Age1>20</Age> <Age2>20</Age> <Age3>0</Age> <Age4>0</Age> </Request> 

Spoiler

Existem vários problemas aqui de uma só vez: não extensibilidade, informações redundantes; além disso, a idade pode realmente ser zero se os convidados forem crianças recém-nascidas. Nesse caso, você precisa usar uma matriz, em vez de tags nomeadas exclusivamente.


6. Encaminhando informações de solicitações anteriores como parte da cadeia de chamadas da API


É hora de pensar na segurança de nossa API. Como entendemos que o usuário recebe informações de nossas respostas anteriores? Deixe que ele nos envie nossa resposta, é claro!


 <Request> <RequestInfo/> <PreviosResp> ... </PreviosResp> </Request> 

Spoiler

Para continuar trabalhando com a API, é necessário enviar toda a resposta do sistema externo das etapas anteriores da API, e não alguns dados importantes dele, ou mesmo um hash que definitivamente corresponderá a esta resposta. Excesso de dados em toda a sua glória.


7. Não use marcadores de um erro que ocorreu, como uma etiqueta de erro ou um código http


Criamos nossa bela API e a apresentamos ao mundo. Porém, quando algo deu errado, não conseguimos formular uma resposta ao usuário devido a um erro interno. O que fazer neste caso? Basta fornecer um modelo de resposta, sem dados, sem códigos de erro ou qualquer outra informação. Ninguém deve saber que nossa API ideal pode às vezes não funcionar!
Um exemplo dessa resposta:


 <Response> <ImportantInfo/> </Response> 

- com um código de resposta 200 OK.


Spoiler

Silenciar os erros cometidos é uma prática muito ruim. O problema é que tudo parece que não há problemas na resposta: não existe uma tag <Error> , o status http diz que tudo está em ordem. Nesse caso, é necessário fazer uma validação adicional das informações recebidas para que não haja consequências imprevisíveis em nosso sistema.


Conclusão
Apesar da grande quantidade de documentação sobre o trabalho com tecnologias SOAP / XML e design de API, muitos problemas ainda são relevantes e algumas soluções são contrárias ao senso comum. Espero que, com este artigo, seja capaz de chamar a atenção dos desenvolvedores para as abordagens não mais bem-sucedidas, a fim de reduzir seu número no futuro.

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


All Articles