所有代码资料:<https://github.com/L4plAcEee/learning/tree/main/FastApi
此时,幻想乡…
每天都有不计其数(确信)的妖怪前来捐赠
灵梦很苦恼,记账实在太麻烦啦~
此时baka提出了一个想法:“要不整个app用来管理自己的赛钱箱?”
说干就干!
0. 初始化
笨重什么的实在是太不优雅了~
所以说FastAPI是一个好东西
- FastAPI*
FastAPI 是一个用于构建 API 的现代、快速(高性能)的 web 框架,专为在 Python 中构建 RESTful API 而设计。
FastAPI 建立在 Starlette 和 Pydantic 之上,利用类型提示进行数据处理,并自动生成API文档。
我们用pip进行下载:
pip install fastapi # FastAPI 依赖 Python 3.8 及更高版本。
另外我们还需要一个 ASGI 服务器,生产环境可以使用 Uvicorn 或者 Hypercorn:
pip install "uvicorn[standard]"
好简单baka很快就配完了环境,现在是时候开始第一个程序啦
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| from fastapi import FastAPI
app = FastAPI()
@app.get("/") def say_hello(): ''' from fastapi import FastAPI: 这行代码从 fastapi 模块中导入了 FastAPI 类。FastAPI 类是 FastAPI 框架的核心,用于创建 FastAPI 应用程序实例。 app = FastAPI():这行代码创建了一个 FastAPI 应用实例。与 Flask 不同,FastAPI 不需要传递 __name__ 参数,因为它默认使用当前模块。 @app.get("/"): 这是一个装饰器,用于告诉 FastAPI 哪个 URL 应该触发下面的函数,并且指定了 HTTP 方法为 GET。在这个例子中,它指定了根 URL(即网站的主页)。 def say_hello():: 这是定义了一个名为 say_hello 的函数,它将被调用当用户使用 GET 方法访问根 URL 时。 return {"Hello": "reimu"}: 这行代码是 say_hello 函数的返回值。当用户使用 GET 方法访问根 URL 时,这个 JSON 对象将被发送回用户的浏览器或 API 客户端。 ''' return {"hello": "reimu"}
|
在命令行中运行以下命令以启动应用:
uvicorn main:app --reload
在根路由下,应该能看到这个
1. FastAPI 交互式 API 文档
FastAPI 提供了内置的交互式 API 文档,使开发者能够轻松了解和测试 API 的各个端点。
这个文档是自动生成的,基于 OpenAPI 规范,支持 Swagger UI 和 ReDoc 两种交互式界面。
通过 FastAPI 的交互式 API 文档,开发者能够更轻松地理解和使用 API,提高开发效率
在运行 FastAPI 应用时,Uvicorn 同时启动了交互式 API 文档服务。
默认情况下,你可以通过访问 http://127.0.0.1:8000/docs
来打开 Swagger UI 风格的文档。
2. 定义实体
baka已经定义了一个基本的根路由验证了FastAPI的正常运行~
但是不清楚模型怎么行!
现在是时候搭建一个最基本的模型啦!
- FastAPI Pydantic 模型
Pydantic 是一个用于数据验证和序列化的 Python 模型库。
它在 FastAPI 中广泛使用,用于定义请求体、响应体和其他数据模型,提供了强大的类型检查和自动文档生成功能。
FastAPI内置且推荐使用Pydantic构建模型:
使用 Pydantic 定义一个模型非常简单,只需创建一个继承自 pydantic.BaseModel 的类,并在其中定义字段。字段的类型可以是任何有效的 Python 类型,也可以是 Pydantic 内置的类型。
1 2 3 4 5 6 7
| class Donation(BaseModel): id: int signature: str donation_amount: Decimal description: Optional[str] = None
|
在以上例子中,我们会在每次回复中携带一个json格式的数据,但是每次也自己手搓也太麻烦了吧!
而且,为了统一回复风格和类型安全,我们更要约定回复风格~
所以baka选择定义了一个APIResponse模型用来约束每次回复的内容:
1 2 3 4 5
| class APIResponse(BaseModel): code: int msg: Optional[str] = None data: Optional[Any] = None
|
那么我们每次回复应该就会是这样的结构:
1 2 3 4 5 6 7
| { "code": 200, "msg": "success", "data": { } }
|
现在我们来更改一下say_hello()
让它符合baka的规范吧!
1 2 3 4
| @app.get("/")
def say_hello(): return APIResponse(code=200, msg='success', data={"hello": "reimu"})
|
然后我们收到这样了的回复:
1
| {"code":200,"msg":"success","data":{"hello":"reimu"}}
|
成功啦!
3. 定义方法
FastAPI提供了装饰器让我们直接使用http方法构建RestfulAPI端点,非常直观
HTTP 方法(HTTP Methods),也称为 HTTP 动词(HTTP Verbs),用于指定客户端与服务器之间的交互方式。
🟢 常见的 HTTP 方法
方法 |
作用 |
是否有请求体 |
是否有响应体 |
幂等性 |
安全性 |
GET |
获取资源 |
❌ 无 |
✅ 有 |
✅ 是 |
✅ 是 |
POST |
创建资源 |
✅ 有 |
✅ 有 |
❌ 否 |
❌ 否 |
PUT |
更新/替换资源 |
✅ 有 |
✅ 有 |
✅ 是 |
❌ 否 |
PATCH |
部分更新资源 |
✅ 有 |
✅ 有 |
❌ 否 |
❌ 否 |
DELETE |
删除资源 |
❌ 通常无 |
✅ 可有 |
✅ 是 |
❌ 否 |
🟡 较少使用的 HTTP 方法
方法 |
作用 |
是否有请求体 |
是否有响应体 |
幂等性 |
安全性 |
HEAD |
获取资源的头部信息(不返回正文) |
❌ 无 |
❌ 无 |
✅ 是 |
✅ 是 |
OPTIONS |
获取服务器支持的 HTTP 方法 |
❌ 无 |
✅ 有 |
✅ 是 |
✅ 是 |
TRACE |
追踪请求(通常用于调试) |
❌ 无 |
✅ 有 |
✅ 是 |
❌ 否 |
CONNECT |
建立 TCP/IP 隧道(通常用于 HTTPS 代理) |
✅ 有 |
✅ 有 |
❌ 否 |
❌ 否 |
定义路由
现在我们就根据http方法定义一些路由
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| @app.get("/donation/{id}") def reimu_get(id: int): return @app.post() def reimu_post(): return @app.put() def reimu_put(): return @app.patch() def reimu_patch(): return @app.delete() def reimu_delete(item_id: int): return @app.options() def func_option(): return
|
baka累了,想休息,先写个TODO挂着吧)
4. 定义数据源
因为之前baka已经定义了对应的模型,所以我们直接存到列表里模拟数据库,
然后使用random库随机生成测试数据:
1 2 3 4 5 6 7 8 9 10 11 12 13
| data: list[Donation] = [] def init_data(num_records: int = 10): """ 初始化随机 Donation 记录 """ global data data.clear() for i in range(1, num_records + 1): donation = Donation( id=i, signature=f"Donor_{random.randint(1000, 9999)}", donation_amount=Decimal(random.uniform(10, 1000)).quantize(Decimal("0.01")), description=random.choice(["Support education", "Medical aid", "Environmental protection", ""]) ) data.append(donation)
|
5.完善代码
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
| from fastapi import FastAPI
from pydantic import BaseModel
from typing import Optional
from typing import Any
from decimal import Decimal
import random
class Donation(BaseModel):
id: int
signature: str
donation_amount: Decimal
description: Optional[str] = None
class APIResponse(BaseModel):
code: int
msg: Optional[str] = None
data: Optional[Any] = None
data: list[Donation] = []
def init_data(num_records: int = 10):
""" 初始化随机 Donation 记录 """
global data
data.clear()
for i in range(1, num_records + 1):
donation = Donation(
id=i,
signature=f"Donor_{random.randint(1000, 9999)}",
donation_amount=Decimal(random.uniform(10, 1000)).quantize(Decimal("0.01")),
description=random.choice(["芙兰朵露", "十六夜咲夜", "红美玲", ""])
)
data.append(donation)
app = FastAPI()
init_data()
@app.get("/")
def say_hello():
"""
from fastapi import FastAPI: 这行代码从 fastapi 模块中导入了 FastAPI 类。FastAPI 类是 FastAPI 框架的核心,用于创建 FastAPI 应用程序实例。
app = FastAPI():这行代码创建了一个 FastAPI 应用实例。与 Flask 不同,FastAPI 不需要传递 __name__ 参数,因为它默认使用当前模块。
@app.get("/"): 这是一个装饰器,用于告诉 FastAPI 哪个 URL 应该触发下面的函数,并且指定了 HTTP 方法为 GET。在这个例子中,它指定了根 URL(即网站的主页)。
def say_hello():: 这是定义了一个名为 say_hello 的函数,它将被调用当用户使用 GET 方法访问根 URL 时。
return {"Hello": "reimu"}: 这行代码是 say_hello 函数的返回值。当用户使用 GET 方法访问根 URL 时,这个 JSON 对象将被发送回用户的浏览器或 API 客户端。
"""
return APIResponse(code=10001, msg="OK", data={"hello": "reimu"})
@app.get("/donation/{id}")
def reimu_get(id: int, q: str):
"""
FastAPI会自动捕获"/{id}?q='xxx'"中的查询参数并解析成python可直接处理的数据格式
"""
for it in data:
if it.id == id or it.signature == q:
return APIResponse(code=10001, msg="OK", data=it)
return APIResponse(code=10404, msg="donation record not found")
@app.post("/donation/")
def reimu_post(donation: Donation):
"""
使用 Pydantic 模型 定义了一个请求体,包含多个字段,其中一些有默认值
FastAPI会自动处理参数并包装成对应Pydantic模型
针对Post的特性,应当创建新数据时使用post
"""
for it in data:
if it.id == donation.id:
return APIResponse(code=205, msg="Reset Content")
data.append(donation)
return APIResponse(code=10001, msg="OK")
@app.put("/donation/")
def reimu_put(donation: Donation):
"""
相同的,FastAPI会自动处理参数并包装成对应Pydantic模型
不过针对put方法的特性,应当完全更新数据时使用put
"""
for index, it in enumerate(data):
if it.id == donation.id:
data[index] = donation
return APIResponse(code=10001, msg="OK")
data.append(donation)
return APIResponse(code=10001, msg="OK")
@app.patch("/donation/")
def reimu_patch(donation: Donation):
"""
相同的,FastAPI会自动处理参数并包装成对应Pydantic模型
不过针对patch方法的特性,应当更新部分字段,而不是整个资源时使用patch
"""
for index, it in enumerate(data):
if it.id == donation.id:
data[index] = donation
return APIResponse(code=10001, msg="OK")
return APIResponse(code=10404, msg="not found")
@app.delete("/donation/{id}")
def reimu_delete(id: int):
"""
相同的,FastAPI会自动处理参数并包装成对应Pydantic模型
不过针对delete方法的特性,删除资源时使用delete
"""
for it in data:
if it.id == id:
data.remove(it)
return APIResponse(code=10001, msg="OK")
return APIResponse(code=10404, msg="not found")
@app.options("/donation/")
def func_option():
return APIResponse(code=10001, msg='OK', data={"qwq": "qaq"})
|