跳转至

实训岗前培训


1 Restful API 设计规范

一种设计风格。

按照既定的设计规范,可以增加代码的可读性。

1.1 URL 设计规范

URL为统一资源定位器 ,接口属于服务端资源,首先要通过URL~~这个~~定位到资源才能去访问,而通常一个完整的URL组成由以下几个部分构成:

1
URI = scheme "://" host  ":"  port "/" path [ "?" query ][ "#" fragment ]

scheme: 指底层用的协议,如http、https、ftp
host: 服务器的IP地址或者域名
port: 端口,http默认为80端口
path: 访问资源的路径,就是各种web 框架中定义的route路由
query: 查询字符串,为发送给服务器的参数,在这里更多发送数据分页、排序等参数。
fragment: 锚点,定位到页面的资源

我们在设计API时URL的path是需要认真考虑的,而RESTful对path的设计做了一些规范,通常一个RESTful API的path组成如下:

1
/{version}/{resources}/{resource_id}

version:API版本号,有些版本号放置在头信息中也可以,通过控制版本号有利于应用迭代。
resources:资源,RESTful API推荐用小写英文单词的复数形式。
resource_id:资源的id,访问或操作该资源。

当然,有时候可能资源级别较大,其下还可细分很多子资源也可以灵活设计URL的path,例如:

1
/{version}/{resources}/{resource_id}/{subresources}/{subresource_id}

此外,有时可能增删改查无法满足业务要求,可以在URL末尾加上action,例如

1
/{version}/{resources}/{resource_id}/action

其中action就是对资源的操作。

从大体样式了解URL路径组成之后,对于RESTful API的URL具体设计的规范如下:

  1. 不用大写字母,所有单词使用英文且小写。
  2. 连字符用中杠"-"​而不用下杠"_"
  3. 正确使用 "/"​表示层级关系,URL的层级不要过深,并且越靠前的层级应该相对越稳定
  4. 结尾不要包含正斜杠分隔符"/"
  5. URL中不出现动词,用请求方式表示动作
  6. 资源表示用复数不要用单数
  7. 不要使用文件扩展名

1.2 HTTP动词

在RESTful API中,不同的HTTP请求方法有各自的含义,这里就展示GET,POST,PUT,DELETE几种请求API的设计与含义分析。针对不同操作,具体的含义如下:

1
2
3
4
5
GET /collection:从服务器查询资源的列表(数组)
GET /collection/resource:从服务器查询单个资源
POST /collection:在服务器创建新的资源
PUT /collection/resource:更新服务器资源
DELETE /collection/resource:从服务器删除资源

在非RESTful风格的API中,

  • 我们通常使用GET请求和POST请求完成增删改查以及其他操作

  • 查询和删除一般使用GET方式请求

  • 更新和插入一般使用POST请求

从请求方式上无法知道API具体是干嘛的,所有在URL上都会有操作的动词来表示API进行的动作,例如:query,add,update,delete等等。

而 RESTful 风格的 API 则要求在 URL 上都以名词的方式出现,从几种请求方式上就可以看出想要进行的操作,这点与非 RESTful 风格的 API 形成鲜明对比。

在谈及 GET,POST,PUT,DELETE 的时候,就必须提一下接口的安全性和幂等性,其中安全性是指方法不会修改资源状态,即读的为安全的,写的操作为非安全的。而幂等性的意思是操作一次和操作多次的最终效果相同,客户端重复调用也只返回同一个结果。

上述四个HTTP请求方法的安全性和幂等性如下:

HTTP Method 安全性 幂等性 解释
GET 安全 幂等 读操作安全,查询一次多次结果一致
POST 非安全 非幂等 写操作非安全,每多插入一次都会出现新结果
PUT 非安全 幂等 写操作非安全,一次和多次更新结果一致
DELETE 非安全 幂等 写操作非安全,一次和多次删除结果一致

1.3 状态码和返回数据

服务端处理完成后客户端也可能不知道具体成功了还是失败了,服务器响应时,包含状态码返回数据两个部分。

1.3.1 状态码

我们首先要正确使用各类状态码来表示该请求的处理执行结果。状态码主要分为五大类:

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

每一大类有若干小类,状态码的种类比较多,而主要常用状态码罗列在下面:

200 OK - [GET]​:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。
201 CREATED - [POST/PUT/PATCH]​:用户新建或修改数据成功。
202 Accepted - [*]​:表示一个请求已经进入后台排队(异步任务)
204 NO CONTENT - [DELETE]​:用户删除数据成功。
400 INVALID REQUEST - [POST/PUT/PATCH]​:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。
401 Unauthorized - [*]​:表示用户没有权限(令牌、用户名、密码错误)。
403 Forbidden - [*]​ 表示用户得到授权(与401错误相对),但是访问是被禁止的。
404 NOT FOUND - [*]​:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
406 Not Acceptable - [GET]​:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
410 Gone -[GET]​:用户请求的资源被永久删除,且不会再得到的。
422 Unprocesable entity - [POST/PUT/PATCH]​ 当创建一个对象时,发生一个验证错误。
500 INTERNAL SERVER ERROR - [*]​:服务器发生错误,用户将无法判断发出的请求是否成功。

1.3.2 返回结果

针对不同操作,服务器向用户返回数据,而各个团队或公司封装的返回实体类也不同,但都返回JSON格式数据给客户端。

2 文件架构

  • 将代码按功能分写到耦合度较低的不同文件中
  • 写上必要的注释
  • 详细描述接口的功能和参数
  • 对可以预想到的错误情况进行相关处理

3 项目流程

论坛项目结构示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Project Root/

├── .venv/                  # 虚拟环境文件夹,用于隔离项目的 Python 依赖

├── routers/                # 路由文件夹,里面存放不同类型的路径操作文件
   ├── __pycache__/         # Python 编译后的字节码文件
   ├── authorization.py    # 用户鉴权相关的路径操作
   ├── backstage.py         # 后台管理端相关的路径操作
   └── frontstage.py       # 前台用户端相关的路径操作

├── sql/                    # 数据库文件夹,里面存放和数据库有关操作的文件
   ├── __pycache__/         # Python 编译后的字节码文件
   ├── __init__.py         # 初始化数据库模块
   ├── crud.py             # 对数据进行增删改查的函数
   ├── database.py         # 初始化构建数据库的一些东西
   ├── models.py           # 数据库的数据表结构
   └── database.db         # 数据库文件,用于存储项目的数据

├── main.py                 # 主程序入口,通常包含 FastAPI 应用实例和启动服务器的代码

├── README.md               # 项目说明文档,提供项目的概述、安装和使用说明等

├── schemas.py              # 定义 Pydantic 模型的文件,用于数据验证和解析

└── user.http               # 用 REST client 插件(如 Insomnia 或 Postman)进行 API 测试的文件

3.1 main.py

3.1.1 跨域资源共享

21 CORS(跨域资源共享)

CORS 或者「跨域资源共享」 指浏览器中运行的前端拥有与后端通信的 JavaScript 代码,而后端处于与前端不同的「源」的情况。

21.1 源

源是协议(http​,https​)、域(myapp.com​,localhost​,localhost.tiangolo.com​)以及端口(80​、443​、8080​)的组合。

因此,这些都是不同的源:

  • http://localhost
  • https://localhost
  • http://localhost:8080

即使它们都在 localhost​ 中,但是它们使用不同的协议或者端口,所以它们都是不同的「源」。

21.2 实现

示例:

1
2
3
4
5
6
7
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  # 允许的源
    allow_credentials=True,  # 允许的凭证
    allow_methods=["*"],  # 允许的方法
    allow_headers=["*"],  # 允许的请求头
)

支持以下参数:

  • allow_origins​ - 一个允许跨域请求的源列表。例如 ['https://example.org', 'https://www.example.org']​。你可以使用 ['*']​ 允许任何源。
  • allow_origin_regex​ - 一个正则表达式字符串,匹配的源允许跨域请求。例如 'https://.*\.example\.org'​。
  • allow_methods​ - 一个允许跨域请求的 HTTP 方法列表。默认为 ['GET']​。你可以使用 ['*']​ 来允许所有标准方法。
  • allow_headers​ - 一个允许跨域请求的 HTTP 请求头列表。默认为 []​。你可以使用 ['*']​ 允许所有的请求头。Accept​、Accept-Language​、Content-Language​ 以及 Content-Type​ 请求头总是允许 CORS 请求。
  • allow_credentials​ - 指示跨域请求支持 cookies。默认是 False​。另外,允许凭证时 allow_origins​ 不能设定为 ['*']​,必须指定源。
  • expose_headers​ - 指示可以被浏览器访问的响应头。默认为 []​。
  • max_age​ - 设定浏览器缓存 CORS 响应的最长时间,单位是秒。默认为 600​。

中间件响应两种特定类型的 HTTP 请求……

21.2.1 开发环境下

1
2
3
4
5
6
7
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  # 允许的源
    allow_credentials=True,  # 允许的凭证
    allow_methods=["*"],  # 允许的方法
    allow_headers=["*"],  # 允许的请求头
)

可以使用通配符 ["*"]​ 来允许所有

21.2.2 生产环境下

将源改为前端的具体的源,以提高安全性

3.1.2 database.py

内容固定,包含创建数据库表的函数 create_db_and_tables()

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from typing import Annotated

from fastapi import Depends
from sqlmodel import Session, SQLModel, create_engine


sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"

connect_args = {"check_same_thread": False}
engine = create_engine(sqlite_url, connect_args=connect_args)


def create_db_and_tables():
    SQLModel.metadata.create_all(engine)


def get_session():
    with Session(engine) as session:
        yield session


SessionDep = Annotated[Session, Depends(get_session)]