原文:python简单进阶之web框架:fastapi使用教程 - 知乎
这是简单进阶教程系列第四篇,本系列文章主要介绍那些可以很快上手的进阶库。
我其实学过一段时间Django框架,但是半途而废了,我觉得可能还是简单一点的框架比较适合我吧……
官方教程:https://fastapi.tiangolo.com/
安装
要求 python3.6版本及以上
pip install fastapi
pip install uvicorn
教程
第一步
from fastapi import FastAPI
app = FastAPI() # 创建API实例
@app.get("/")
async def root():
return {"message": "Hello World"}
代码解释
@app.get("/")
功能是定义路径操作,代表着访问example.com/
时执行GET操作。
路径,即网址第一个斜杠到最后的部分,比如https://example.com/items/foo
的路径就是/items/foo
,通常也称为端点或 路由
操作,即GET,POST,PUT,DELETE等HTTP方法
在python中,@something
被称为装饰,意味着采用下面的函数进行处理。
async def
是定义异步函数的方法,你也可以定义为普通函数def
简单来说,如果你的程序不需要执行的先后顺序(比如先访问数据库,再返回字典),那么可以用异步,否则的话用普通的函数即可
return
可以返回dict,list,str,int等等。
运行
将其复制到main.py
,打开cmd
,输入uvicorn main:app --reload
,即可运行。
参数解释。main
:文件main.py
。app
:main.py
内创建的对象app = FastAPI()
。--reload
:更改代码后服务器重新启动,仅用于开发。
打开浏览器输入地址http://127.0.0.1:8000
,即可看到成功返回
{"message": "Hello World"}
输入http://127.0.0.1:8000/docs
,即可看到交互式文档;输入http://127.0.0.1:8000/redoc
即可看到API文档。
路径参数
传递参数
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(item_id):
return {"item_id": item_id}
使用大括号将输入的参数括起来,即可将同名参数传递给下面的函数。
输入http://127.0.0.1:8000/items/foo
,返回{"item_id":"foo"}
参数类型
@app.get("/items/{item_id}")
async def read_item(item_id: int):
return {"item_id": item_id}
使用def func(para:type)
的格式定义参数数据类型。
这样也有将输入参数自动进行类型转换的效果,比如http://127.0.0.1:8000/items/3
,那么返回的就是个int而不是str。当然如果无法转换也会有友好的错误提示。
比如输入http://127.0.0.1:8000/items/fool
或http://127.0.0.1:8000/items/3.2
都会返回错误
创建枚举类
from enum import Enum
from fastapi import FastAPI
class ModelName(str, Enum):
alexnet = "alexnet"
resnet = "resnet"
lenet = "lenet"
app = FastAPI()
@app.get("/model/{model_name}")
async def get_model(model_name: ModelName):
if model_name == ModelName.alexnet:
return {"model_name": model_name, "message": "Deep Learning FTW!"}
if model_name.value == "lenet":
return {"model_name": model_name, "message": "LeCNN all the images"}
return {"model_name": model_name, "message": "Have some residuals"}
如果有好几个预定义参数有相同的数据类型,那么可以使用enum
模块,并通过类名.参数名
调用。
PS:alexnet、resnet、lenet都是机器学习术语,可以换成任何参数。
它最大的好处就是可以在文档中显示可用参数
查询参数
声明不属于路径参数的其他功能参数时,它们将自动被解释为“查询”参数。
from fastapi import FastAPI
app = FastAPI()
fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
@app.get("/items/")
async def read_item(skip: int = 0, limit: int = 10):
return fake_items_db[skip : skip + limit]
在HTTP路径中,查询参数表现为URL中位于?
之后,以&
字符分隔的键值对。
比如http://127.0.0.1:8000/items/?skip=0&limit=1
即skip
为0,limit
为1
返回[{"item_name":"Foo"},{"item_name":"Bar"}]
默认值
上面例子中skip: int = 0
即设定skip参数默认为0,那么我们访问http://127.0.0.1:8000/items/
,等同于http://127.0.0.1:8000/items/?skip=0&limit=10
可选参数
把默认值设为None
即可,如q: str = None
多路径和查询参数组合
from fastapi import FastAPI
app = FastAPI()
@app.get("/users/{user_id}/items/{item_id}")
async def read_user_item(
user_id: int, item_id: str, q: str = None, short: bool = False
):
item = {"item_id": item_id, "owner_id": user_id}
if q:
item.update({"q": q})
if not short:
item.update(
{"description": "This is an amazing item that has a long description"}
)
return item
请求体
注:要使用请求体,就不能使用GET
操作,而用POST
(较常见), PUT
,DELETE
或PATCH
。
from fastapi import FastAPI
from pydantic import BaseModel
# 将数据模型定义为继承BaseModel的类
class Item(BaseModel):
name: str
description: str = None
price: float
tax: float = None
app = FastAPI()
@app.put("/items/{item_id}")
async def create_item(item_id: int, item: Item, q: str = None):
result = {"item_id": item_id, **item.dict()}
if q:
result.update({"q": q})
return result
看起来有丶复杂,不过我们用python请求一下就知道怎么回事了。
import requests
import json
body = {
"name": "yanshu",
"description": "yanshu's blog",
"price": 100,
"tax": 0
}
body = json.dumps(body) # 需要先解析
response = requests.put('http://127.0.0.1:8000/items/3',data = body)
print(response.text)
返回
{"item_id":3,"name":"yanshu","description":"yanshu's blog","price":100.0,"tax":0.0}
字符串验证
本节讲一下如何限定输入字符串的格式,比如最大输入字符数,必须含有XXX等等。
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: str = Query(None, min_length=3,max_length=50)): # q的最大长度为50,最小长度为3
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
正则表达式如q: str = Query(None, min_length=3, max_length=50, regex="^fixedquery$")
上述例子都是为q添加了一个默认参数None,也就是让它变成了可选参数,那我如何把它变成必须参数呢?很简单:
def read_items(q: str = Query(..., min_length=3)):
查询多个参数值
from typing import List
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: List[str] = Query(None)):
query_items = {"q": q}
return query_items
这样http://localhost:8000/items/?q=foo&q=bar
就会同时查询q为foo和bar的情况。
当然,也可以指定默认值
async def read_items(q: List[str] = Query(["foo", "bar"])):
别名参数
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: str = Query(None, alias="item-query")):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
这样,你就可以http://127.0.0.1:8000/items/?item-query=foobaritems
已弃用参数
deprecated=True
,这样这个参数仍然能用,但是文档中就会显示这个参数已经不再维护。
部署
直接使用
uvicorn main:app --host 0.0.0.0 --port 8000
即可
注意main后面直接:app
如何持续后台运行?
安装screen
yum install screen # centos
apt-get update -y #Debian
apt-get install screen -y
使用:
screen -S name # 创建一个名为name的screen窗口
screen -ls # 查看所有窗口
screen -r name # 返回名为name的窗口
exit # 退出当前窗口
虽然官方推荐gunicorn,但是我用的时候直接报
Internal Server Error
错误,没办法只能曲线救国了
想使用自己的域名?
很简单,用宝塔反向代理即可,如果想要加SSL的话,就不能使用文件验证,而要用DNS验证。