When I connect to my colleague's interface, all the interfaces he defined are post requests. The reason is that post is safer for https. I am used to using restful APIs. If only post requests are safe for https? Then why do we need get, put, and delete? How can I refute him?
Why Use Different HTTP Verbs?
In the programming world, there are generally two types of logic: "business logic" and "control logic."
Business Logic refers to the code that implements business requirements, closely related to user needs. For example, saving user-submitted data, querying user data, completing a transaction, issuing refunds, etc. This is business logic.
Control Logic is non-functional code used to control program execution. It involves loop control variables and conditions, multithreading or distributed techniques, HTTP/TCP protocols, database choices, middleware, etc.—elements that are unrelated to user needs.
Network protocols are similar. Almost all mainstream network protocols consist of two parts: the protocol header and the protocol body. The header contains data needed by the protocol itself, while the body contains user data. Therefore, the protocol header mainly serves control logic, while the protocol body serves business logic.
HTTP verbs (or methods) reside in the protocol header and are primarily used for control logic.
Below are the HTTP verb specifications. Generally, REST APIs require developers to strictly adhere to these standards (refer to RFC7231, Section 4.2.2 – Idempotent Methods):
Method | Description | Idempotent |
---|---|---|
GET | Used for querying operations, corresponding to database SELECT operations | ✔︎ |
PUT | Used for all information updates, corresponding to database UPDATE operations | ✔︎ |
DELETE | Used for deletion operations, corresponding to database DELETE operations | ✔︎ |
POST | Used for creation operations, corresponding to database INSERT operations | ✘ |
HEAD | Returns "metadata" for a resource or checks if the API is healthy | ✔︎ |
PATCH | Used for partial information updates, corresponding to database UPDATE operations | ✘ |
OPTIONS | Retrieves information related to the API | ✔︎ |
Both PUT and PATCH are used for updating business resource information; if the resource does not exist, they can create one. The difference is that PUT updates all complete information of a business object, akin to submitting all data via a form, while PATCH pertains to more API-specific data updates that only require the fields that need to be updated (see RFC 5789).
Of course, in the real world, API design might not strictly follow CRUD operations. For instance, consider a login API (/login). Should it be GET, POST, PUT, or PATCH? When a user logs in, they need to input a username and password, which are then compared against the database (a SELECT operation) to return a session token. Semantically, logging in isn’t about querying information; it’s an update or creation of user state (a new session), so it should use POST. Conversely, /logout could use DELETE. This highlights that one shouldn’t rigidly map CRUD operations to HTTP verbs—analyzing the business semantics is crucial.
Moreover, the final column in the table notes whether the API is idempotent. Idempotency is vital for control logic. An idempotent API means that executing it multiple times yields the same result as executing it once, with no side effects.
POST is used for adding new data (e.g., creating a transaction order) and is not idempotent.
DELETE is idempotent since deleting the same data multiple times results in the same outcome.
PUT is idempotent because it updates all data.
PATCH involves partial updates, such as incrementing a field (e.g.,
cnt = cnt + 1
), and is not idempotent.
Idempotency is crucial for remote calls; network issues can lead to timeouts, and if an API is not idempotent, retrying a request could cause unintended effects. For instance, a money transfer operation would yield different results if executed multiple times. Adhering to HTTP verb standards allows developers to understand which verbs can be retried and which cannot. Using POST for everything would lead to a lack of control.
Beyond idempotency, you may require various control logic needs:
Caching: For APIs, caching is advisable for query (GET) operations.
Rate Limiting: HTTP verbs allow for finer granularity in controlling API usage frequencies for read and write operations.
Routing: Direct write requests to write services, and read requests to read services.
Permissions: Achieve more granular control and auditing.
Monitoring: Different API methods may have varying performance metrics for analysis.
Load Testing: Distinguishing verbs is essential for effective load testing.
You might argue that your business is simple enough not to warrant such complexity. Fine; at a minimum, you should achieve "read-write separation" with at least two verbs: GET for read operations and POST for write operations.
RESTful Complex Queries
For querying APIs, you generally need to perform four operations: sorting, filtering, searching, and pagination. Below are some relevant guidelines based on what I consider to be the best RESTful API documentation: Microsoft REST API Guidelines and PayPal API Design Guidelines.
Sorting
Use the sort
keyword with the syntax {field_name}|{asc|desc},{field_name}|{asc|desc}
for sorting result sets. For instance, to return a list of companies sorted by rank: GET /admin/companies?sort=rank|asc
or GET /admin/companies?sort=rank|asc,zip_code|desc
.
Filtering
Use the filter
keyword with the syntax {field_name} op{value}
for filtering result sets. For example: GET /companies?category=banking&location=china
. However, for more flexible expressions, construct them in the URL, defining comparison operations: =
, <
, >
, <=
, >=
, and logical operations: and
, or
, not
. An example query might be: GET /products?$filter=name eq 'Milk' and price lt 2.55
to find all milk priced under 2.55.
Searching
For searches, use the search
keyword followed by the search term: GET /books/search?description=algorithm
or full-text search with GET /books/search?key=algorithm
.
Pagination
Pagination should be a default behavior to avoid excessive return data. Use page
and per_page
to represent page numbers and the number of items per page, like GET /books?page=3&per_page=20
.
Optional
The page method may have two issues: performance (with large datasets) and data skew (due to high-frequency updates). In such cases, use absolute positions to retrieve data, keeping track of the last retrieved data's ID, timestamp, etc., to get "data before this ID" or "data before this timestamp." For example: GET /news?max_id=23454345&per_page=20
or GET /news?published_before=2011-01-01T00:00:00Z&per_page=20
.
Note: Theoretically, GET can include a body, but many libraries or middleware do not support GET with a body, forcing you to use POST. The principle is:
For simple queries, most parameters are embedded in the RESTful API's path, making GET appropriate for filter/sort/pagination operations.
For complex queries, like those involving Elasticsearch's
index/_search
DSL, GET should also be used unless objectively impossible. Elasticsearch's official documentation supports this: "The authors of Elasticsearch prefer using GET for a search request because they feel that it describes the action—retrieving information—better than the POST verb."
Response to Common Questions
Here are direct responses to several questions. If you have more questions, feel free to comment, and I will add them below.
Why should APIs be RESTful and compliant with standards?
RESTful APIs are essentially a standard for HTTP. They represent a global consensus on HTTP APIs. This consensus allows you to leverage numerous technological benefits, such as CDN, API gateways, service governance, and monitoring, significantly reducing development costs and avoiding pitfalls.Why is "premature optimization" unsuitable for API design?
APIs are contracts; once used, they are difficult to change. Even with new API versions, you must encourage various callers to upgrade their usage. Interface design is akin to database schema design; once established, future changes become challenging. Thus, thoughtful design is crucial. Referencing documents like Microsoft REST API Guidelines, PayPal API Design Guidelines, or Google API Design Guide can help in this regard.Is POST safer?
Not necessarily.
Many believe that since GET requests include data in the URL while POST does not, POST is safer. However, the entire HTTP request URL path is encapsulated in the HTTP header. As long as it’s HTTPS, it is secure. Some gateways (e.g., nginx) may log URLs or store them in browser history, leading to the misconception that GET is insecure. Yet, POST has its vulnerabilities, particularly regarding CSRF. Security is complex; neither method inherently ensures greater safety.Does using only POST save time and reduce communication?
It does not; in fact, it can worsen the situation.
Assigning different verbs to APIs is quick. Writing CRUD in different functions is a good programming style. Most development frameworks support rapid CRUD development (e.g., Spring Boot) without needing to write SQL code. Furthermore, adhering to standards can save time for new team members and minimize inter-team communication costs, ultimately enhancing overall efficiency.The correct approach to getting home early
Don’t think that you will be fine after you go home. If your code has this or that problem, and others can’t understand it, or they misuse your code and it goes wrong, then what’s the point of going home early? You will still be disturbed, or even called to the company to deal with the problem. So, what you should do is to “go home early in the long term”, not “go home early in the short term”. To go home early in the long term, it is usually like this:Organize and design the code well, and have better scalability. In this way, when facing new requirements, you can change the code less, or even not change the code. Only in this way can you go home early. Otherwise, every time a requirement comes, you have to rewrite it, how can you go home early?
Your code quality is good, with good documentation and comments. Therefore, others will not always come to you with problems, or ask you to deal with problems after you get off work. Even anyone can easily take over your code, so that you can really not be disturbed
It’s just work, elegance can’t be eaten
Reply to two points:
First, it’s just following a specification. Calling “normal” “elegant” shows how low the standard is. With such a low standard, you can only "survive for food".
Second, as a "professional programmer", you must learn to love and respect your profession. The most important thing about loving your profession is not to let outsiders look down on this profession. If you don't respect this profession yourself, how can you let others respect it? Respecting your profession is not only about getting enviable rewards, but also about making your profession more valuable.
I hope everyone can respect the profession they are engaged in and become a truly professional programmer, not a code farmer!