【python】FastAPI简单学习和路径参数(一)

FastApi学习

一、快速上手

第一个FastApi

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# main.py
import uvicorn
from fastapi import FastAPI

app = FastAPI()


@app.get("/")
async def root():
return {"message": "Hello World"}


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


@app.get("/study/{address}")
async def say_study(address: str):
return {"address": f"住在: {address} "}


if __name__ == '__main__':
uvicorn.run("main:app", host="127.0.0.1", port=8080, reload=True)

启动方式有两种

  • 1、命令行启动

    • uvicorn main:app --reload --host 127.0.0.1 --port 8080
      
      1
      2
      3
      4
      5
      6
      7
      8

      -

      - 2、执行python 脚本文件也是可以启动fastapi 项目的(调试的时候用,不推荐)

      - ```python
      if __name__ == '__main__':
      uvicorn.run("main:app", host="127.0.0.1", port=8080, reload=True)

四个核心点:

  • 路径操作装饰器:@app.get("/"),在这里可以定义接口的url,并指定访问该URL使用的请求方式。比如这个例子钟,我们定义了一个URL: /也就是根目录,根路由,访问方式是get后面简称为路径装饰器
  • 路径操作函数:def root()这个就是我们定义的路径操作函数,或者可以把他叫做api接口,当别人在浏览器上访问URL/的时候,fastapi 就会执行这个接口(对外叫接口,对内叫函数)里面的代码
  • 接口返回值:return{"message":"hello word"}, 这个就是api接口的返回值返回内容,也就是当我们访问URL/的时候,浏览器上看到的响应结果

1-6路径参数

我们把这些URL称之为路径参数。不同的URL表示不同的路径,不同的含义

在FastAPI中可以非常方便的定义和设计这些URL(或者说路径参数),并且同时支持静态路径参数和动态路径参数

  • 静态路径 :写死不变的URL
  • 动态路径 :一组格式相同,但包含动态可变的部分。比如: /books/2 和 /books/3 等等,编号是可变的。

在前面的代码中,我们就定义了一个动态路径 用的是{}引用

1
2
3
@app.get("/study/{address}")
async def say_study(address: str):
return {"address": f"住在: {address} "}

1-7接口顺序

如代码:

1
2
3
4
5
6
7
8
@app.get("/study/{id}")
async def say_study(id: int):
return {"id": f"编号: {id} "}


@app.get("/study/id1")
async def say_study():
return {"id": f"id = id 1"}
  • 当请求的URL来了,FastAPI会从上往下开始匹配我们定义的接口,

  • 动态路径传函数传递时的类型是int,而静态路径写死是一个字符串,这个时候避免报错

解决方式是把静态的写在前面,动态的写在后面

1-8分页查询

需求场景:

图书列表页面,该页面的图书数据都是从数据库取出来通过图书接口/books,返回给浏览器

但是数据库成千上万,浏览器页面显示位置有限,通常用分页展示

  • 定义形参:page:int,size:int
  • 注意的点:URL中查询参数默认都是字符串格式,所以默认pagesize都是字符串
  • FastAPI特性:当我们再定义形参时加上类型提示,此时FastAPI会帮我们自动做类型转换,并且因此还具备了类型校验的功能
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import uvicorn
from fastapi import FastAPI

app = FastAPI()

books = [
{"id": 1, "name": "图书1"},
{"id": 2, "name": "图书2"},
{"id": 3, "name": "图书3"},
{"id": 4, "name": "图书4"},
{"id": 5, "name": "图书5"},
{"id": 6, "name": "图书6"},
{"id": 7, "name": "图书7"},
{"id": 8, "name": "图书8"},
]
@app.get("/books")
async def get_books(page: int, size: int):
# 列表[索引1:索引2]取值
return books[(page - 1) * size:page * size]

  • 注意: 最好使用Postman等接口调试工具去填写查询参数发起get请求(或者使用接口文档访问)

1-9 OpenAPI文档

OpenAPI是swagger的底层,swagger就是基于OpenAPI封装的一层,,主要用来做接口管理

FastAPI自带的API文档且功能强大,启动服务只需要访问两个URL即可

  • swagger : ip:port/docs
  • redoc : ip:port/redoc

设置API文档标签*(还是上面的代码,在定义路由的地方加参数)*

1
2
3
@app.get("/books", tags=["Book"], description="设置描述信息", summary='设置概括信息')
def get_books(page: int, size: int):
return books[(page - 1) * size:page * size]

拓展:关于@app.get() 装饰器用于定义 HTTP GET 请求的处理函数。它可以接受以下参数:

  • path(必需):表示路由的路径,即请求的 URL 路径。例如,/books 表示处理 /books 路径的 GET 请求。
  • summary(可选):提供一个简要的描述,用于生成 API 文档。通常用于描述路由的目的或功能。
  • description(可选):提供更详细的描述,用于生成 API 文档。可以在这里提供有关路由的更多信息,如用法示例、参数说明等。
  • response_model(可选):指定路由的响应模型,即返回给客户端的数据结构。可以是 Pydantic 模型类,用于验证和序列化响应数据(不用)
  • status_code(可选):指定成功响应的 HTTP 状态码。默认为 200。
  • dependencies(可选):指定用于该路由的依赖项。可以是单个依赖项或依赖项列表。
  • responses(可选):指定其他非默认响应的配置。可以自定义不同状态码的响应,例如错误处理或其他特定情况下的响应。

标签信息

二、Path和Query

2-1 路径转换器

在 FastAPI 中,路径转换器是一种用于定义路由路径参数的机制。路径转换器允许你在路由路径中使用特定的参数模式,并将其捕获并传递给处理函数。

常见的路径转换器包括:

  • {variable}: 使用花括号包围的变量名称,例如 {item_id}。这种形式的路径参数将匹配路径中的任何值,并将其传递给处理函数。
  • {variable:type}: 在花括号中指定变量的名称和类型,例如 {item_id:int}。这种形式的路径参数将匹配指定类型的值,并将其转换为相应的数据类型后传递给处理函数。

示例1:使用路径转换器int,可以直接帮我们将这个参数转化为int类型

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
import typing
from fastapi import FastAPI


app = FastAPI(title="路径转化器")

books = {
1: {"id": 1, "name": "图书1"},
2: {"id": 2, "name": "图书2"},
3: {"id": 3, "name": "图书3"},
}


@app.get("/books/{id:int}") # 注意 {id:int} :两边不能有空格
def get_books_by_id(id):
print(id, type(id))
return books.get(id)
@app.get("/files/{file_path}") # 无路径转换器
def read_file(file_path: str):
print(file_path)
with open(file_path, "r") as f:
return f.read()
@app.get("/files/{file_path:path}") # path路径转换器
def read_file(file_path: str):
print(file_path)
with open(file_path, "r") as f:
return f.read()

注意1:get_books_by_id(id)因为此时没有在路径函数内使用类型提示,所以openapi文档没有了类型校验功能

注意2:如果参数类型不匹配,则直接返回404 Not Found,程序是不会执行路径函数内的代码。

内置5种路径转换器

  • str 返回字符串(默认模式)
  • int:返回整数
  • float:返回pyhon中的float浮点数
  • uuid :返回python中的uuid.UUID
  • path返回一个路径,可以包含任意多个的/

补充1:这些内置转化器是Fastapi直接复用Starlette中的路径转化器

补充2:除了这5个转化器,我们也可以自定义符合自己需求的转化器(参考官网代码)

2-2 路径参数枚举值

可以定义一个数据类,枚举出几种可能性,进行限制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from enum import Enum

from fastapi import FastAPI


class TagName(str, Enum): # 多继承,python特性
PYTHON = "python"
LINUX = "linux"
WEB = "web"


app = FastAPI()


@app.get("/blogs/{tag}")
def get_blogs_by_tag(tag: TagName): # 类型提示
if tag == TagName.PYTHON:
return "some blogs about python"

if tag.value == "web":
return "some blogs about web"

return "some blogs about linux"
# 有了
  • TagName继承 Enum是为了使用枚举类型,继承 str是为了有类型提示和API文档显示功能。(python3.11可以直接使用StrEnum)

  • 有了枚举类型,可以直接使用枚举变量的 namnevalue两个属性。

    • name:表示枚举变量的名字,比如, PYTHON

    • value:表示枚举变量的值,比如,python

  • 有了TagName,FastAPI就知道tag这个路劲参数只有三个字符串类型的值,如果URL中有其他值就会直接报错。

2-3 路径参数使用Path 校验

需求场景:

限制入参的长度,比如说字符串长度、数字大小。使用Path()函数

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


app = FastAPI()


@app.get("/blog/{name}") # 最小3个字符,最多10个字符
def get_blog_by_name(name: str = Path(min_length=3, max_length=10)):
return f"the length of this blog name is: {len(name)}"

@app.get("/blog/{id}") # 最小3-10之间
def get_blog_by_id(id: int = Path(ge=3, le=10)):
return id

2-4 Path之接口文档设置【可以看一下】

Path()不仅可以用来做路径参数的校验逻辑,还可以用来设置接口文档的展示功能,常用的设置字段如下:

1
2
3
4
5
6
7
alias:  		    # 设置别名,修改是要配合路径装饰器中的参数名一块修改
title: # 设置标题,在redoc中有显示
description: # 设置参数描述信息
example: # 设置例子
examples: # 设置多个例子,比较复杂
deprecated: # 将要废弃该接口
include_in_schema: # 是否把schema显示在api文档

示例1: examples

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


app = FastAPI()

examples = {
"valid": {"value": 20},
"invalid": {"value": 8},
}


@app.get("/blog/{id}")
def get_blog_by_id(id: int = Path(ge=10, example=examples)):
return id

2-5 设置查询参数 分页和排序

比如,图书列表页面,使用page和size两个查询参数做分页,但存在URL中没有携带这俩参数的情况,此时能否有默认值呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import random
from fastapi import FastAPI

app = FastAPI()

books = [{"id": i, "name": f"图书{i}"} for i in range(10)]
random.shuffle(books)


@app.get("/books")
def get_books(page: int, size: int, sort: bool):
results = books[(page - 1) * size:page * size]
if sort: # 如果sort是ture
results.sort(key=lambda x: x["id"]) # 进行sort 排序

return results # 否则直接返回分页

看一下他的请求就知道了

1
2
3
4
5
http://127.0.0.1:8000/books?page=1&size=5&sort=1
http://127.0.0.1:8000/books?page=1&size=5&sort=True
http://127.0.0.1:8000/books?page=1&size=5&sort=true
http://127.0.0.1:8000/books?page=1&size=5&sort=on
http://127.0.0.1:8000/books?page=1&size=5&sort=yes

2-6 查询参数有多个值

URL上可能携带多个相同的查询参数,/books?id=10&id=20,此时如何获取多个id这个查询参数的多个值?

示例1:在路径函数内定义多个查询参数时,需要遵循Python形参顺序的基本原则:先位置参数,后关键字参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 正确的顺序
@app.get("/books")
def get_books(page: int, size: int = 4, sort: bool = False):
results = books[(page - 1) * size:page * size]
if sort:
results.sort(key=lambda x: x["id"])
return results

# 错误的顺序
@app.get("/books")
def get_books(page: int, size: int = 4, sort: bool):
results = books[(page - 1) * size:page * size]
if sort:
results.sort(key=lambda x: x["id"])
return results

示例2:获取URL中相同参数的多个值,需要使用Query()函数

1
2
3
4
5
6
7
8
9
import typing
from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/books")
def get_books(id: typing.List[int] = Query()):
return id

2-7 查询参数使用Query校验

2-8 路径参数和查询参数的顺序问题

你是否有疑问:一个接口中既有路径参数,又有查询参数,FastAPI是如何区分它们的,和顺序有关系吗?

你是否有疑问:一个接口中的路径参数和查询参数的顺序有关系吗,为什么我定义参数会有语法错误,无法运行程序?

原则:

  • 路径函数内的形参名字不能重复

  • 普通类型的参数默认会被识别为查询参数,比如:int, float, str, bool;复杂类型需要配合Query()显示声明为查询参数

  • 只要形参的名字出现在路径装饰器中定义的参数,则肯定是路径参数,即使使用了Query()

2-9 阶段总结

  • 结论1:路径指的就是路由或者说URL,分静态路由和动态路由
  • 结论2:动态路由中的参数,就是路径参数;只要定义在路径函数的形参和路径装饰器中的参数同名,则该形参就会被是被为路径参数
  • 结论3:路径参数默认是必须的,且参数类型默认是字符串,可以使用路径转化器或者类型提示的方式做类型转换
  • 结论4:跟在URL ? 后面的键值对参数称之为 查询参数,默认是字符串类型,可以使用类型提示的方式做类型转化
  • 结论5:普通类型的形参(int/str/float/bool),FastAPI会默认它为查询参数;也可以使用Query()显示声明
  • 结论6:使用Path()给路径参数做校验,使用Query()给查询参数做校验;做API文档的设置
  • 结论7:查询参数和路过参数的定义顺序无关,FastAPI能都自动是被它们
  • 结论8:定义在路径函数内形参的顺序,需要遵循:没有默认值的放在前面,有默认值的放在后面(python的语法要求)
1
2
3
4
5
6
7
8
# 简单说:路径参数是从 URL 路径中提取的参数,在路由路径中使用花括号 {} 包围参数名称来定义。例如,/users/{user_id} 中的 user_id 就是一个路径参数。在处理函数中,你可以像普通函数参数一样声明路径参数,并指定其类型。FastAPI 将自动从请求 URL 中提取参数的值,并将其传递给相应的处理函数。
@app.get("/users/{user_id}")
def get_user(user_id: int):
return {"user_id": user_id}
# 查询参数是,查询参数是附加在 URL 后面的参数,使用 ? 分隔 URL 和查询参数,然后以 key=value 的形式添加到 URL 中。FastAPI 会自动解析查询参数,并将其传递给处理函数。
@app.get("/items/")
def get_items(page: int = 1, limit: int = 10):
return {"page": page, "limit": limit}

【python】FastAPI简单学习和路径参数(一)
http://example.com/2024/01/15/681FastAPI1简单学习和路径参数/
作者
Wangxiaowang
发布于
2024年1月15日
许可协议