RESTful API writing guide

Based on some good RESTful development components, you can quickly develop good RESTful APIs, but if you do not understand the fundamentals of developing standardized and robust RESTful APIs, even if excellent RESTful development components are in front of you, you cannot understand them well. and use. Below, Gevin combines his own practical experience to sort out the core points of developing a RESTful API from scratch. A complete RESTful development component will basically contain all or most of the points. For the points that are not well supported, we can also write our own code to implement them.

1. Request 和 Response

The development and use of RESTful API is nothing more than the client sending a request to the server (request), and the server's response to the client's request (response). The native RESTful architectural style is characterized by a unified interface, that is, using different http methods to express different behaviors:

  • GET (SELECT): Get a resource (one or more) from the server
  • POST (CREATE): Create a new resource on the server
  • PUT (UPDATE): Update resources on the server (client provides complete resource data)
  • PATCH (UPDATE): Update resources on the server (the client provides the resource data that needs to be modified)
  • DELETE (DELETE): deletes a resource from the server

The client will GETsend a request to the server to obtain data based on the method, and send a request to update the data to the server based on the method PUTor PATCHmethod. When designing the API, the server should also process the corresponding request according to the corresponding specifications, which should now be all The developers of the RESTful API have reached a consensus, and the request and response classes of each web framework are very powerful, with reasonable default settings and flexible customization. Gevin is only going to emphasize that when responding to these requests, the commonly used Response should be Included data and status code (status code), incomplete content, welcome to add:

  • When GETPUTand PATCHthe request are successful, the corresponding data and status code should be returned 200, namely SUCCESS
  • When the POSTcreation of data is successful, the created data and status code should be returned 201, that is, CREATED
  • When the DELETEdeletion of data is successful, no data is returned, and the status code is to be returned 204, that is, NO CONTENT
  • When there GET is no data, the status code should be returned 404, that is, NOT FOUND
  • At any time, if there is a problem with the request, such as an error is found when verifying the request data, a status code should be returned  400, that is, BAD REQUEST
  • When the API request requires user authentication, if the authentication information in the request is incorrect, the status code should be returned  401, that is, NOT AUTHORIZED
  • When the API request needs to verify user permissions, if the current user does not have the corresponding permissions, the status code should be returned  403, that is, FORBIDDEN

Finally, regarding Request and Response, don't ignore the Content-Type in the http header. Taking json as an example, if the API requires the client to pass in json data when sending a request, the server only needs to obtain and parse the json data, but if the server supports the incoming of multiple types of data, such as For json and form-data, according to the Content-Type in the header when the client sends the request, the different types of data are obtained and parsed respectively; if the API needs to return json data after responding to the client request, it needs to be added in the header. Content-Type=application/json.

2. Serialization 和 Deserialization

Serialization 和 Deserialization即序列化和反序列化。RESTful API以规范统一的格式作为数据的载体,常用的格式为jsonxml,以json格式为例,当客户端向服务器发请求时,或者服务器相应客户端的请求,向客户端返回数据时,都是传输json格式的文本,而在服务器内部,数据处理时基本不用json格式的字符串,而是native类型的数据,最典型的如类的实例,即对象(object),json仅为服务器和客户端通信时,在网络上传输的数据的格式,服务器和客户端内部,均存在将json转为native类型数据和将native类型数据转为json的需求,其中,将native类型数据转为json即为序列化,将json转为native类型数据即为反序列化。虽然某些开发语言,如Python,其原生数据类型listdict能轻易实现序列化和反序列化,但对于复杂的API,内部实现时总会以对象作为数据的载体,因此,确保序列化和反序列化方法的实现,是开发RESTful API最重要的一步准备工作

题外话,序列化和反序列化的便捷,造就了RESTful API跨平台的特点,使得REST取代RPC成为Web Service的主流

序列化和反序列化是RESTful API开发中的一项硬需求,所以几乎每一种常用的开发语言都会有一个或多个优秀的开源库,来实现序列化和反序列化,因此,我们在开发RESTful API时,没必要制造重复的轮子,选一个好用的库即可,如python中的marshmallow,如果基于Django开发,Django REST Framework中的serializer即可。

3. Validation

Validation即数据校验,是开发健壮RESTful API中另一个重要的一环。仍以json为例,当客户端向服务器发出postputpatch请求时,通常会同时给服务器发送json格式的相关数据,服务器在做数据处理之前,先做数据校验,是最合理和安全的前后端交互。如果客户端发送的数据不正确或不合理,服务器端经过校验后直接向客户端返回400错误及相应的数据错误信息即可。常见的数据校验包括:

  • 数据类型校验,如字段类型如果是int,那么给字段赋字符串的值则报错
  • 数据格式校验,如邮箱或密码,其赋值必须满足相应的正则表达式,才是正确的输入数据
  • 数据逻辑校验,如数据包含出生日期和年龄两个字段,如果这两个字段的数据不一致,则数据校验失败

以上三种类型的校验,数据逻辑校验最为复杂,通常涉及到多个字段的配合,或者要结合用户和权限做相应的校验。Validation虽然是RESTful API 编写中的一个可选项,但它对API的安全、服务器的开销和交互的友好性而言,都具有重要意义,因此,Gevin建议,开发一套完善的RESTful API时,Validation的实现必不可少。

4. Authentication 和 Permission

Authentication指用户认证,Permission指权限机制,这两点是使RESTful API 强大、灵活和安全的基本保障。

常用的认证机制是Basic AuthOAuth,RESTful API 开发中,除非API非常简单,且没有潜在的安全性问题,否则,认证机制是必须实现的,并应用到API中去。Basic Auth非常简单,很多框架都集成了Basic Auth的实现,自己写一个也能很快搞定,OAuth目前已经成为企业级服务的标配,其相关的开源实现方案非常丰富更多)。

我在《RESTful 架构风格概述》中,对认证机制做了更加详细的描述,有兴趣的同学不妨阅读相关章节。

权限机制是对API请求更近一步的限制,只有通过认证的用户符合权限要求,才能访问API。权限机制的具体实现通常依赖于系统的业务逻辑和应用场景,generally speaking,常用的权限机制主要包含全局型的和对象型的,全局型的权限机制,主要指通过为用户赋予权限,或者为用户赋予角色或划分到用户组,然后为角色或用户组赋予权限的方式来实现权限控制,对象型的权限机制,主要指权限控制的颗粒度在object上,用户对某个具体对象的访问、修改、删除或其行为,要单独在该对象上为用户赋予相关权限来实现权限控制。

全局型的权限机制容易理解,实现也简单,有很多开源库可做备选方案,不少完善的web开发框架,也会集成相关的权限逻辑,object permission 相对难复杂一点,但也有很多典型的应用场景,如多人博客系统中,作者对自己文章的编辑权限即为object permission,其对应的开源库也有很多。

注: 我写过一篇《Django权限机制的实现》,Django 开发者可做延伸阅读。

开发一套完整的RESTful API,权限机制必须纳入考虑范围,虽然权限机制的具体实现依赖于业务,权限机制本身,是有典型的模式存在的,需要开发者掌握基本的权限机制实现方案,以便随时应用到API中去。

5. CORS

CORS即Cross-origin resource sharing,在RESTful API开发中,主要是为js服务的,解决javascript 调用 RESTful API时的跨域问题。

由于固有的安全机制,js的跨域请求时是无法被服务器成功响应的。现在前后端分离日益成为web开发主流方式的大趋势下,后台逐渐趋向指提供API服务,为各客户端提供数据及相关操作,而网站的开发全部交给前端搞定,网站和API服务很少部署在同一台服务器上并使用相同的端口,js的跨域请求时普遍存在的,开发RESTful API时,通常都要考虑到CORS功能的实现,以便js能正常使用API。

目前各主流web开发语言都有很多优秀的实现CORS的开源库,我们在开发RESTful API时,要注意CORS功能的实现,直接拿现有的轮子来用即可。

更多关于CORS的介绍,有兴趣的同学可以查看阮一峰老师的跨域资源共享 CORS 详解

6. URL Rules

RESTful API 是写给开发者来消费的,其命名和结构需要有意义。因此,在设计和编写URL时,要符合一些规范。Url rules 可以单独写一篇博客来详细阐述,本文只列出一些关键点。

6.1 Version your API

规范的API应该包含版本信息,在RESTful API中,最简单的包含版本的方法是将版本信息放到url中,如:

/api/v1/posts/
/api/v1/drafts/

/api/v2/posts/
/api/v2/drafts/

另一种优雅的做法是,使用HTTP header中的accept来传递版本信息,这也是GitHub API 采取的策略

6.2 Use nouns, not verbs

RESTful API 中的url是指向资源的,而不是描述行为的,因此设计API时,应使用名词而非动词来描述语义,否则会引起混淆和语义不清。即:

# Bad APIs
/api/getArticle/1/
/api/updateArticle/1/
/api/deleteArticle/1/

上面四个url都是指向同一个资源的,虽然一个资源允许多个url指向它,但不同的url应该表达不同的语义,上面的API可以优化为:

# Good APIs
/api/Article/1/

 The acquisition, update and  deletion of article resources can be done through  API GETand  method requests respectively.  Just imagine how uncomfortable it would be to use a method request  if the url was described as a verb .PUTDELETEPUT/api/deleteArticle/1/

6.3 GET and HEAD should always be safe

RFC2616 has made it clear that GETand HEADmethods must always be safe. For example, there is such a non-standard API:


# The following api is used to delete articles
# [GET]
/api/deleteArticle?id=1

Just imagine, what would happen if a search engine visited the above url?

6.4 Nested resources routing

If you want to get a subset of resources, it  nested routing is an elegant way to use, for example, list all the articles written by Gevin:

# List Gevin's articles
/api/authors/gevin/articles/

Another way to obtain a subset of resources is based on filter(see the following sections), both of which are compliant with the specification, but have different semantics: it  feels more appropriate to use  if a subset of resources is semantically seen as a separate collection of resources , It is more appropriate to use nested routingif a subset of resources is obtained for filtering purposes .filter

As for which method to use when writing a RESTful API, it is up to the beholder to see the difference, the wise to see the wisdom, and the semantics is sufficient.

6.5 Filter

For resource collections, you can filter resources by url parameters, such as:

# List Gevin's articles
/api/articles?author=gevin

Paging is one of the most typical resource filtering.

6.6 Pagination

For resource collections, paging is a more reasonable way. If it is based on a development framework (such as Django REST Framework), you can directly use the paging mechanism in the development framework. If you implement the paging mechanism yourself, Gevin's strategy is:

The returned resource collection is, containing data related to paging as follows:

{
  "page": 1,            # 当前是第几页
  "pages": 3,           # 总共多少页
  "per_page": 10,       # 每页多少数据
  "has_next": true,     # 是否有下一页数据
  "has_prev": false,    # 是否有前一页数据
  "total": 27           # 总共多少数据
}

When you want to request a resource collection from the API, the optional paging parameters are:

Parameter meaning
page 当前是第几页,默认为1
per_page 每页多少条记录,默认为系统默认值

另外,系统内还设置一个per_page_max字段,用于标记系统允许的每页最大记录数,当per_page值大于 per_page_max 值时,每页记录条数为 per_page_max

6.7 Url design tricks

(1)Url是区分大小写的,这点经常被忽略,即:

  • /Posts
  • /posts

上面这两个url是不同的两个url,可以指向不同的资源

(2)Back forward Slash (/)

目前比较流行的API设计方案,通常建议url以/作为结尾,如果API GET请求中,url不以/结尾,则重定向到以/结尾的API上去(这点现在的web框架基本都支持),因为有没有 /,也是两个url,即:

  • /posts/
  • /posts

这也是两个不同的url,可以对应不同的行为和资源

(3)连接符 - 和 下划线 _

RESTful API 应具备良好的可读性,当url中某一个片段(segment)由多个单词组成时,建议使用 - 来隔断单词,而不是使用 _,即:

# Good
/api/featured-post/

# Bad
/api/featured_post/

这主要是因为,浏览器中超链接显示的默认效果是,文字并附带下划线,如果API以_隔断单词,二者会重叠,影响可读性。

总结

编写本文的初衷,是为了整理一套从零开始编写规范、安全的RESTful API的基本思路。本文介绍了开发RESTful API时,要考虑的基本内容,对于类似Flask这种天生支持RESTful风格的web框架,不依赖其他RESTful第三方库开发RESTful 服务时,可以从本文内容入手;不少强大的RESTful 库,虽然其相关接口基本涵盖了本文的全部或大部分内容,但本文的总结,相信对这些库的理解和使用也是有帮助的。

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326946544&siteId=291194637