# 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 传送门

> * [restless](https://github.com/toastdriven/restless)
> * [Google Cloud API 设计指南](https://cloud.google.com/apis/design?hl=zh-cn)
> * [RESTful API 最佳实践 -- 阮一峰](http://www.ruanyifeng.com/blog/2018/10/restful-api-best-practices.html)
> * [API 设计的几条原则](https://insights.thoughtworks.cn/how-to-design-api/)
> * <https://github.com/Jaymon/endpoints> rest 风格 api


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://meetbill.gitbook.io/butterfly-project-doc/project-framework/how/api/butterfly-rest.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
