FastAPI初识

FastAPI

准备

官网:https://fastapi.tiangolo.com/

Documentation: https://fastapi.tiangolo.com

Source Code: https://github.com/tiangolo/fastapi

FastAPI 框架,高性能,易于学习,高效编码,生产可用

FastAPI 是一个用于构建 API 的现代、快速(高性能)的 web 框架,使用 Python 3.6+ 并基于标准的 Python 类型提示。

关键特性:

  • 快速:可与 NodeJS 和 Go 比肩的极高性能(归功于 Starlette(网络工具包) 和 Pydantic(数据认证))。最快的 Python web 框架之一。
  • 高效编码:提高功能开发速度约 200% 至 300%。*
  • 更少 bug:减少约 40% 的人为(开发者)导致错误。*
  • 智能:极佳的编辑器支持。处处皆可自动补全,减少调试时间。
  • 简单:设计的易于使用和学习,阅读文档的时间更短。
  • 简短:使代码重复最小化。通过不同的参数声明实现丰富功能。bug 更少。
  • 健壮:生产可用级别的代码。还有自动生成的交互式文档。
  • 标准化:基于(并完全兼容)API 的相关开放标准:OpenAPI (以前被称为 Swagger) 和 JSON Schema。

FastAPI可以用来做什么

  • 由Sebastian Ramirez 开发
  • 开发 web API
  • 开发网站
  • 做一个测试平台
  • 做一个持续集成工具
  • 生成文档

为什么要学习FastAPI

  • FastAPI的使用方法和设计参考了几十个python明星项目,博采众长,吸收各家精华
  • FastAPI和Fast一样简约
  • FastAPI非常新,并且基于python3.6的类型声明系统
  • 自动生成文档
  • 类型声明和数据检验
  • 内置身份认证
  • 性能强
  • 原生支持异步
  • 强大的依赖注入系统
  • GraphQL和Websocket支持

可选依赖¶

用于 Pydantic:

  • ujson - 更快的 JSON 「解析」。
  • email_validator - 用于 email 校验。

用于 Starlette:

  • requests - 使用 TestClient 时安装。
  • aiofiles - 使用 FileResponse 或 StaticFiles 时安装。
  • jinja2 - 使用默认模板配置时安装。
  • python-multipart - 需要通过 request.form() 对表单进行「解析」时安装。
  • itsdangerous - 需要 SessionMiddleware 支持时安装。
  • pyyaml - 使用 Starlette 提供的 SchemaGenerator 时安装(有 FastAPI 你可能并不需要它)。
  • graphene - 需要 GraphQLApp 支持时安装。
  • ujson - 使用 UJSONResponse 时安装。

用于 FastAPI / Starlette:

  • uvicorn - 用于加载和运行你的应用程序的服务器。
  • orjson - 使用 ORJSONResponse 时安装。

你可以通过 pip install fastapi[all] 命令来安装以上所有依赖。

# python和pycharm安装
# anaconda创建虚拟环境
conda create -n fastapi python=3.9
# 进入/切换到指定名称的虚拟环境,如果不带任何参数,则默认回到全局环境base中。
# conda activate  <虚拟环境名称>
conda activate fastapi 
# 退出当前虚拟环境
conda deactivate

安装 FastAPI¶

# 安装所有的可选依赖及对应功能包括了 uvicorn,你可以将其用作运行代码的服务器
pip install fastapi[all]  # 安装所有组件
# 分开来安装
pip install fastapi
# 安装uvicorn来作为服务器:
pip install uvicorn[standard]

创建FastAPI项目并运行

from fastapi import FastAPI

app = FastAPI()


@app.get("/")
async def root():
    # 返回字符串
    # return "hello world"
    # 返回列表
    # return ["项目1", "项目2", "项目3"]
    # 返回json格式
    return {"message": "Hello World"}


@app.get("/hello/{name}")
async def say_hello(name: str):
    return {"message": f"Hello {name}"}

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

# uvicorn main.py:app --reload

url和url模板渲染、template

main.py代码:

from fastapi import FastAPI  # 导入fastapi
from starlette.requests import Request
from starlette.templating import Jinja2Templates

app = FastAPI()
templates = Jinja2Templates(directory="templates")


@app.get("/")
async def main(request: Request):
    #  context: dict, 上下文管理器 flask没有这个是因为它本身做了这个 不写会报错
    # hello 键 是变量 会传给 templates文件夹中的index.html
    return templates.TemplateResponse('index.html', {'request': request, 'hello': 'world'})


@app.get("/{item_id}/")
async def item_id(request: Request, item_id):
    return templates.TemplateResponse('index.html', {'request': request, "item_id": item_id})


if __name__ == '__main__':
    import uvicorn

    uvicorn.run(app, host="127.0.0.1", port=8000)

# uvicorn main.py:app --reload

templates/index.html代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>HELLO FastAPI...</h1>
    <h1>{{ hello }}</h1>
    <h1>{{ item_id }}</h1>
</body>
</html>

form表单与登录页面

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>HELLO FastAPI...</h1>
    <h1>{{ hello }}</h1>
    <h1>{{ item_id }}</h1>
    <h1>HELLO...{{ username }}</h1>
    <h1>HELLO...{{ password }}</h1>
</body>
</html>

post.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div class="container">
    <form action="/user/" enctype="application/x-www-form-urlencoded" method="post">
        <label for="">username</label>
        <br>
        <input type="username" name="username">
        <br>
        <label for="">password</label>
        <br>
        <input type="password" name="password">
        <br><br>
        <input type="submit">
    </form>
</div>
</body>
</html>

main.py

from fastapi import FastAPI, Form
from starlette.requests import Request
from starlette.templating import Jinja2Templates

app = FastAPI()
templates = Jinja2Templates(directory="templates")


@app.post("/user/")
async def create_upload_files(request: Request, username: str = Form(...), password: str = Form(...)):
    # flask的做法:写在函数体里面 Request.get.form('index.html')
    print('username', username)
    print('password', password)

    # return {'text_1': text_1, 'text_2': text_2}
    return templates.TemplateResponse('index.html', {'request': request, 'username': username, 'password': password})


@app.get("/")
async def main(request: Request):
    #  context: dict, 上下文管理器 flask没有这个是因为它本身做了这个 不写会报错
    # hello 键 是变量 会传给 templates文件夹中的index.html
    return templates.TemplateResponse('post.html', {'request': request})


if __name__ == '__main__':
    import uvicorn

    uvicorn.run(app, host="127.0.0.1", port=8000)

# uvicorn main.py:app --reload

file上传文件

main.py

from fastapi import FastAPI, Form, File, UploadFile
from typing import List
from starlette.requests import Request
from starlette.templating import Jinja2Templates

app = FastAPI()
templates = Jinja2Templates(directory="templates")


@app.post("/files/")
async def files(
        request: Request,
        files_list: List[bytes] = File(...),  # List 可以上传多个
        files_name: List[UploadFile] = File(...),
):
    return templates.TemplateResponse('index.html',
                                      {
                                          'request': request,
                                          'file_sizes': [len(file) for file in files_list],  # 字节长度表示文件大小
                                          'filenames': [file.filename for file in files_name],  # 文件名
                                      })


@app.post("/create_file/")
async def create_file(
        request: Request,
        file: bytes = File(...),  # 单个文件 bytes这里指定什么类型,后面就自动给它什么类型(前提是有这个类型)
        fileb: UploadFile = File(...),
        notes: str = Form(...),
):
    return templates.TemplateResponse('index.html',
                                      {
                                          'request': request,
                                          "file_size": len(file),  # 文件大小
                                          "notes": notes,  # 注释
                                          "fileb_content_type": fileb.content_type,  # 文件类型
                                      })


@app.get("/")
async def main(request: Request):
    return templates.TemplateResponse("post.html", {"request": request})


if __name__ == '__main__':
    import uvicorn

    uvicorn.run(app, host="127.0.0.1", port=8000)

# uvicorn main.py:app --reload

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <div class="container">
        <h1>NO.1</h1>
        <h2>File Name : {{ filenames }}</h2>
        <h2>File Size : {{ file_sizes }}</h2>

        <h1>NO.2</h1>
        <h2>File Size : {{ file_size }}</h2>
        <h2>File Notes : {{ notes }}</h2>
        <h2>File Type : {{ fileb_content_type }}</h2>
    </div>
</body>
</html>

post.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/files/" enctype="multipart/form-data" method="post">
        <input name="files_list" type="file" multiple><br><br>
        <input name="files_name" type="file" multiple><br><br>
        <input type="submit" value="点击这里显示文件名字与大小">
    </form>
    <br><br><br>

    <form action="/create_file/" enctype="multipart/form-data" method="post">
        <input type="file" name="file" multiple><br><br>
        <input type="file" name="fileb" multiple><br><br>
        <input type="text" name="notes" multiple><br><br>
        <input type="submit" value="点击这里显示文件属性">
    </form>

</body>
</html>

文档自动生成

FastAPI-Swagger UI:   http://127.0.0.1:8000/docs
FastAPI-ReDoc:      http://127.0.0.1:8000/redoc

发送POST请求

main.py 代码:

from fastapi import FastAPI

app = FastAPI()


# @app.post("/login")
# async def login():
#     return {"msg": "login success"}

@app.api_route("/login", methods=["GET", "POST", "PUT"])
def login():
    return {"msg": "login success"}

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

# uvicorn main:app --reload  终端启动

获取URL参数

main.py 代码:

http://127.0.0.1:8000/user/5

from fastapi import FastAPI

app = FastAPI()


@app.get("/user/{pk}")
def user(pk):
    return {"pk": pk}

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

# uvicorn main:app --reload  终端启动

获取请求头和请求体的参数

http://127.0.0.1:8000/user/?pk=1

main.py

from fastapi import FastAPI
app = FastAPI()
@app.get("/user/")
def user(pk):
    return {"pk": pk}

pip install python-multipart  # 使用Form 需要安装

from fastapi import FastAPI, Header, Body, Form

app = FastAPI()


@app.get("/user/")
def user(pk, token=Header(None)):
    return {"pk": pk, "token": token}  # 请求头


# @app.post("/login")
# def login(data=Body(None)):
#     return {"data": data}  # 请求体 json

@app.post("/login")
def login(username=Form(None), password=Form(None)):
    return {"data": {"username": username, "password": password}}  # 请求体 Form 表单

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

# uvicorn main:app --reload  终端启动

返回图片和定制返回信息

main.py

from fastapi import FastAPI
from fastapi.responses import JSONResponse, HTMLResponse, FileResponse

app = FastAPI()

@app.get("/user")
def user():
    # return {"msg": "get user"}
    return JSONResponse(content={"msg": "get user"},
                        status_code=202,
                        headers={"name": "xiaobai"})

@app.get("/")
def user():
    html_content = """
    <html>
        <body><p style="color:red">Hello World</p></body>
    </html>
    """
    return HTMLResponse(content=html_content)

@app.get("/avatar")
def user():
    avatar = 'static/mage.jpg'
    return FileResponse(avatar)  # 
    # return FileResponse(path=avatar, filename="mage.jpg")  # 下载图片
# pip install aiofiles

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

# uvicorn main:app --reload  终端启动

jinja2模板返回HTML页面

http://127.0.0.1:8000/?username=xiaobai

main.py

from fastapi import FastAPI, Request
from fastapi.templating import Jinja2Templates  # pip install jinja2

app = FastAPI()
template = Jinja2Templates("templates")


@app.get("/")
def user(username, req: Request):
    # context 必须加 key必须为"request"固定不可改变
    return template.TemplateResponse("index.html", context={"request": req, "name": username})

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

# uvicorn main:app --reload  终端启动

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>首页</h1>
    <p>欢迎来到 FastAPI</p>
    <p>今天是2021年12月25日星期六</p>
    <p>用户名:{{ name }}</p>
</body>
</html>

实战小应用

main.py

from fastapi import FastAPI, Request, Form
from fastapi.templating import Jinja2Templates  # pip install jinja2
from fastapi.responses import RedirectResponse

app = FastAPI()
template = Jinja2Templates("templates")

todos = ["写日记", "看电影", "玩游戏"]


@app.get("/")
def index(req: Request):
    return template.TemplateResponse("index.html", context={"request": req, "todos": todos})


@app.post("/todo")
def todo(todo=Form(None)):
    """处理用户发送过来的todolist数据"""
    todos.insert(0, todo)
    return RedirectResponse("/", status_code=302)  # 默认为307 不能跳转 get请求

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

# uvicorn main:app --reload  终端启动

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>待办事项</h1>

    <div>
        <form action="/todo" method="post">
            <input type="text" placeholder="请添加待办事项..." name="todo">
            <input type="submit" value="添加">
        </form>
    </div>

    {% for todo in todos %}
        <p>{{ todo }}</p>
    {% endfor %}


</body>
</html>

FastAPI集成Bootstrap

main.py

from fastapi import FastAPI, Form
from starlette.requests import Request
from starlette.staticfiles import StaticFiles
from starlette.templating import Jinja2Templates

app = FastAPI()
templates = Jinja2Templates(directory="templates")
app.mount("/static", StaticFiles(directory='static'), name='static')


@app.post("/user/")
async def files(
        request: Request,
        username: str = Form(...),
        password: str = Form(...),
):
    print('username', username)
    print('password', password)
    return templates.TemplateResponse(
        name="index.html",
        context={
            "request": request,
            "username": username,
        })


@app.get("/")
async def main(request: Request):
    if __name__ == '__main__':
        return templates.TemplateResponse(name='signin.html', context={'request': request})


if __name__ == '__main__':
    import uvicorn

    uvicorn.run(app, host="127.0.0.1", port=8000)

# uvicorn main:app --reload  终端启动

signin.html

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
    <meta name="description" content="">
    <meta name="author" content="">
<!--    <link rel="icon" href="../../favicon.ico">-->
    <link rel="icon" href="{{ url_for('static', path='/images/favicon.ico') }}">

    <title>Signin Template for Bootstrap</title>

    <!-- Bootstrap core CSS -->
    <link href="{{ url_for('static', path='/css/bootstrap.min.css') }}" rel="stylesheet">

    <!-- Custom styles for this template -->
    <link href="{{ url_for('static', path='/css/signin.css') }}" rel="stylesheet">

    <![endif]-->
  </head>

  <body>

    <div class="container">

      <form class="form-signin" action="/user/" enctype="application/x-www-form-urlencoded" method="post">
        <h2 class="form-signin-heading">Please sign in</h2>
        <label for="inputEmail" class="sr-only">username</label>
        <input type="username" name="username" id="inputEmail" class="form-control" placeholder="username" required autofocus>
        <label for="inputPassword" class="sr-only">Password</label>
        <input type="password" name="password" id="inputPassword" class="form-control" placeholder="Password" required>
        <div class="checkbox">
          <label>
            <input type="checkbox" value="remember-me"> Remember me
          </label>
        </div>
        <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
      </form>

    </div> <!-- /container -->

  </body>
</html>

index.html

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
    <meta name="description" content="">
    <meta name="author" content="">
    <link rel="icon" href="{{ url_for('static', path='/images/favicon.ico') }}">

    <title>Jumbotron Template for Bootstrap</title>

    <!-- Bootstrap core CSS -->
    <link href="{{ url_for('static', path='/css/bootstrap.min.css') }}" rel="stylesheet">

    <!-- Custom styles for this template -->
    <link href="{{ url_for('static', path='/css/jumbotron.css') }}" rel="stylesheet">

</head>

<body>

<nav class="navbar navbar-inverse navbar-fixed-top">
    <div class="container">
        <div class="navbar-header">
            <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar"
                    aria-expanded="false" aria-controls="navbar">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="#">Project name</a>
        </div>
        <div id="navbar" class="navbar-collapse collapse">
            <form class="navbar-form navbar-right">
                <div class="form-group">
                    <input type="text" placeholder="Email" class="form-control">
                </div>
                <div class="form-group">
                    <input type="password" placeholder="Password" class="form-control">
                </div>
                <button type="submit" class="btn btn-success">Sign in</button>
            </form>
        </div><!--/.navbar-collapse -->
    </div>
</nav>

<!-- Main jumbotron for a primary marketing message or call to action -->
<div class="jumbotron">
    <div class="container">
        <h1>Hello, {{ username }}!</h1>
        <p>This is a template for a simple marketing or informational website. It includes a large callout called a
            jumbotron and three supporting pieces of content. Use it as a starting point to create something more
            unique.</p>
        <p><a class="btn btn-primary btn-lg" href="#" role="button">Learn more &raquo;</a></p>
    </div>
</div>

<div class="container">
    <!-- Example row of columns -->
    <div class="row">
        <div class="col-md-4">
            <h2>Heading</h2>
            <p>Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris
                condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis
                euismod. Donec sed odio dui. </p>
            <p><a class="btn btn-default" href="#" role="button">View details &raquo;</a></p>
        </div>
        <div class="col-md-4">
            <h2>Heading</h2>
            <p>Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris
                condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis
                euismod. Donec sed odio dui. </p>
            <p><a class="btn btn-default" href="#" role="button">View details &raquo;</a></p>
        </div>
        <div class="col-md-4">
            <h2>Heading</h2>
            <p>Donec sed odio dui. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Vestibulum id ligula
                porta felis euismod semper. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut
                fermentum massa justo sit amet risus.</p>
            <p><a class="btn btn-default" href="#" role="button">View details &raquo;</a></p>
        </div>
    </div>

    <hr>

    <footer>
        <p>&copy; 2016 Company, Inc.</p>
    </footer>
</div> <!-- /container -->


<!-- Bootstrap core JavaScript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="https://code.jquery.com/jquery-1.12.4.min.js"
        integrity="sha384-nvAa0+6Qg9clwYCGGPpDQLVpLNn0fRaROjHqs13t4Ggj3Ez50XnGQqc/r8MhnRDZ"
        crossorigin="anonymous"></script>
<script>window.jQuery || document.write('<script src="../../assets/js/vendor/jquery.min.js"><\/script>')</script>
<script src="{{ url_for('static', path='/js/bootstrap.min.js') }}"></script>
</body>
</html>

1 url和交互式API文档

ajax请求不能重定向

from fastapi import FastAPI

app = FastAPI()


# read_item_me必须写在read_item前面
@app.get("/me/xx")
async def read_item_me():
    return {"me": 'me'}


@app.get("/me/{item_id}")
async def read_item(item_id: str):
    return {"item_id": item_id, }
# 路径参数 item_id 的值将作为参数 item_id 传递给你的函数。

@app.get("/")
async def main():
    return {"message": "Hello, FastAPI"}

if __name__ == '__main__':
    import uvicorn

    uvicorn.run(app, host="127.0.0.1", port=8000)

# uvicorn main:app --reload  终端启动




from fastapi import FastAPI
from enum import Enum


# 枚举
class Name(str, Enum):
    Allan = "张三"
    Jon = "李四"
    Bob = "王五"


app = FastAPI()


@app.get("/{who}")
async def get_day(who: Name):
    if who == Name.Allan:
        return {"who": who, "message": "张三是德国人"}
    if who.value == "李四":
        return {"who": who, "message": "李四是英国人"}
    return {"who": who, "message": "王五是法国人"}


@app.get("/")
async def main():
    return {"message": "Hello, FastAPI"}


if __name__ == '__main__':
    import uvicorn

    uvicorn.run(app, host="127.0.0.1", port=8000)

# uvicorn main:app --reload  终端启动

2 Query Parameters查询参数

from fastapi import FastAPI

app = FastAPI()
fake_item_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]


# http://127.0.0.1:8000/items/?skip=0&limit=2
@app.get("/items/")
async def read_item(skip: int = 0, limit: int = 10):
    return fake_item_db[skip: skip + limit]  # 默认返回0-10个


@app.get("/i/")
async def i(A: str = 'HI..', B: str = 'Hello..', C: str = 'He..'):
    return {'cc': A + B + C}, {'dd': B + C}


# http://127.0.0.1:8000/ii/?A=100
@app.get("/ii/")
async def ii(A: int = 0, B: int = 10, C: int = 20):
    return {'cc': A + B + C}, {'dd': B + C}


@app.get("/iii/")
async def iii(A: int = 0, B: int = 10, C: int = 20):
    return 'A+B+C', A + B + C


# http://127.0.0.1:8000/xxx/item_id=aaaa
# bool与类型转换
@app.get("/xxx/{item_id}")
async def xxx(item_id: str, QQ: str = None, SS: bool = False):
    item = {"item_id": item_id}
    if QQ:
        item.update({"QQ": QQ})
    if not SS:  # 如果SS是假
        item.update(
            {"item_id": "This is SSSSSS"}
        )
    return item


# http://127.0.0.1:8000/user/1/item/2
# http://127.0.0.1:8000/user/1/item/a?q=dddd
# 多路径 和 查询参数 和 必填字段
@app.get("/user/{user_id}/item/{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


if __name__ == '__main__':
    import uvicorn

    uvicorn.run(app, host="127.0.0.1", port=8000)

# uvicorn main:app --reload  终端启动

3 Request_Body请求正文(交互式API文档)

当你需要将数据从客户端(例如浏览器)发送给 API 时,你将其作为「请求体」发送。

请求体是客户端发送给 API 的数据。响应体是 API 发送给客户端的数据。

你的 API 几乎总是要发送响应体。但是客户端并不总是需要发送请求体。

你不能使用 GET 操作(HTTP 方法)发送请求体。

要发送数据,你必须使用下列方法之一:POST(较常见)、PUT、DELETE 或 PATCH。

from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    description: str = None
    price: float
    tax: float = None


app = FastAPI()


@app.post("/items/")
async def create_item(item: Item):
    print(item.dict())
    return item, '人生没有无意义的经历!'


@app.put("/items/{item_id}")
async def create_item2(item_id: int, item: Item, q: str = None):
    result = {"item_id": item_id, **item.dict()}
    if q:
        result.update({"q": q})
    print(result)
    return result


if __name__ == '__main__':
    import uvicorn

    uvicorn.run(app, host="127.0.0.1", port=8000)

# uvicorn main:app --reload  终端启动

4 Query_Validations查询参数和字符串验证

from fastapi import FastAPI, Query
from typing import List

app = FastAPI()


#############################################################
# 限制长度
@app.get("/items/")
async def read_item(q: str = Query(None, min_length=3, max_length=50)):
    # 填None就是默认值  填...则是必填项
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})

    return results


###########################################
# 正则表达式
@app.get("/items2/")
async def read_item2(q: str = Query(None, min_length=3, max_length=50, regex="^nice")):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results


##############################################
# 列表
@app.get("/items3/")
async def read_item3(q: List[str] = Query(["foo", "bar"])):
    query_items = {"q": q}
    return query_items


################################################
# http://127.0.0.1:8000/items4/?item-query=ddddd
# 别名参数
@app.get("/items4/")
async def read_items4(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/items5/?item-query=13771122333
# 弃用参数
@app.get("/items5/")
async def read_items5(
        q: str = Query(
            None,
            alias="item-query",
            title="Query string",
            description="Query string for the items to search in the database that have a good match",
            min_length=3,
            max_length=50,
            regex=r"^1[3-9]\d{9}$",
            deprecated=True
        )
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results


if __name__ == '__main__':
    import uvicorn

    uvicorn.run(app, host="127.0.0.1", port=8000)

# uvicorn main:app --reload  终端启动

5 Path_Numeric路径参数与数值验证

from fastapi import FastAPI, Query, Path

app = FastAPI()


# http://127.0.0.1:8000/items/100?item-query=rrrr&size=0.8
@app.get("/items/{item_id}")
async def read_item(
        item_id: int = Path(..., title="The ID of the item to get", ge=50, le=100),
        q: str = Query(None, alias="item-query"),
        size: float = Query(1, gt=0, lt=10.5)
):
    results = {"items": item_id}
    if q:
        results.update({"q": q})

    return results


if __name__ == '__main__':
    import uvicorn

    uvicorn.run(app, host="127.0.0.1", port=8000)

# uvicorn main:app --reload  终端启动

6 Body_Multiple_Parameters多参数Body混合

混合使用 Path、Query 和请求体参数

from typing import Optional

from fastapi import FastAPI, Path
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: Optional[str] = None
    price: float
    tax: Optional[float] = None


# 请注意,在这种情况下,将从请求体获取的 item 是可选的。因为它的默认值为 None。
# http://127.0.0.1:8000/items/88?q=AAAA
@app.put("/items/{item_id}")
async def update_item(
        *,
        item_id: int = Path(..., title="The ID of the item to get", ge=0, le=1000),
        q: Optional[str] = None,
        item: Optional[Item] = None,
):
    results = {"item_id": item_id}
    if q:
        results.update({"q": q})
    if item:
        results.update({"item": item})
    return results


if __name__ == '__main__':
    import uvicorn

    uvicorn.run(app, host="127.0.0.1", port=8000)

# uvicorn main:app --reload  终端启动

多个请求体参数¶

from typing import Optional

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: Optional[str] = None
    price: float
    tax: Optional[float] = None


class User(BaseModel):
    username: str
    full_name: Optional[str] = None


@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item, user: User):
    results = {"item_id": item_id, "item": item, "user": user}
    return results


if __name__ == '__main__':
    import uvicorn

    uvicorn.run(app, host="127.0.0.1", port=8000)

# uvicorn main:app --reload  终端启动

请求体中的单一值¶

from typing import Optional

from fastapi import Body, FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: Optional[str] = None
    price: float
    tax: Optional[float] = None


class User(BaseModel):
    username: str
    full_name: Optional[str] = None


@app.put("/items/{item_id}")
async def update_item(
        item_id: int, item: Item, user: User, importance: int = Body(...)
):
    results = {"item_id": item_id, "item": item, "user": user, "importance": importance}
    return results


if __name__ == '__main__':
    import uvicorn

    uvicorn.run(app, host="127.0.0.1", port=8000)

# uvicorn main:app --reload  终端启动

多个请求体参数和查询参数¶

from typing import Optional

from fastapi import Body, FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: Optional[str] = None
    price: float
    tax: Optional[float] = None


class User(BaseModel):
    username: str
    full_name: Optional[str] = None


@app.put("/items/{item_id}")
async def update_item(
        *,
        item_id: int,
        item: Item,
        user: User,
        importance: int = Body(..., gt=0),
        q: Optional[str] = None
):
    results = {"item_id": item_id, "item": item, "user": user, "importance": importance}
    if q:
        results.update({"q": q})
    return results


if __name__ == '__main__':
    import uvicorn

    uvicorn.run(app, host="127.0.0.1", port=8000)

# uvicorn main:app --reload  终端启动

嵌入单个请求体参数¶

from typing import Optional

from fastapi import Body, FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: Optional[str] = None
    price: float
    tax: Optional[float] = None


@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item = Body(..., embed=True)):
    results = {"item_id": item_id, "item": item}
    return results


if __name__ == '__main__':
    import uvicorn

    uvicorn.run(app, host="127.0.0.1", port=8000)

# uvicorn main:app --reload  终端启动

7 Body_Fields字段验证

请求体 - 字段¶

from typing import Optional

from fastapi import Body, FastAPI
from pydantic import BaseModel, Field

app = FastAPI()


class Item(BaseModel):
    name: str
    description: Optional[str] = Field(
        None, title="The description of the item", max_length=300
    )
    price: float = Field(..., gt=0, description="The price must be greater than zero")
    tax: Optional[float] = None


@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item = Body(..., embed=True)):
    results = {"item_id": item_id, "item": item}
    return results


if __name__ == '__main__':
    import uvicorn

    uvicorn.run(app, host="127.0.0.1", port=8000)

# uvicorn main:app --reload  终端启动

声明模型属性¶

from typing import Optional

from fastapi import Body, FastAPI
from pydantic import BaseModel, Field

app = FastAPI()


class Item(BaseModel):
    name: str
    description: Optional[str] = Field(
        None, title="The description of the item", max_length=300
    )
    price: float = Field(..., gt=0, description="The price must be greater than zero")
    tax: Optional[float] = None


@app.put("/items/{item_id}")
# async def update_item(item_id: int, item: Item = Body(..., example={})):
async def update_item(item_id: int, item: Item = Body(..., embed=True)):
    results = {"item_id": item_id, "item": item}
    return results


if __name__ == '__main__':
    import uvicorn

    uvicorn.run(app, host="127.0.0.1", port=8000)

# uvicorn main:app --reload  终端启动

8 Body_Nested_Models_嵌套模型

请求体 - 嵌套模型¶

List 字段¶

from typing import Optional

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: Optional[str] = None
    price: float
    tax: Optional[float] = None
    tags: list = []


@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
    results = {"item_id": item_id, "item": item}
    return results


if __name__ == '__main__':
    import uvicorn

    uvicorn.run(app, host="127.0.0.1", port=8000)

# uvicorn main:app --reload  终端启动

具有子类型的 List 字段¶声明具有子类型的 List¶Set 类型¶

from typing import List, Optional, Set

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: Optional[str] = None
    price: float
    tax: Optional[float] = None
    tags: list = []
    tags1: List[str] = []
    tags2: List[str] = []
    tags3: Set[str] = set()


@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
    results = {"item_id": item_id, "item": item}
    return results


if __name__ == '__main__':
    import uvicorn

    uvicorn.run(app, host="127.0.0.1", port=8000)

# uvicorn main:app --reload  终端启动

定义子模型¶

from typing import Optional, Set

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Image(BaseModel):
    url: str
    name: str


class Item(BaseModel):
    name: str
    description: Optional[str] = None
    price: float
    tax: Optional[float] = None
    tags: Set[str] = []
    image: Optional[Image] = None


@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
    results = {"item_id": item_id, "item": item}
    return results


if __name__ == '__main__':
    import uvicorn

    uvicorn.run(app, host="127.0.0.1", port=8000)

# uvicorn main:app --reload  终端启动

深度嵌套模型¶

from typing import List, Optional, Set

from fastapi import FastAPI
from pydantic import BaseModel, HttpUrl

app = FastAPI()


class Image(BaseModel):
    url: HttpUrl
    name: str


class Item(BaseModel):
    name: str
    description: Optional[str] = None
    price: float
    tax: Optional[float] = None
    tags: Set[str] = set()
    images: Optional[List[Image]] = None


class Offer(BaseModel):
    name: str
    description: Optional[str] = None
    price: float
    items: List[Item]


@app.post("/offers/")
async def create_offer(offer: Offer):
    return offer
posted @ 2022-03-01 19:36  寻月隐君  阅读(1732)  评论(1编辑  收藏  举报