# KnowledgeBase **Repository Path**: seiwunsei/knowledge-base ## Basic Information - **Project Name**: KnowledgeBase - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2024-03-19 - **Last Updated**: 2024-03-21 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # KnowledgeBase #### 介绍 自己收集和整理的学习资料 #### Web Api https://zhuanlan.zhihu.com/p/621540464?utm_id=0 1. 通常情况下,URL 的模型如下所示: /$(prefix)/$(module)/$(model)/$(sub-model)/$(verb)?$(query)#${fragment} 其中,Prefix 可能是 API 的版本,也可能是特殊限定,如有些公司会靠此进行接入层分流; Module 是业务模块,也可以省略; Model 是模型; SubModel 是子模型,可以省略; Verb 是动词,也可以省略; Query 是请求参数; Fragment 是 HTTP 原语 Fragment。 2. 版本在URL中的优势 我们在设计 URL 时遵循一致性的原则,无论是哪种身份或状态,都会使用相同的 URL 来访问同一个资源。 这也是 Uniform Resource Location 的基本原则。 虽然我们可以接受不同的内容格式(例如 JSON / YAML / HTML / PDF / etc), 但是我们希望资源的位置是唯一的。 然而,问题是,对于同一资源在不同版本之间的呈现,是否应该在 URL 中体现呢?这取决于设计者是否认为版本化属于位置信息的范畴。 Vincent: 比如我们定义一个资源Student, 我们一开始定义的呈现可能是这样 `{ Name: 'Vincent', Class: 3, Level: Good }` 后来我们发现这些信息还不够,另外有些属性的命名也需要改一下,但是这个Api已经被很多客户端使用了,怎么办呢,我们可以定义一个 V2版本的Student呈现 `{ Name: 'Vincent', Grade: 7, Class: 3, Score: 90 }` 3. HTTP Verb不够用怎么办 在 REST 设计中,我们需要使用 HTTP 的 GET / POST / PUT / DELETE / PATCH / HEAD 等动词对资源进行操作。 比如使用 API GET /apis/books 查看书籍列别,这个自然且合理。 但是,当需要执行类似「借一本书」这样的动作时, 我们没有合适的动词(BORROW)来表示。 针对这种情况,有两种可行的选择: 使用 POST 方法与自定义动词,例如 POST /apis/books/borrow,表示借书这一动作; 创建一个借书记录,使用资源新增方式来结构不存在的动作,例如 POST /apis/books/borrow-log/; 这个问题在复杂的场景中会经常出现,例如用户登录(POST /api/auth/login vs POST /api/session)和帐户转账(vs 转账记录创建)等等。 API 抽象还是具体,始终离不开业务的解释。 我们不能简单地将所有业务都笼统概括到 CRUD 上面, 而是需要合理划分业务,以便更清晰地实现和让用户理解。 在进行设计时,我们可以考虑是否需要为每个 API 创建一个对应的按钮来方便用户的操作。 如果系统中只有一个名为 /api/do 的 API 并将所有业务都绑定在其中,虽然技术上可行, 但这种设计不符合业务需求, 每一层的抽象都是为了标准化解决特定问题的解法。 4. Readable Stable Identity 解释 在标记一个资源时,我们通常有几种选择: 使用 ID:ID 通常与数据库自增 ID 绑定。 使用 GUID:例如 UUID,尽管不那么精确。 使用可读性和稳定性标识符(Readable Stable Identity):通常使用名称、UID 或特定 ID(如主机名、IP 地址或序列号)来标识, 要求该标识符具有稳定性且全局唯一,在内部系统中非常有用。 我个人有一个设计小技巧:使用 ${type}/${type-id} 形式的 slug 来描述标识符。Slug 是一种人类可读的唯一标识符, 例如 hostname/abc.sqa 或 ip/172.133.2.1。 这种设计方式可以在可读性和唯一性之间实现很好的平衡。 A slug is a human-readable, unique identifier, used to identify a resource instead of a less human-readable identifier like an id . 5. 一级和多级的解释 URL 的层级设计可以根据建模来进行,也可以采用直接单层结构的设计。具体问题的解决方式, 例如在设计用户拥有的书籍时,可以选择多级结构的 /api/users/foo/books 或一级结构的 /api/books?owner=foo。 技术上这两种方案都可以,前者尊重模型的归属关系,后者则是注重 URL 结构的简单。 多级结构更直观,但也需要解决可能存在的多种组织方式的问题,例如图书馆中书籍按照作者或类别进行组织? 这种情况下,可以考虑在多级结构中明确模型的归属关系, 例如 /api/author/foo/books(基于作者)或 /api/category/computer/books(基于类别)。 6. Alias URL 解释 对于一些频繁使用的 URL,虽然可以按照 URL 规则进行设计,但我们仍然可以设计出一个更为简洁的 URL, 以方便用户的展示和使用。这种设计在 Web URL 中尤其常见。比如一个图书馆最热门书籍的 API: 原始 URL https://test.com/apis/v3/books?sort=hot&limit=10 Alias URL https://test.com/apis/v3/books/hot 7. 关于全盘 POST 有些新手(或者自认为有经验的人)可能得出一个错误的结论,即除了 GET 请求以外, 所有的 HTTP 请求都应该使用 POST 方法。甚至有些人要求 所有行为(即使是只读的请求)也应该使用 POST 方法。 这种观点通常会以“简单一致”、“避免缓存”或者“运营商的要求”为由来支持。 然而,我们必须明白 HTTP 方法的设计初衷:它是用来描述资源操作类型的,从而派生出了包括缓存、安全、幂等性等一系列问题。 在相对简单的场景下,省略掉这一层抽象的确不会带来太大的问题,但一旦进入到复杂的领域中, 使用 HTTP 方法这一层抽象就显得非常重要了。这是否遵循标准将决定你是否能够获得标准化带来的好处, 类比一下就像一个新的手机厂商可以选择不使用 USB TypeC 接口。 技术上来说是可行的,但同时也失去了很多标准化支持和大家心智上的约定俗成。 8. 2XX 状态码家族 HTTP 状态码的用途在于表明客户端与服务器间通信的结果。2XX 状态码系列代表服务器已经成功接收、 理解并处理了客户端请求,回应的内容是成功的。以下是 2XX 系列中常见的状态码及其含义: 200 OK:请求已成功处理,服务器返回了响应。 201 Created:请求已经被成功处理,并且在服务器上创建了一个新的资源。 202 Accepted:请求已被服务器接受,但尚未执行。该状态码通常用于异步处理。 204 No Content:请求已成功处理,但是服务器没有返回任何响应体内容。 2XX 系列的状态码表示请求已被成功处理,这些状态码可以让客户端明确知晓请求已被正确处理,从而进行下一步操作。 是否需要全面使用 2XX 系列的状态码,取决于是否需要向客户端明确/显示的信息, 告知它下一步动作。如果已经通过其他方式(包括文档、口头协议)描述清楚, 那么确实可以通盘使用 200 状态码进行返回。但基于行为传递含义, 或是基于文档(甚至口头协议)传递含义,哪种更优秀呢?是更为复杂还是更为简洁? 9. Request 最佳实践 1) 分页方式解释 我们最为常见的两种分页方式是 Page-based 和 Offset-based,可以通过公式进行映射。 2)分页控制着解释 在某些情况下,我们需要区分客户端分页(Client Pagination)和服务器分页(Server Pagniation)。 客户端分页是指下一页的参数由客户端计算而来,而服务器分页则是由服务器返回 rel 或 JSON.API 等协议。 使用服务器分页可以避免一些问题,例如批量屏蔽了一些内容,如果使用客户端分页,可能会导致缺页或者白屏。 10. Response 最佳实践 1)模型的几种形式 Business Object(BO):原始的业务模型 Data Object(DO):存储到 RDBMS 的模型,所以必须是打平的字段结构,有时候一个 BO 会对应到多个 DO View Object(VO):呈现到表现层的模型,只保留用户需要看到信息,比如会去掉敏感信息 Data Transfer Object(DTO):用来在 RPC 系统进行传输的模型,一般和 原始的 Model 差异不大,根据不同序列化系统会有差异 (比如枚举的处理) 除此之外,还经常使用两类:Rich Model 和 Tiny Model(请忽略命名,不同团队叫法差异比较大): Rich Model:用来描述一个丰富模型,这个模型包含了几乎所有需要用的的数据,也允许子资源进行嵌套 Tiny Model:是一个精简模型,往往用来在列表 API 里面被使用 2)模型的连接、侧载和嵌入 在 API 设计中,我们经常需要处理一个模型中包含多个子模型的情况,例如 Book 包含 Comments。 对于这种情况,通常有三种表现形式可供选择:链接(Link)、侧载(Side)和嵌入(Embed) 链接(有时候这个 URL 也会隐藏,基于客户端和服务端的隐式协议进行请求): `{ "data": { "id": 42, "name": "朝花夕拾", "relationships": { "comments": "http://www.domain.com/book/42/comments", "author": [ "http://www.domain.com/author/鲁迅" ]} } }` 侧载: `{ "data": { "id": 42, "name": "朝花夕拾", "relationships": { "comments": "http://www.domain.com/book/42/comments", "authors": [ "http://www.domain.com/author/鲁迅" ]} }, "includes": { "comments": [ { "id": 91, "author": "匿名", "content": "非常棒" } ], "authors": [ { "name": "鲁迅", "description": "鲁迅原名周树人" }] } }` 嵌入: `{ "data": { "id": 42, "name": "朝花夕拾", "comments": [ { "id": 91, "author": "匿名", "content": "非常棒" }], "authors": [{ "name": "鲁迅", "description": "鲁迅原名周树人" }] } }` 11. 认证鉴权方案 在 Web API 设计中,常见的认证方式包括:HTTP Basic Auth、OAuth2 和账号密码登录等。 常用的状态管理方式则有 Bearer Token 和 Cookie。此外,在防篡改等方面,还会采用基于 HMac 算法的防重放和篡改方案。 12. Github 风格的Restful Api Github 的 API 是我常常参考的对象。它对其业务领域建模非常清晰,提供了详尽的文档,使得沟通成本大大降低。 我主要参考以下两个链接: API 定义[GitHub REST API documentation](https://docs.github.com/en/rest?apiVersion=2022-11-28) 和 面向应用程序提供的 API 列表 [Endpoints available for GitHub Apps](https://docs.github.com/en/rest/authentication/endpoints-available-for-github-app-installation-access-tokens?apiVersion=2022-11-28) ,该列表几乎包含了 Github 的全部 API。 问题 选择 备注 URL API Path 里面 Prefix 二级域名 https://api.github.com Path 里面是否包含 API 版本 Header X-GitHub-Api-Version API Versions Path 是否包含 Group Path 是否包含动作 看情况(如果 HTTP Verb CRUD 无法满足就包含) 比如 P UT /repos/{owner}/{repo}/pulls/{pull_number}/merge POST /repos/{owner}/{repo}/releases/generate-notes 模型 ID 形式 Readable Stable Identity URL 中模型单数还是复数 复数 资源是一级(平铺)还是多级(嵌套) 多级 搜索如何实现,独立接口(/models/search)还是基于列表/models/ 接口 独立 是否有 Alias URL ? URL 中模型是否允许缩写(或精简) 没有看到明显信息,基于多级模型也不需要,但是存在 GET /orgs/{org}/actions/required_workflows URL 中模型多个词语拼接的连字符 - 和 _ GET /repos/{owner}/{repo}/git/matching-refs/{ref} vs GET /orgs/{org}/actions/required_workflows 是否要区分 Web API 以及 Open API(面向非浏览器) Header 是否所有 Verb 都使用 POST 修改(Modify)动作是 POST 还是 PATCH? PATCH HTTP Status 返回值 充分利用 HTTP Status 常用,包括限流洗损 是否使用考虑限流系统 ✅ 429 是否使用缓存系统 ✅ ETag / Last Modify Resources in the REST API#client-errors 是否校验 UserAgent ✅ 是否校验 Referrral Request 复杂的参数是放到 Form Fields 还是单独一个 JSON Body Body 参考 Pulls#create-a-pull-request 子资源是一次性查询还是独立查询 嵌套 从 Pulls 进行判断 分页参数存放 URL Query 分页方式 Page Using pagination in the REST API 分页控制者 服务端 同上 Response 模型呈现种类 多种模型 比如 Commits 里面的 明细和 Parent Commits 大模型如何包含子模型模型 核心模型 + 多次关联资源查询? 没有明确说明, 根据几个核心 API 反推 字段返回是按需还是归并还是统一 统一 字段表现格式 Snake 错误码 无 Resources in the REST API#client-errors 错误格式 全局统一 Resources in the REST API#client-errors 时区 复合方案(ISO 8601 > Time-Zone Header > User Last > UTC) Resources in the REST API#Timezones HATEOAS