FastApi学习
fastapi框架介绍
一个构建API的现代、快速(高性能) 的web框架——更适配于前后端分离
fastapi的两个核心组件
Starlette(负责web部分,Asyncio——python3.5之后开始支持异步)
是一个轻量级的ASGI框架 ,非常适合python构建web服务
Pydantic(负责数据部分,类型提示)
from datetime import datetime
from typing import List, Optional
from pydantic import BaseModel### 类似于js的弱类型语言
class User(Basemodel):###继承Basemodel类id:int #必须是整型,必须字段name: "John" #必须是字符串,默认值,不是必填signup_ts: Optional[datetime] = None ### 有默认值friends: List[int] = [] ### 有默认值external_data = { ### 可以看做前后端分离时前端传给后端的数据'id': '123','signup_ts' '2019-0601 12:12','friends': [1,2,'3'],
} ####当数据不匹配时,Pydantic会做类型转换,但是会转换失败比如字母字符串无法转换成数据user = User(**external_data)
print(user.id)
print(repr(user.signup_ts))
print(user.friends)
HTTP协议部分
不作详细笔记,可以看其他视频复习
DIY一个web应用程序测试http协议格式
# web应用程序:遵循http协议——基于请求响应
import socketsock = socket.socket()sock.bind(("127.0.0.1",8080))sock.listen(5)while 1:conn,addr = sock.accept() # 阻塞,等待连接data = conn.recv(1024)print(data)conn.send(b"HTTP/1.1 200 ok\r\nserver:yuan\r\n\r\n<div>111</div>") # 发送数据,"yuan"是服务器表示头,用于告诉运维人员服务器是什么环境conn.close()
tips:为什么响应没有content-type也能正常显示——因为当 HTTP 响应头 没有 Content-Type 时,浏览器会尝试 自动猜测内容类型
DIY一个web应用程序测试content-type
下载安装postman——便利的测试软件
# web应用程序:遵循http协议——基于请求响应
import socketsock = socket.socket()sock.bind(("127.0.0.1",8080))sock.listen(5)while 1:conn,addr = sock.accept() # 阻塞,等待连接data = conn.recv(1024)print(data)### 增加格式conn.send(b"HTTP/1.1 200 ok\r\nserver:yuan\r\ncontent-type:text/html\r\n\r\n<div>111</div>") # 发送数据,"yuan"是服务器表示头,用于告诉运维人员服务器是什么环境conn.close()
什么是前后端分离模式 (职责分离)
-
前后端不分离客户端看到的内容所有界面效果都是由服务端提供出来的
后端服务器,在返回页面之前,把数据嵌入到页面再把整个页面当作数据返回给客户端(模板语法) -
前后端分离【 把前端的界面效果分离到另一个服务器,python服务端只负责返回数据即可 】
前端形成一个独立的网站,服务端构成一个独立的网站
restful接口开发规范(restful | RPC)
应用程序编程接口(application programming interface,api接口),就是应用程序对外提供了一个操作数据的入口,这个入口可以是一个函数或类方法,也可以是一个url地址或者一个网络地址。当客户端调用这个这个入口时,应用程序则会执行对应的代码操作,给客户端完成相对应的功能。
restful是一种专门对web开发而定义api接口的设计风格,面向资源开发——基于不同的请求动作来表达对数据的增删改查。
quick start(开始fastapi吧qaq)
安装
pip install fastapi
pip install uvicorn
使用fastAPI写一个简单的后端
from fastapi import FastAPI
import uvicornapp = FastAPI()@app.get("/") ###restful规范 路径操作装饰器
async def home(): ###async标识函数里面可以await异步 路径操作函数return {"message": "Hello, World!"}@app.get("/shop")
async def shop():return {"shop":"Welcome to the Shop!"}if __name__ == "__main__": ###如果直接运行这个文件,则执行下面的代码uvicorn.run("testfast:app", port=8080, reload=True) ###debug=True, reload=True 使得代码修改后自动重启
路径操作装饰器方法参数简介
路径操作修饰器:
@app.get()
@app.post()
@app.put()
@app.patch()
@app.delete()
@app.options()
@app.head()
@app.trace()
参数:
tags=["这是一个***测试接口"] ###影响自动化文档
summary="" ###总结
description="" ###描述
response_description="" ###响应的描述
deprecated=True/False ###是否废弃
fastapi 的路由分发 include_router
from typing import Unionfrom fastapi import FastAPI
import uvicorn
from apps.app01.urls import shop
from apps.app02.urls import userapp = FastAPI()app.include_router(shop,prefix="/shop",tags=["shop"])
app.include_router(user,prefix="/user",tags=["user"])if __name__ == "__main__" :uvicorn.run("include_router路由:app", host="127.0.0.1", port=8000, reload=True)
请求与响应
路径参数
@app.get("/user/{id}")
def get_user(id: int): ### 类型转换return {"user_id": id}
查询参数(请求参数)
post也可以有查询参数(?号后面的数据)
from fastapi import FastAPI
from typing import Unionapp02 = FastAPI()@app02.get("/jobs/{kd}")
async def get_jobs(kd,xk=None,gj:Union[str,None] = None): ### kd是路径参数,其他是查询参数,有默认参数可填可不填,无默认参数则必填### Union[str,None] = None 可以写成 Optional[str] = None# 基于kd,xl,gj数据库查询岗位信息return {"kd": kd,"xl": xl,"gj": gj,}
请求体
from fastapi import FastAPI, APIRouter
import uvicornapp03 = APIRouter()@app03.get("/user")
def get_user():return {"user":"Welcome to the Shop!"}
from fastapi import FastAPI
import uvicorn
from apps.app03 import app03app = FastAPI()app.include_router(app03,tags=["app03 请求体数据"])if __name__ == "__main__":uvicorn.run("路径操作装饰器:app", host="127.0.0.1", port=8080, reload=True)
### 扩展,类型限制可以使用Field
from pydantic import BaseModel, Fieldclass User(BaseModel):name: str = Field(..., min_length=2, max_length=50, example="Alice")email: str = Field(..., regex=r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$")price: float = Field(..., gt=0, description="价格必须大于0")age: int = Field(..., ge=18, le=100, example=25)### ......### @validator 用于对模型字段进行自定义验证,适用于 Field 无法直接满足的复杂逻辑(如跨字段校验、条件判断等)### 类可以嵌套使用限制类型
form表单数据
@app04.post("/regin")
def reg(username: str=Form(),password: str=Form()):print(f"username: {username}, password: {password}")# 这里可以添加注册逻辑,比如存储到数据库等return {"username": username}
存储在请求体里面
后端只会去x-www-form-urlencoded文件里面找
文件上传
from fastapi import APIRouter, File, UploadFile
import uvicornapp04 = APIRouter()@app04.post("/file")
def get_file(file: bytes = File()):# 适合小文件return {"file_size": len(file)}@app04.post("/files")
def get_files(files: list[bytes] = File()):# 适合小文件return {"file_size": len(files)}@app04.post("/uploadFiles")
def get_uploadFiles(file: UploadFile):# 适合小文件return {"file": file.filename}
File和UploadFile的区别
何时选择?
用 File(bytes)当:
- 文件很小(如 < 1MB)。
- 只需文件内容,无需元数据。
- 追求极简代码。
用 UploadFile 当:
- 文件较大(如 > 1MB)。
- 需要文件名、MIME 类型等信息。
- 需流式处理(如保存到云存储或数据库)。
- 需要支持暂停/恢复上传。
Request对象
request对象封装了当前HTTP请求的所有信息
from fastapi import Form, APIRouter, Request
from pydantic import BaseModel
from datetime import date
import uvicornapp05 = APIRouter()@app05.post("/items")
def get_items(request: Request):print("url",request.url)print("method",request.method)print("headers",request.headers)print("cookie",request.cookies)print("client",request.client.host)return {"message": "Items received"}
请求静态文件
在web开发中,需要请求很多静态资源文件(不是由服务器生成的的文件),如csss/js和图片文件等。
###导入
from fastapi import FastAPI,StaticFiles###挂载开放的文件夹
app.mount("/static",StaticFiles(directory="statics"))
响应模型相关参数
(1)response_model
@app.post("/items",response_model=Item) ### 路径操作参数
(2)response_model_exclude_unset
响应时排除没有设置的值
(3)INCLUDE和EXCLUDE
response_model_exclude 排除,其他的都包含
response_model_include 除了包含的,都排除
from fastapi import Form, APIRouter, Request
from pydantic import BaseModel,EmailStr
from typing import Union
from datetime import date
import uvicornapp06 = APIRouter()class UserIn(BaseModel):username: strpassword: stremail: strfull_name: Union[str, None] = Noneclass UserOut(BaseModel):username: stremail: EmailStrfull_name: Union[str, None] = Noneclass Item(BaseModel):name: strpric: Union[float, None] = Nonetax: Union[float, None] = Noneitems = {"foo": {"name": "foo", "tax": 0.1},"bar": {"name": "bar", "tax": 0.2},"baz": {"name": "baz", "tax": 0.3},
}@app06.post("/user", response_model=UserOut)
def create_user(user: UserIn):#存到数据库return user# @app06.get("/itemss/{item_id}", response_model=Item, response_model_exclude_none=True)
# def get_item(item_id: str):
# return items[item_id]# @app06.get("/itemss/{item_id}", response_model=Item, response_model_exclude={"tax"})
# def get_item(item_id: str):
# return items[item_id]# @app06.get("/itemss/{item_id}", response_model=Item, response_model_include={"tax"})
# def get_item(item_id: str):
# return items[item_id]@app06.get("/itemss/{item_id}", response_model=Item, response_model_exclude_unset=True)
def get_item(item_id: str):return items[item_id]
Jinja2模板
Jinja2 是一个现代化的、功能丰富的 Python 模板引擎,广泛用于生成动态 HTML、XML、JSON 等文本内容
模板简单来说就是一个其中包含占位变量表示动态部分的文件,模板文件在经过动态赋值后,返回给用户
jinja2是flask作者开发的一个模板系统,起初是模仿django模板的一个模板引擎,为flask提供模板支持,由于其灵活,快速和安全等优点被广泛使用。
类似于
"姓名:%s,年龄:%d"%(name,age) ###模板字符串
jinja2的变量
from fastapi import FastAPI, Request
import uvicorn
from fastapi.templating import Jinja2Templatesapp = FastAPI()templates = Jinja2Templates(directory="templates")@app.get("/index")
def index(request: Request):name = "root"age = 20books = ["金瓶梅", "红楼梦", "西游记", "三国演义"]user = {"name": "liju","age": age,"books": books}return templates.TemplateResponse("index.html",##模板文件{"request": request, ##请求对象"name":name,"age": age,"books": books,"user": user}, ##上下文对象)if __name__ == "__main__":uvicorn.run("test:app", port=8080, reload=True)
jinja2的过滤器
在html模板文件中
<p> {{ user|upper }} </p>
<p> {{ pi|round(3) }} </p>
jinja2的控制结构
分支控制
{% if age > 18 %}<div>是否显示1</div>
{% else %}<div>是否显示2</div>
{% endif %}
循环
<ul>{% for book in books %}<li>{{ book }}</li>{% endfor %}
</ul>
ORM操作(Object Relational Mapper)
即用原生python语言去映射一个sql操作
创建模型
from tortoise.models import Model
from tortoise import fields# 选课系统
class Student(Model):id = fields.IntField(pk=True)name = fields.CharField(max_length=32, description="姓名")pwd = fields.CharField(max_length=32, description="密码")sno = fields.IntField(description="学号")##一对多的关系clas = fields.ForeignKeyField("models.Clas",related_name="students")##多对多的关系courses = fields.ManyToManyField("models.Course", related_name="students", through="student_course")### through参数显性展示一个多对多的关系class Course(Model):id = fields.IntField(pk=True)name = fields.CharField(max_length=32, description="课程")class Clas(Model):id = fields.IntField(pk=True)name = fields.CharField(max_length=32,description="班级")class Teacher(Model):id = fields.IntField(pk=True)name = fields.CharField(max_length=32, description="姓名")pwd = fields.CharField(max_length=32, description="密码")tno = fields.IntField(description="教师编号")
ORM的迁移命令
tips
aerich init-db ###命令做数据迁移时无响应
- 数据库端口配置不正确——默认端口是3306
api接口与restful规范
简单来说,REST的含义就是客户端与web服务器之间进行交互的时候,使用HTTP协议中的4个请求方法代表不同的动作(面向资源开发,不在url中提现动作)
GET 获取资源
POST 新建资源
PUT 更新资源
DELETE 删除资源get
/students 查看所有学生post
/students 添加学生put
/students/1 更新id=1学生delete
/students/1 删除id=1的学生get
/students/1 查看id=1的学生
ORM查询操作
要用异步标记函数
from models import *students = await Student.all() #携程并发
print (students[0].name)### 过滤查询filter
students = await Student.filter(name="rain")
print (students[0].name)### 过滤查询get
stu = await Student.get(id=6)
print(stu.name) ### 模糊查询
stu = await Student.filter(sno__gt=2001)
print(stu[0])### value查询
stus = await Student.all().values("name")
print(stus)