API 设计风格

RPC VS RESTful 风格的 API

1 前言

  • RPC:面向过程,也就是要做一件什么事情,只发送 GET 和 POST 请求;

    • GET 用来查询信息,其他情况下一律用 POST;请求参数是动词。

  • RESTful:面向资源,这里的资源可以是一段文字、一个文件、一张图片,总之是一个具体的存在,可以使用 GET、POST、DELETE、PUT 请求,对应了增删查改的操作;请求参数是名词。

比如按照 id 查找用户:

 RPC 风格:GET /queryUser?userid=xxx;
 RESTful 风格:GET /user/{userid}

2 RPC 风格

RPC 通常包含传输协议和序列化协议,单说传输协议,RPC 可以建立在 TCP 协议之上(比如 Dubbo),也可以建立在 HTTP 协议之上(比如 gRPC);如果是基于数据形式分类,RPC 又可以分成基于二进制、XML 和 JSON 三种;

对于 RPC 来讲,它通常代表 remote procedure call, 这里我们把它理解成为 WYGOPIAO, 也就是 what you get or post is an operation. 通过这个 RPC 类型,你通过 http 协议暴露出自己的动作以及数据封装。据我所知,通常也没有什么规则可循,但是大致是这样的:

  • endpoint 通常都是包括动作名称的

  • 通常只是使用 get 或者 post 来通信

GET /someoperation?data=anId

POST /anotheroperation
{
  "data":"anId";
  "anotherdata":"another value"
}

3 REST 风格

  • endpoint 通常会包含要操作的资源

  • 通常会使用 CRUD 来描述对于资源的操作

GET /someresources/anId

PUT /someresources/anId
{"anotherdata":"another value"}

动词 + 宾语

客户端发出的数据操作指令都是"动词 + 宾语"的结构。

比如,GET /articles 这个命令,GET 是动词,/articles 是宾语。

宾语必须是名词

/getAllCars             # 错误
/createNewCar           # 错误
/deleteAllRedCars       # 错误
/articles               # 正确

复数 URL

/articles/2             # 推荐
/article/2              # 不推荐

避免多级 URL, 除了第一级,其他级别都用查询字符串表达。

# 比如获取某个作者的某一类文章
GET /authors/12/categories/2    # 不推荐,不利于扩展,语义也不明确
GET /authors/12?categories=2    # 推荐

# 查询已发布的文章
GET /articles/published         # 不推荐
GET /articles?published=true    # 推荐

3.1 如何设计为 REST 风格

需要支持 RESTful 规范,则需要支持动态路由,即 /articles/2 形式,后面的 2 是个动态值

则可以通过如下方式实现

3.1.1 约束

请求路径只有 1 级的 resource,且 URL 结尾不包含斜杠“/”

path="/resources/object/"       # 错误
path="/resources/object"        # 正确
path="/resources/"              # 错误
path="/resources"               # 正确

访问 Butterfly 时进行自动去掉末尾的 “/”

3.1.2 分析

获取 resources 和 object

# 去掉 PATH 结尾的斜杠
path = wsgienv.get("PATH_INFO", "/")
if path.endswith("/") and path != "/":
    func_name = path[:-1].encode("ascii")
else:
    func_name = path.encode("ascii")

resource_name = ''
object_key = ''
# 当 PATH_INFO 为 "/" 时,则 index_slash 为 -1
index_slash = path.find('/', 1)
if index_slash < 0 and len(path) > 1:
    resource_name = path[1:]
elif index_slash > 1:
    resource_name = path[1:index_slash]
    object_key = path[index_slash + 1:]
self.resource_name = resource_name
self.object_key = object_key

实现 api map

根据 resources+mathod 自动 map 到特定的 package/func_name

3.1.3 抛砖引玉

handler 例子

class Resources(object):
    def __init__(self, req, object_key=None)
        ...
    def list(self, arg1=xx):
        ...
    def get(self, arg1=xx):
        ...
    def create(self):
        ...
    def update(self):
        ...
    def delete(self):
        ...

---------------------------------------------------------------------------
get /app/resources/object  ===> 将 object_key 进行初始化,并调用 get 方法
get /app/resources         ===> object_key 传 None, 调用 list 方法
post /app/resources/object ===> 将 object_key 进行初始化,并调用 create 方法
等等

4 传送门

Last updated