最流行的 RESTful API 要怎么设计?

云栖号资讯:【点击查看更多行业资讯】
在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来!

RESTful 是目前最流行的 API 设计规范,用于 Web 数据接口的设计。它的大原则W } g :容易把握,但是细节不容易做对。本文总结 RESTful 的设* v |计细节,介绍] ! 6 T k O如何设计出易于理解和使用的 API

UR q ^ z m yL设计

动词+宾语
RESTful的核心思想就是,客M [ % a E V L `户端发出的数据+操作指令都是“动词+宾语”的结构,比如GET /articles这个命令,GET是动词,/articles是宾语,动词通常就有5种HTTP请求方法,对应CRUD操作,根据 HTTP 规范,动词一律大写。

# GET:读取(Read)
# POST:新建(Create)
# PUT:更新(Update)
# PATCe R S OH:更新(Update),通常是部分更新# DELETE:删除(Den ; X q zlete)

动词的覆盖

有些客户端只能使用GET和POST这两种方法。服务器必须接受POST模拟其他三个方法(PUT、PATCH、DELETE)。这时,客户端发出的 HTTP 请求,要加上X-HTTP-Method-Override属性,告诉服务器应该使用哪一个动词,Q D @ c H / f +覆盖POST方法。

POST /api/Person/4 HTTP/1.1  X-HTTP-MetV % a ( K q s hod-Override: PUT

上面代码中,X-HTTP-Method-Override指定本次请求G y M y 3 O o 2的方法是PUT,而不是POST。

宾语i n @ z D u 9必须是名词

宾语h 2 g $ V O Y $就是 API 的 URL,是 HTTP 动词作用的对象。它应该是名词,不能是动词C C 8 v。比如,/articles这个 URL 就是正确的,而下面的: - K 0 S ^ R URL 不是名k ; { , K词,= ` C R / F /所以都是错误的。

# /getAllCars# /createNewCar# /deleteAllRedCars

复数 Uu B C H u s - LRL

既然 URL 是名词,; ? n - A那么应该使用复数,还是单数?这没有统一的规定,但是常见的操作是读取一个集合,比如GET /articles(读取所有文章),这里明显应该是复数。

为了统一l Q g / t - Q [起见,建议都使用复数 URL,比如GET /articles/2要好于GET /article/2。

避免多级 URL

常见的情况是,资源需要多级分类,因此很容易写出多级的 URL,比如获取某个作者的某一类文章。

# GET /au^ 2 o F 9thors/12/categories/2

这种 URL 不利于扩展,语义也不明确,往往要想一会,才能明白含义。
更好的做法是,除了第一级,其他级别都用查询字符串表达。

# GET /authors/12?categories=2

下面是另一个例子,查询已发布的文章。l R y h M你可能会设计成下面的 URL。

# GET /articles/published

查询字符串的写法明显更好

# GET /art% P 2 r n v [ l Xicles?published=true

状态码必须精确

客户端的每一次请求,服务器都必须给出回应。回应e $ 6 0 - ~ x a e包括 HTTP 状态码和数据两部分。
HTTP 状态码就是一个三位数,分成五个类别。

# 1xx:相关信息# 2xx:操作成功# 3C D f ixx:重定向# 4xx:客户端错误# 5xx:服务器错误

这五大类总共包含` k E % x A ] C R100多种状态码,; a Q ! 9覆盖了绝大部分可能遇到的情况。每一种状p 4 U ` F P i态码都有标准的(或者约定的)解释,客户端只需查看状态码,就可以判断出发生了什么情况,所以服务器应该返回尽可能精确的状态码。

API 不w V x S b需要1xx状态码,下面介绍其他四类状态码的精确含义{ N T ?

2XX状态码

200状态码表示操作成功,但是不同的方法可以返回更精确的状态码。

# GET: 200 OK
# POST: 201 Created
# PUT: 200 OK
# PATCH: 200 OK
# DELETE:. ; Q 204b M W K 6 n No Content

上面代码中] 4 $ i e,POSTA 9 d 2 Y返回201状态码,表示生成了新的资源;DELETE返回204状态码,表示资源已经不存在。
此外,202 Accepted状态码表示服务器已经收到请求,但还未进行处理,会在未来再处理,8 { % ( , # 6通常用于异步操作。下面是一个例子。

HTTP/1.1 202 Accepted {"task": {"href": : : ! v n"/api/company/job-management/jobs/2130040","id": "2130040"}}

3xx 状态码

API 用不到301状态码(永久重定向)和302状态码(暂时重定向,307也是R g w 7 } H ~ o这个含义),因为它们可以由应用级别返回,浏览器会直接跳转,API 级别可以不考虑这 ? = S V 8 n Y两种情况。

API 用到的3xx状态码,主要是303 See Other,表示参考另一个 URL。它与302和307的含义一样,也是"暂z . ~ $ C _ C P时重定向",区别在于3` 9 E ^ R 2 ^ ` J02和307用于GET请求,而303用于POST、PUT和DELETE请求。

收到303以后,浏览器不会自] C b A } & ]动跳转,而会让用户自己决定下一步怎么办。下面是一个例子。

HTTP/1.1 303 See OtherLocation: /api/orders/12345

4xx 状态码

4xN ? x J f O ; Ox状态码r ) i & {表示客户端错误,主要有下面几种。
400 Bad Request:服务器不理解客户端的请求,未做任何处理。
401 Unauthorized:用户未提供身份验证凭据! R v 4 # 7,或者没有H J W n通过身份验证。
403 ForbiF , . q z g # 0dden:用户通过了身份验证,但是不具有访问资源所需的权限。
4T Z y m04 N/ + ` # E % jot Found:所请求的资源不存在,或不可用。
405 Method Not Allowed:用户已经通过身份验证,但是所用的 HTTP 方法不X B - z @ c 9在他的权限之内。
410 Gone:所请求的资源已从这个地址转移,不再K X p可用。
415 Unsupported{ X # Med{ , ~ia Type:客户端要求的返回y 9 Q W m ! k格式不支持。比如,API 只能返回 JSON 格式,但是客户端要求返回 XML 格式。
4@ / b F P22 Unprocessable Entity :客户端上传的附件无法处理,导致请求失败。
429 Too Many Requests:客, Y b h户端的请求次数超过限额。

5xx 状态码

5xx状态码表示服务端错误。J 0 5 g R一般来说,API 不会向Q p { Z {用户透露服务器的详细信息@ * & K O % A,所以只要两个状态码就够了。
500 Internal Ser- & ( ^ $ Y K jver Error:客户端请求有效,服务器处理时发生了意外。
503 Service Unavailable:服务器无法处理请求,一般用于网站维护状态。

不要返回纯本文

API 返回的数据格式,不应该是纯文本,而应该是一个 JSON 对象,因为这样才能返回标准的结构化数据。所以,服务器回应的 HTTP 头的Content-Type属性要设为app- I A | m U ^ z .lication/json。
客户端请求时. W i 9 F L ; Y= x r - G B % B也要明确告诉服务器,可以接受 JSON 格式,即请求的 HTTP 头的ACCEPT属性也要设成application/json。下面是一个例子。

GET /orders/2 HTTP/1.1 Accept: aN [ 8pplication/json

发生错误时,g Y k U不要返回 200 状态码
有一种不恰当的做法是,即使发生错误,也返5 $ H R _回200状态码,把错误信息放在数据体里面,就像下面这样。

HTTP/1.1 200 OK Content-Type: application/json {"staJ 5 K Z . Dtus": "failur] E %e","data": {"error": "Expected at least two items in list."}}

上面代码中,解析数据体以后,才能得知操作失败。
这张做法实际上取消了状态码,这是完全不可取的。正确的做法是,状态码反映发生的错误,具? W ( C体的错误信息} q C放在数据体U l ( f ;里面返回。下面P M a是一个例子。

HTTP/1.1 400 Bad RequestContent-Type: application/json{  "erry h Q 7or": "Invalp h D g ~ F p . yid payoad.",  "detail": {     "surname": "This field is required."  }}

提供链接

API 的使用者} 7 e R X L o 1未必知道,URL 是怎么设计的。一个解决方法就是,在回应中,给出相关链接,便于下一步操作。这样的话,用户只要记住一个 URL,就可以发现其他的 URL。这种方法叫做 HATEOAS。
举例来说,GitHub 的 API 都在 a0 2 I 8 m k i 7pi.github.com 这个域名。访问它,就可以得到其他 URL。

{  ...  "feeds_url": "https://api.github.com/feeds",  "followers_url": "https://api.github.com/user/followers",  "followingN 1 x h _ :_url": "https://api.github.com/user/following{/targe5 } a 4 8 i % #t}",  "gists_url": "https://api.github.com/gist% ` ? ^s{/gist_id}",  "hub_url": "https://api.github.com/v / ` ?hub",  ...}

上面的回应中, d ; 7 b,挑一个 URL 访问,又可以得到别的 URL。对于用户来 ] z p说,{ 5 } s n I .不需要记住 URL 设计,只要从 api.github.com 一步步查找就可以了。
HATEOAS 的格式没有统一规定,上面例子中Z + R =,GitHub 将它们与其他属性放在一起。更好的做法应该是,将相关链接与其他属性分开。

HTTP/1.1 200 OK Content-Type: ap$ * ! {plication/jV x Y . P Y 8 Wson{"n H W tstatus": "In progress",r & 5 A ."links": {7 @ # p C u H _ S[{ "rel":"can5 & ! O lcel", # W r L"method": "delete", "href":"/api/status/12345, K c" } ,{ "rel":"edit"! 7 =, "method": "put", "g : ? fhref":"/api/status/12345" }]}}

【云栖号在( * S @ C线课堂】每天都有产品技术专家分享!
课程地址:https://yqh.aliyun.com/zhibo

立即加入社群,与专家面对面,及时L p ! @ 1了解课程最新动态!
【云栖号在线课堂 社群】https://c.tb.cn/F3.Z8s n wgvnK

原文发布时间:2021 - 2 g O $ 60-06-16
本文作者:阮一峰
本文来自:“互联L o T 9网架构师 微信公众号”,了解相关信息可以关注“互联网架; ^ b I构师”