restfulapi接⼝规范_RESTfulAPI书写规范
基于⼀些不错的RESTful开发组件,可以快速的开发出不错的RESTful API,但如果不了解开发规范的、健壮的RESTful API的基本⾯,即便优秀的RESTful开发组件摆在⾯前,也⽆法很好的理解和使⽤。下⽂
Gevin结合⾃⼰的实践经验,整理了从零开始开发RESTful API的核⼼要点,完善的RESTful开发组件基本都会包含全部或⼤部分要点,对于⽀持不够到位的要点,我们也可以⾃⼰写代码实现。
1. Request 和 Response
RESTful API的开发和使⽤,⽆⾮是客户端向服务器发请求(request),以及服务器对客户端请求的响应(response)。本真RESTful架构风格具有统⼀接⼝的特点,即,使⽤不同的http⽅法表达不同的⾏为:
GET(SELECT):从服务器取出资源(⼀项或多项)
POST(CREATE):在服务器新建⼀个资源
PUT(UPDATE):在服务器更新资源(客户端提供完整资源数据)
PATCH(UPDATE):在服务器更新资源(客户端提供需要修改的资源数据)
DELETE(DELETE):从服务器删除资源
客户端会基于GET⽅法向服务器发送获取数据的请求,基于PUT或PATCH⽅法向服务器发送更新数据的请求等,服务端在设计API时,也要按照相应规范来处理对应的请求,这点现在应该已经成为所有R
ESTful API的开发者的共识了,⽽且各web框架的request类和response 类都很强⼤,具有合理的默认设置和灵活的定制性,Gevin在这⾥仅准备强调⼀下响应这些request时,常⽤的Response要包含的数据和状态码(status code),不完善的内容,欢迎⼤家补充:
当GET, PUT和PATCH请求成功时,要返回对应的数据,及状态码200,即SUCCESS
当POST创建数据成功时,要返回创建的数据,及状态码201,即CREATED
当DELETE删除数据成功时,不返回数据,状态码要返回204,即NO CONTENT
当GET 不到数据时,状态码要返回404,即NOT FOUND
任何时候,如果请求有问题,如校验请求数据时发现错误,要返回状态码 400,即BAD REQUEST
当API 请求需要⽤户认证时,如果request中的认证信息不正确,要返回状态码 401,即NOT AUTHORIZED
当API 请求需要验证⽤户权限时,如果当前⽤户⽆相应权限,要返回状态码 403,即FORBIDDEN
最后,关于Request 和 Response,不要忽略了http header中的Content-Type。以json为例,如果API
要求客户端发送request时要传⼊json数据,则服务器端仅做好json数据的获取和解析即可,但如果服务端⽀持多种类型数据的传⼊,如同时⽀持json和form-data,则要根据客户端发送请求时header中的Content-Type,对不同类型是数据分别实现获取和解析;如果API响应客户端请求后,需要返回json数据,需要在header中添加Content-Type=application/json。
2. Serialization 和 Deserialization
Serialization 和 Deserialization即序列化和反序列化。RESTful API以规范统⼀的格式作为数据的载体,常⽤的格式为json或xml,以json格式为例,当客户端向服务器发请求时,或者服务器相应客户端的请求,向客户端返回数据时,都是传输json格式的⽂本,⽽在服务器内部,数据处理时基本不⽤json格式的字符串,⽽是native类型的数据,最典型的如类的实例,即对象(object),json仅为服务器和客户端通信时,在⽹络上传输的数据的格式,服务器和客户端内部,均存在将json转为native类型数据和将native类型数据转为json的需求,其中,将native类型数据转为json即为序列化,将json转为native类型数据即为反序列化。虽然某些开发语⾔,如Python,其原⽣数据类型list和dict能轻易实现序列化和反序列化,但对于复杂的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为例,当客户端向服务器发出post, put或patch请求时,通常会同时给服务器发送json格式的相关数据,服务器在做数据处理之前,先做数据校验,是最合理和安全的前后端交互。如果客户端发送的数据不正确或不合理,服务器端经过校验后直接向客户端返回400错误及相应的数据错误信息即可。常见的数据校验包括:
数据类型校验,如字段类型如果是int,那么给字段赋字符串的值则报错
数据格式校验,如邮箱或密码,其赋值必须满⾜相应的正则表达式,才是正确的输⼊数据
数据逻辑校验,如数据包含出⽣⽇期和年龄两个字段,如果这两个字段的数据不⼀致,则数据校验失败
以上三种类型的校验,数据逻辑校验最为复杂,通常涉及到多个字段的配合,或者要结合⽤户和权限
做相应的校验。Validation虽然是RESTful API 编写中的⼀个可选项,但它对API的安全、服务器的开销和交互的友好性⽽⾔,都具有重要意义,因此,Gevin建议,开发⼀套完善的RESTful API时,Validation的实现必不可少。
4. Authentication 和 Permission
Authentication指⽤户认证,Permission指权限机制,这两点是使RESTful API 强⼤、灵活和安全的基本保障。
常⽤的认证机制是Basic Auth和OAuth,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/
article 资源的获取、更新和删除分别通过 GET, PUT 和 DELETE⽅法请求API即可。试想,如果url以动词来描述,⽤PUT⽅法请求
/api/deleteArticle/1/ 会感觉多么不舒服。
6.3 GET and HEAD should always be safe
RFC2616已经明确指出,GET和HEAD⽅法必须始终是安全的。例如,有这样⼀个不规范的API:
# The following api is used to delete articles# [GET]/api/deleteArticle?id=1
试想,如果搜索引擎访问了上⾯url会如何?
6.4 Nested resources routing
如果要获取⼀个资源⼦集,采⽤ nested routing 是⼀个优雅的⽅式,如,列出所有⽂章中属于Gevin编写的⽂章:
# List Gevin's articles/api/authors/gevin/articles/
获取资源⼦集的另⼀种⽅式是基于filter(见下⾯章节),这两种⽅式都符合规范,但语义不同:如果语义上将资源⼦集看作⼀个独⽴的资源集合,则使⽤ nested routing 感觉更恰当,如果资源⼦集的获取是出于过滤的⽬的,则使⽤filter更恰当。
⾄于编写RESTful API时到底应采⽤哪种⽅式,则仁者见仁,智者见智,语义上说的通即可。
6.5 Filter
对于资源集合,可以通过url参数对资源进⾏过滤,如:
# List Gevin's articles/api/articles?author=gevin
分页就是⼀种最典型的资源过滤。
6.6 Pagination
对于资源集合,分页获取是⼀种⽐较合理的⽅式。如果基于开发框架(如Django REST Framework),直接使⽤开发框架中的分页机制即
可,如果是⾃⼰实现分页机制,Gevin的策略是:
影视资源站采集api接口数据返回资源集合是,包含与分页有关的数据如下:
{ "page": 1, # 当前是第⼏页 "pages": 3, # 总共多少页 "per_page": 10, # 每页多少数据 "has_next": true, # 是否有下⼀页数据 "has_prev": false, # 是否有前⼀页数据当想API请求资源集合时,可选的分页参数为:
参数含义page当前是第⼏页,默认为1per_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 库,虽然其相关接⼝基本涵盖了本⽂的全部或⼤部分内容,但本⽂的总结,相信对这些库的理解和使⽤也是有帮助的。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论