FastApi中的常见请求类型
后端开发语言中,我钟情于node,高效的异步处理真是让我眼前一亮,同时,简单易懂的语法也让我非常倾心
但是但是,因为考虑要写一个深度学习算法的后端接口,所以不得不选用python作为后端进行开发,并不是我不喜欢Python,相反,Python作为我的第一语言,我同样非常喜欢,只不过我更喜欢用它来做数据分析
关于Python的后端框架,在Django和fastapi中,我最终选择了fastapi。相当长的一段时间里,我都是用Django在写后端,但是Django太臃肿了,重量级的框架很多地方难以学透,在现在前后端分离的大环境下,Django的模板语法优势也并不大
相反,fastapi只专注于后端接口开发,同时也提供了和node相似的异步处理程序。在学习fastapi过程中,记录了以下几种常见的接口请求方式:
一、主程序构建
在请求之前,先记录一下如何把后端服务跑起来
根目录下创建main.py文件,代码如下:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# author:HP
# datetime:2024/6/28 16:30
from fastapi import FastAPI
import uvicorn
from apps.app01 import app01
app = FastAPI()
app.include_router(app01)
if __name__ == '__main__':
uvicorn.run("main:app", port=8001, reload=True)
两点需要注意:
-
路由分发,即
app.include_router(app01)
,路由处理函数写在于main文件同目录下的apps文件夹下,该文件夹下面有一个app01.py的路由处理文件,里面就是各种路由的处理函数,当一个项目同时存在多个子项时,可以创建多个app.py文件,实现路由分发 -
服务启动命令
uvicorn.run("main:app", port=8001, reload=True)
,很多情况下,需要在终端执行这句代码以启动服务,但这么操作非常不方便,也记不住,因此,把这行代码写在main函数中,只要运行这个程序,就可以正常启动服务了,注意,reload=True表示自动重启服务,也就是代码改了之后,服务就会自动重启
二、请求类型
请求类型大概包括路径参数、查询参数、请求体参数、form表单上传和文件上传这么几种,其中文件上传是最复杂的,我先把全部的代码放进来,然后一一解释
apps/app01.py中的代码:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# author:HP
# datetime:2024/6/28 16:30
from fastapi import APIRouter, Form, File, UploadFile
from pydantic import BaseModel
from datetime import date, datetime
from typing import List
import os
app01 = APIRouter()
class User(BaseModel):
name: str = 'root' # 默认值
age: int
birth: date
friends: List[int]
# 路径参数
@app01.get('/user/{id}')
async def get_user(id: int):
print(id)
return {
"user_id": id
}
# 查询参数
@app01.get("/jobs")
async def get_jobs(kd, gj=None, xl=None):
return {
"kd": kd,
"gj": gj,
"xl": xl,
}
# 请求体参数
@app01.post("/data")
async def get_data(data: User):
return data
# form表单数据
@app01.post('/register')
async def register(username: str = Form(), password: str = Form()):
print(username)
print('----------')
print(password)
return {
"username": username,
}
# 上传单个文件
@app01.post('/upload')
async def upload(file: bytes = File()):
# 这里的file直接就是字节流,没有其他信息
# 将文件存储到服务器
# 获取当前时间,精确到毫秒
current_time = datetime.now().strftime('%Y%m%d%H%M%S%f')[:-3] # 去掉最后的毫秒部分的最后一位,确保文件名合法
filename = current_time + '.jpg'
path = os.path.join('imgs', filename)
with open(path, 'wb') as f:
f.write(file)
return {
"status": 1,
"message": "success",
"file": filename,
}
# 上传多个文件
@app01.post('/uploads')
async def uploads(files: List[bytes] = File()):
for file in files:
print(len(file))
return {
"file": len(files)
}
# 更加常用的文件上传方式,但postman中无法测试,因为数据中不支持UploadFile类型的数据
# 上传单个文件
@app01.post('/uploadFlie')
async def upload_file(file: UploadFile):
current_time = datetime.now().strftime('%Y%m%d%H%M%S%f')[:-3] # 去掉最后的毫秒部分的最后一位,确保文件名合法
filename = current_time + '.jpg'
path = os.path.join('imgs', filename)
with open(path, 'wb') as f:
for line in file.file:
f.write(line)
return {
"origin_name": file.filename,
"sever_name": filename,
}
# 上传多个文件
@app01.post('/uploadFiles')
async def upload_files(files: List[UploadFile]):
print(files)
return {
"file": len(files),
}
1、路径参数
# 路径参数
@app01.get('/user/{id}')
async def get_user(id: int):
print(id)
return {
"user_id": id
}
请求示例:
请求参数写在路径上,通常用于处理表格中的数据,比较好理解
2、查询参数
# 查询参数
@app01.get("/jobs")
async def get_jobs(kd, gj=None, xl=None):
return {
"kd": kd,
"gj": gj,
"xl": xl,
}
postman中的请求示例:
这也挺简单的,以查询参数的形式传递参数,通常来说,就是路由中?后面的都是查询参数,用&分割
3、请求体参数
一般用是post请求中,把参数封装在body(请求体)中
# 请求体参数
@app01.post("/data")
async def get_data(data: User):
return data
这里用到了pydantic中的BaseModel模型,User模型继承于BaseModel,主要得记住写法
同样看看postman中的请求示例:
在postman中,body参数要写在raw下,这里不是表单数据,不能写在x-www-form-urlencoded中
其实也挺简单的
4、form表单参数
这里正好与第3点请求体参数比较一下
# form表单数据
@app01.post('/register')
async def register(username: str = Form(), password: str = Form()):
print(username)
print('----------')
print(password)
return {
"username": username,
}
fastapi中封装好了Form表单数据的数据类型,这和postman中的x-www-form-urlencoded定义的form数据是一样的
看看postman中的请求示例:
参数位置不一样
5、文件上传
(1)上传单个文件
# 上传单个文件
@app01.post('/upload')
async def upload(file: bytes = File()):
# 这里的file直接就是字节流,没有其他信息
# 将文件存储到服务器
# 获取当前时间,精确到毫秒
current_time = datetime.now().strftime('%Y%m%d%H%M%S%f')[:-3] # 去掉最后的毫秒部分的最后一位,确保文件名合法
filename = current_time + '.jpg'
path = os.path.join('imgs', filename)
with open(path, 'wb') as f:
f.write(file)
return {
"status": 1,
"message": "success",
"file": filename,
}
目标,前端上传文件,并将文件保存在服务器中与main.py同名的imgs文件夹下,文件名是当前时间(到毫秒).jpg
注意,这里的file是字节流,可以直接写到文件中,同样,也可以在postman中测试
服务器下的imgs文件夹下多了一张图片
(2)上传多个文件
# 上传多个文件
@app01.post('/uploads')
async def uploads(files: List[bytes] = File()):
for file in files:
print(len(file))
return {
"file": len(files)
}
这里没再写文件存储的过程,与上传单个文件差不多,看看postman中的示例:
(3)UploadFile上传单个文件
前面两种情况应该已经能满足大部分情况了,但是,fastapi封装了一种专用于文件上传的UploadFile类,代码如下:
# 更加常用的文件上传方式,但postman中无法测试,因为数据中不支持UploadFile类型的数据
# 上传单个文件
@app01.post('/uploadFlie')
async def upload_file(file: UploadFile):
current_time = datetime.now().strftime('%Y%m%d%H%M%S%f')[:-3] # 去掉最后的毫秒部分的最后一位,确保文件名合法
filename = current_time + '.jpg'
path = os.path.join('imgs', filename)
with open(path, 'wb') as f:
for line in file.file:
f.write(line)
return {
"origin_name": file.filename,
"sever_name": filename,
}
因为这是fastapi封装的,postman不兼容,所以postman没法测,用fastapi自带的接口测试文档看下示例:
同样看看服务器端的imgs目录:
可以看到,对应的地方多了一张图片
需要注意的是,这里上传的file,就并不再是纯字节流数据了,而是fastapi封装的UploadFile对象,需要知道这个对象里有哪些内容,可以查看源码,也可以借助编译器逐个去看
(4)UploadFile上传多个文件
# 上传多个文件
@app01.post('/uploadFiles')
async def upload_files(files: List[UploadFile]):
print(files)
return {
"file": len(files),
}
理解了UploadFile上传单个文件,多个文件就不难了,看看接口测试文档中的示例:
无非是多了个添加item的选项而已
对象里有哪些内容,可以查看源码,也可以借助编译器逐个去看
好了,学习完接口的请求类型,后面应该要学习orm了。。