fastapi入门

简介

该框架的速度(天然支持异步)比一般的django和flask要快N多倍

使用该框架需要保证python解释器版本是3.6及以上

Ps:django3.X版本也支持异步,但是它的异步功能并没有真正的实现,还有很多bug

FastApi官网展示了FastApi的特点

快速:非常高的性能,看齐的NodeJS和Go(感谢Starlette和Pydantic)。现有最快的Python框架之一。
快速编码:将功能开发速度提高约200%至300%。
更少的错误:减少约40%的人为错误(开发人员)。
直观:强大的编辑器支持。完成无处不在。调试时间更少。
简易:旨在易于使用和学习。减少阅读文档的时间。
短:最小化代码重复。每个参数声明中的多个功能。更少的错误。
健壮:获取可用于生产的代码。具有自动交互式文档。
基于标准:基于(并完全兼容)API的开放标准:OpenAPI(以前称为Swagger)和JSON Schema。

安装

1
2
pip3 install fastapi
pip3 install unicorn

创建

编辑一个文件main.py

1
2
3
4
5
6
7
from fastapi import FastAPI

app = FastAPI() # 初始化app

@app.get("/") # 监听GET请求
async def read_root():
return {"hello": "world"} # 返回json

以上就是最简单的一个 Fast 接口, 需要注意的是, FastApi内部处理网络io的时候使用的是Async, 但是进入函数的具体逻辑不受框架控制, 你可以写成同步,当然写成异步最佳,这里及本系列后面的都是能用异步则用异步,实在没有的再用同步

启动这个项目

在命令行下输入

1
uvicorn main:app --reload

这行命令的意思是使用 uvicorn 启动 main.py 的 app

reload 指检测到文件改动时自动重载(这在调试时非常有用)

运行后会输出

1
2
3
4
5
6
Copy(.venv) ➜  fast uvicorn main:app --reload
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [70401]
INFO: Started server process [70404]
INFO: Waiting for application startup.
INFO: Application startup complete.

代表启动成功,此时我们可以使用 postman 等调试工具测试接口

以 get 的方式请求 http://127.0.0.1:8000/

返回我们定义的数据为正常

1
{"hello": "world"}

或者

1
2
3
4
5
6
7
8
9
10
11
12
13
@app.get("/")
async def read_root():
return {"Hello": "World"}


@app.get("/items/{item_id}")
def read_item(item_id: int, q: str = '232', w: str= '2322222'):
return {"item_id": item_id, "q": q, 'w': w}


if __name__ == '__main__':
import uvicorn
uvicorn.run(app, host='127.0.0.1', port=8000)

可以在pycharm直接运行,但不推荐这个方法

模版渲染

您可以将任何所需的模板引擎与FastAPI一起使用。

常见的选择是Jinja2,与Flask和其他工具使用的选举相同。

有一些实用程序可以轻松配置它,您可以直接在FastAPI应用程序(由Starlette提供)中使用它。

安装jinja2

1
pip install jinja2

如果您还需要提供静态文件(如本例所示),请安装aiofiles

1
pip install aiofiles

main.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from fastapi import FastAPI
from starlette.requests import Request
from fastapi.templating import Jinja2Templates
app = FastAPI()
# 挂载模板文件夹 directory后面是真正的文件夹名字
templates = Jinja2Templates(directory='templates')


@app.get('/')
async def get_temp(request:Request): # async加了就支持异步
return templates.TemplateResponse('index.html',
{'request': request, # 一定要返回request
'hello': 'hello world messiless' # 额外的参数(可有可无)
}
)


@app.get('/{item_id}') # url后缀
async def get_item(request:Request,item_id):
return templates.TemplateResponse('index.html',
{'request':request,
'kw':item_id
})

index.html

1
2
3
4
5
6
7
8
9
10
11
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>hello fastapi</h1>
<h1>{{ hello }}</h1>
<h1>{{ kw }}</h1>
</body>
</html>

输入http://127.0.0.1:8000/

得到

1
2
hello fastapi
hello world messiless

输入http://127.0.0.1:8000/233333

得到

1
2
hello fastapi
233333

form表单数据交互

基本数据

注意: 如果要使用request.form()支持表单“解析”,则为必需 python-multipart 。

pip install python-multipart

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
from starlette.requests import Request
from fastapi import FastAPI,Form
from starlette.templating import Jinja2Templates


app = FastAPI()
tmp = Jinja2Templates(directory='templates')


@app.get('/') # 接受get请求
async def get_user(request:Request):
return tmp.TemplateResponse('form.html',{'request':request})


@app.post('/user/') # 接受post请求
async def get_user(request:Request,
username:str=Form(...), # 直接去请求体里面获取username键对应的值并自动转化成字符串类型
password:int=Form(...) # 直接去请求体里面获取pwd键对应的值并自动转化成整型
):
# 请注意,因为后端写的是username和password,所以前端对应的标签name属性也得为username和password
print(username,type(username))
print(pwd,type(pwd))
return tmp.TemplateResponse('form.html',{
'request':request,
'username':username,
'pwd':pwd
})

前端为

1
2
3
4
5
6
7
8
9
10
11
<form action="/user/" method="post" enctype="application/x-www-form-urlencoded">
<label>username</label>
<br>
<input type="username" name="username">
<br>
<label>password</label>
<br>
<input type="password" name="password">
<br>
<input type="submit">
</form>

文件交互

单文件上传

1
2
3
4
5
6
7
8
9
10
11
12
13
from fastapi import FastAPI, File, UploadFile

app = FastAPI()


@app.post("/files/")
async def create_file(file: bytes = File(...)):
return {"file_size": len(file)}


@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile = File(...)):
return {"filename": file.filename}

多文件上传

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from typing import List

from fastapi import FastAPI, File, UploadFile

app = FastAPI()


@app.post("/files/")
async def create_files(files: List[bytes] = File(...)):
return {"file_sizes": [len(file) for file in files]}


@app.post("/uploadfiles/")
async def create_upload_files(files: List[UploadFile] = File(...)):
return {"filenames": [file.filename for file in files]}

请注意因为后端定义为files,所以前端对应的标签name属性也得为files,否则会失败这些文件将作为“表单数据”上传。

如果您将路径操作函数参数的类型声明为bytes,则FastAPI将为您读取文件,并且您将以形式接收内容bytes

请记住,这意味着全部内容将存储在内存中。这对于小文件将非常有效。

但是在某些情况下,您可能会从中受益UploadFile

UploadFile

使用UploadFile有几个优势相对于bytes:

  • 它会使用“spooled”文件,文件存储在内存中并拥有最大的内存大小限制,并且超过了这个限制就会存储在硬盘中。
  • 意味着它同样适用于更大的文件(图片,视频,更大的二进制文件等)又不会消耗很大的内存。
  • 可以从上传文件中得到metadata
  • 它会暴露出一个python的SpooledTemporaryFile对象,你可以直接使用它进行传递。

它拥有一下属性:

  1. filename:上传的源文件的名字
  2. content_type:(MIME type/media type)(eg:image/jpeg)
  3. file:可用于直接传递的SpooledTemporaraFile对象。

同时也拥有一下async方法:

  1. write(data):
  2. read(data):
  3. seek(data):
  4. close()

这些都是async方法,需要使用await调用他们。