客至汲泉烹茶, 抚琴听者知音

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.pyappmain.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/foolhttp://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=1skip为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(较常见), PUTDELETEPATCH

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验证。

添加新评论