📜 ⬆️ ⬇️

How to write a bad API

Write the code as if it would be accompanied by a violent psychopath who knows where you live.


Hello!


I work as a team leader in Integration Development in the online hotel reservation service Ostrovok.ru and today I would like to share my experience with various APIs.



As a developer of a system working with external suppliers, I often meet with various APIs - most often it is SOAP / REST or something similar to them. However, working with many of them leaves the impression that they were written without being guided either by technical rules or by common sense - as if from the book “Bad advice” by Gregory of Oster. In this article I will try to describe such cases in the style of "bad advice" and consider examples related to XML. Comments and discussion are welcome.


History reference

SOAP (from the English. Simple Object Access Protocol - a simple protocol for accessing objects) is a protocol for exchanging structured messages in a distributed computing environment. Originally, SOAP was intended primarily for implementing remote procedure call (RPC). Now the protocol is used to exchange arbitrary messages in XML format, and not just to call procedures.


Go to the examples.


1. Passing xml to url


What do API users most want? Of course, simplicity, reliability and brevity. So let's not read the request body, but accept XML as url-encoded information as a parameter of the request path! What could be better:


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 

Everything becomes simple, and you do not need to read some body from the request - you never know what the problem may be with it.


Spoiler

I’m not going to know why this was done. The problems here are the following: many servers have a limit on the length of the request path that can be passed in them. If the XML is large in terms of data, then you can cause the error 413 Entity Too Large as one of the options for the development of events. In addition, the amount of information increases as we produce url-encoding before sending.


2. Information transfer by redundant nesting of data objects


Let's think about how to make the information in the answers as difficult as possible? Let's use nested structures, and even in different formats! No sooner said than done -


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

Indeed, a high-level xml, inside it is another xml, and inside it is json, in which the data is presented in base64, and in it is again json, and it will already have the information we need! A great solution, almost like from a fairy tale about the death of Koshchei, hidden in an egg.


Spoiler

One of the most noticeable disadvantages is slowing down the work of parsing the response until all the nested structures are passed, and then it may turn out that the error code is sewn into json, and not levels higher. I understand that encoding binary data in base64 inside xml / json is a common practice, but encoding another format inside another format is already beyond good and evil.


3. Adding information not related to the request data and not valid within the data format


Suppose XML comes to us in the request body, we process it and give an answer. It looks too difficult for a well-designed and heavily loaded system. Let's commit users to send the data type in the request body. How to do it? Right in the body of the request, of course.


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

In this simple way, we will always know that we received a request in XML format.


Spoiler

It turns out that we must add more leading bytes to the already formed request body and only after that it will be possible to make a request. Lucky if you do not need to change the leading bytes, depending on the type of data request. In this case, it would be better to use http Header to specify the data type, and not to change the body of the request.


4. Duplication of data without the need


Suppose we have very, very important information in the XML response structure. How to show it to the user? The most obvious - let's show it several times as part of the answer, then he will definitely pay attention to her.


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

After that, the end user will accurately pay attention to the Info field.


Spoiler

In this case, I thought about it and even asked the company providing the API about the meaning of the Info field and whether the information in the tags at different levels would be different. The answer to me was: no, it won't be - they duplicate each other. Why mislead users and make the answer more difficult if it is not necessary?


5. Passing parameters of the same type separately, not an array


In one of the APIs that we use to search for hotels, there are fields indicating the age of the guests. What presentation format is best used to transmit this information? Let the format be this: each age will be a separate mandatory XML tag, and the value 0 will be considered as the absence of this parameter.


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

Spoiler

There are several problems at once: nonextendability, excessive information, besides that, age can really be zero if the guests are newborn children. In this case, you should use an array, not uniquely named tags.


6. Forwarding information from previous requests within the API call chain


It's time to think about the security of our API. How do we understand if a user gets information from our previous answers? Let him send us our answer, of course!


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

Spoiler

To continue working with the API, you need to send the entire response of the external system from the previous API steps, and not some important data from it, and not even a hash that will uniquely correspond to this answer. Excess data in all its glory.


7. Not using error markers such as an error tag or http code


We made our excellent API and presented it to the world. But once something went wrong, and we could not form a response to the user due to an internal error. What to do in this case? Just give a response template, without data, without error codes or any other information. No one should know that our ideal API can sometimes not work!
An example of such a response:


 <Response> <ImportantInfo/> </Response> 

- with the answer code 200 OK.


Spoiler

Hushing up the mistakes that happened is a very bad practice. The problem is that everything looks as if there are no problems in the answer: there is no <Error> tag, the http status says that everything is in order. In this case, it is necessary to do additional validation of the information received so that unintended consequences do not occur already in our system.


Conclusion
Despite the large amount of documentation on how to work with SOAP / XML technologies and API design, many problems are still relevant, and some solutions contradict common sense. I hope this article will be able to draw the attention of developers to the less successful approaches in order to reduce their number in the future.


')

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


All Articles