YONGFEIUALL

izheyi.com


  • Home

  • Archives

  • Categories

  • Tags

  • About

  • Search

FastAPI: Dependencies(13)

Posted on 2021-10-03 | In FastAPI |

FastAPI 提供了简单易用,但功能强大的依赖注入系统,可以让开发人员轻松地把组件集成至 FastAPI。

编程中的「依赖注入」是声明代码(本文中为路径操作函数 )运行所需的,或要使用的「依赖」的一种方式。

基本示例

1
2
3
4
5
6
7
8
9
10
def result(start: int = 0, limit: int = 10):
return {'start': start, 'limit': limit}

@app.get('/users/')
def users(res: dict = Depends(result)):
return res

@app.get('/books/')
def books(res: dict = Depends(result)):
return res

这里只能传给 Depends 一个参数。

接收到新的请求时,FastAPI 执行如下操作:

  • 用正确的参数调用依赖项函数(「可依赖项」)
  • 获取函数返回的结果
  • 把函数返回的结果赋值给路径操作函数的参数

Class 依赖注入

也可以用Python Class来实现依赖注入。

1
2
3
4
5
6
7
8
class Result():
def __init__(self, start: int = 0, limit: int = 10):
self.start = start
self.limit = limit

@app.get('/users/')
def users(res: Result = Depends(Result)):
return res

引用时,也可以用下边方式:

res: Result = Depends()

子依赖注入

FastAPI 支持创建含任意深度的子依赖项。类似于函数调用:fun1 -> fun2 -> fun3

1
2
3
4
5
6
7
8
9
10
11
def result1(start: int = 0):
return start

def result2(start: int = Depends(result1), limit: int = 0):
if limit:
return limit
return start

@app.get('/users/')
def users(res: dict = Depends(result2)):
return res

结果

1
http://127.0.0.1:8000/users/?start=2
2

http://127.0.0.1:8000/users/?limit=5
5

多次使用同一个依赖项

如果在同一个路径操作 多次声明了同一个依赖项,例如,多个依赖项共用一个子依赖项,FastAPI 在处理同一请求时,只调用一次该子依赖项。

FastAPI 不会为同一个请求多次调用同一个依赖项,而是把依赖项的返回值进行「缓存」,并把它传递给同一请求中所有需要使用该返回值的「依赖项」。

在高级使用场景中,如果不想使用「缓存」值,而是为需要在同一请求的每一步操作(多次)中都实际调用依赖项,可以把 Depends 的参数 use_cache 的值设置为 False

路径依赖

以在路径操作装饰器中添加一个由 dependencies 组成的 list来注入多个引用。

1
2
3
4
5
6
7
8
9
10
11
def result1(start: int):
if start != 2:
raise HTTPException(status_code=400, detail='start not == 2')

def result2(limit: int):
if limit != 5:
raise HTTPException(status_code=400, detail='limit not == 5')

@app.get('/users/', dependencies=[Depends(result1), Depends(result2)])
def users():
return {'status': 'ok'}

全局依赖

有时,我们要为整个应用添加依赖项。

可以把依赖项添加至整个 FastAPI 应用。后续就可以为所有路径操作应用该依赖项。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def result1(start: int):
if start != 2:
raise HTTPException(status_code=400, detail='start not == 2')

def result2(limit: int):
if limit != 5:
raise HTTPException(status_code=400, detail='limit not == 5')

app = FastAPI(dependencies=[Depends(result1), Depends(result2)])

@app.get('/users/')
def users():
return {'status': 'ok'}

@app.get('/books/')
def users():
return {'status': 'ok'}

FastAPI: 响应模型(12)

Posted on 2021-10-02 | In FastAPI |

在前面的操作中也看到,请求响应返回的就是请求体的内容,比如,要是登记注册就是返回用户名和密码,这种行为是不行的。

可以使用 response_model 参数来声明用于响应的模型。

  • 将输出数据转换为其声明的类型。
  • 校验数据。
  • 会将输出数据限制在该模型定义内。
  • 在 OpenAPI 的路径操作中为响应添加一个 JSON Schema。
  • 并在自动生成文档系统中使用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class User(BaseModel):
username: str
password: str
email: EmailStr
adress: Optional[str] = 'beijing'

class UserRes(BaseModel):
username: str
email: EmailStr
address: Optional[str] = 'beijing'

@app.post('/user/register/', response_model=UserRes)
def register(user: User):
return user

上面的例子中,可以看到响应里会自动的返回请求体里默认的字段。如果请求体中不传,就不要返回带默认值的字段,可以用response_model_exclude_unset来实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class User(BaseModel):
username: str
password: str
email: EmailStr
adress: Optional[str] = 'beijing'

class UserRes(BaseModel):
username: str
email: EmailStr
address: Optional[str] = 'beijing'

@app.post('/user/register/', response_model=UserRes, response_model_exclude_unset=True)
def register(user: User):
return user

FastAPI: 响应状态码(11)

Posted on 2021-10-02 | In FastAPI |

可以使用 status_code 参数来声明用于响应的 HTTP 状态码。

1
2
3
@app.post('/uploads/', status_code=201)
def root(files: List[UploadFile] = File(...)):
return {'filename': [file.filename for file in files]}

默认返回的状态码是200,修改后返回201。

HTTP的状态码很多,也不容易 记住所有状态码的意思,FastAPI提供了更方便的方式。

FastAPI: 请求Form&File(10)

Posted on 2021-10-02 | In FastAPI |

Form表单和File Upload是避免不了的操作。

要使用表单,需预先安装 python-multipart

1
pip install python-multipart

Form

1
2
3
@app.post('/login/')
def root(username : str = Form(...), password: str = Form(...)):
return {'username': username, 'password': password}

File

文件上传也是以Form形式发送。

1
2
3
@app.post('/upload/')
def root(file: UploadFile = File(...)):
return {'filename': file.filename}

也支持多个文件上传

1
2
3
@app.post('/uploads/')
def root(files: List[UploadFile] = File(...)):
return {'filename': [file.filename for file in files]}

FastAPI: 请求Header&Cookie(9)

Posted on 2021-10-02 | In FastAPI |

Header

请求自定义Header。

1
2
3
@app.get('/users/')
def root(user_agent: Optional[str] = Header(None)):
return {'User-Agent': user_agent}

Cookie

1
2
3
4
@app.get('/users/')
def root(user_agent: Optional[str] = Header(None),
sid: Optional[str] = Cookie(None)):

return {'User-Agent': user_agent}, {'sid': sid}

FastAPI: 请求体嵌套模型(8)

Posted on 2021-10-01 | In FastAPI |

可以将一个模型属性定义为有多子元素的类型。List来实现

1
2
3
4
5
6
7
8
9
10
11
12
class Book(BaseModel):
name: str = Field(..., min_length=2, max_length=10)
price: float
description: Optional[str] = None
tag: List[str] = None

app = FastAPI()

@app.post('/books/')
def root(book: Book = Body(..., embed=True),
query: Optional[str] = None):

return book, query

还可以在模型里嵌套模型,可以做到任意深度嵌套,来实现更复杂的结构。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Author(BaseModel):
name: str
address: Optional[str] = None


class Book(BaseModel):
name: str
price: float
description: Optional[str] = None
author: Author

app = FastAPI()

@app.post('/authors/{id}')
def root(*,
id: int = Path(..., gt=12),
book: Book):

return id, book

FastAPI: 请求体Query-Path-Body-Field(7)

Posted on 2021-10-01 | In FastAPI |

多参数混用Path和Query

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Book(BaseModel):
name: str
price: float
description: Optional[str] = None

class Author(BaseModel):
name: str
address: Optional[str] = None

app = FastAPI()

@app.post('/authors/{id}')
def root(*,
id: int = Path(..., gt=12),
author: Author,
book: Book,
query: Optional[str] = Query(None)):

return id, author, book, query

Body实现请求体中单一值

在设定好的模型之外,想在发送请求时另外加一个字段进去,如果单独定义就会把它当成一个查询参数,可以利用Body来实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Book(BaseModel):
name: str
price: float
description: Optional[str] = None

class Author(BaseModel):
name: str
address: Optional[str] = None

app = FastAPI()

@app.post('/authors/{id}')
def root(*,
id: int = Path(..., gt=12),
author: Author,
book: Book,
publisher: str = Body(...),
query: Optional[str] = Query(None)):

return id, author, book, publisher, query

嵌入请求体参数

如果想把请求体外边再加一个字段来标识,也可以通过Body来实现

1
2
3
4
5
6
7
8
9
10
11
class Book(BaseModel):
name: str
price: float
description: Optional[str] = None

app = FastAPI()

@app.post('/books/')
def root(book: Book = Body(..., embed=True),
query: Optional[str] = None):

return book, query

Field校验模型里字段

1
2
3
4
5
6
7
8
9
10
11
class Book(BaseModel):
name: str = Field(..., min_length=2, max_length=10)
price: float
description: Optional[str] = None

app = FastAPI()

@app.post('/books/')
def root(book: Book = Body(..., embed=True),
query: Optional[str] = None):

return book, query

FastAPI: 请求体基础(6)

Posted on 2021-10-01 | In FastAPI |

这个不多说,就是客户端发送给 API 的数据。通过Pydantic来声明请求体。

1
2
3
4
5
6
7
8
9
10
class Book(BaseModel):
name: str
price: float
description: Optional[str] = None

app = FastAPI()

@app.post('/books/')
def root(book: Book):
return book

FastAPI: 请求路径参数的校验(5)

Posted on 2021-09-29 | In FastAPI |

可以通过 Path 对路径参数做校验。

1
2
3
4
5
6
7
8
9
books = [{'name': 'python'}, {'name': 'fastapi'}, {'name': 'java'}, ]

@app.get('/users/{id}')
def root(id: int = Path(..., gt=12),
start: int = 0,
limit: Optional[int] = None):

if limit:
return {id: books[start : limit]}
return {id: books[start: ]}
  • gt:大于(greater than)
  • ge:大于等于(greater than or equal)
  • lt:小于(less than)
  • le:小于等于(less than or equal)
1
2
3
http://127.0.0.1:8000/users/11

{"detail":[{"loc":["path","id"],"msg":"ensure this value is greater than 12","type":"value_error.number.not_gt","ctx":{"limit_value":12}}]}

参数排序

有默认值参数和必填参数值都存在时,有默认值参数必须放到必填参数后边。

传递 * 作为函数的第一个参数。

1
2
3
4
5
6
7
@app.get('/users/{id}')
def root(*, id: int = Path(..., gt=12),
start: int = 0,
limit: Optional[int] = None):

if limit:
return {id: books[start : limit]}
return {id: books[start: ]}

Python 不会对该 * 做任何事情,它将之后的所有参数都作为关键字参数来调用。

FastAPI: 请求查询参数的校验(4)

Posted on 2021-09-28 | In FastAPI |

可以通过Query来实现对字符串查询参数的校验。

常规校验

1
2
3
4
5
6
7
8
books = [{'name': 'python'}, {'name': 'fastapi'}, {'name': 'java'}]

@app.get('/books/')
def root(book: str = Query('Java Script', min_length=2)):

if book:
books.append({'name': book})
return books

参数的默认值是Java Script,最小长度是2。

1
2
3
4
5
http://127.0.0.1:8000/books/
[{"name":"python"},{"name":"fastapi"},{"name":"java"},{"name":"Java Script"}]

http://127.0.0.1:8000/books/?book=c
{"detail":[{"loc":["query","book"],"msg":"ensure this value has at least 2 characters","type":"value_error.any_str.min_length","ctx":{"limit_value":2}}]}

甚至可以直接用正则表达式来做检验,一般应该也用不到。

1
2
3
4
5
6
@app.get('/books/')
def root(book: Optional[str] = Query('Java Script', min_length=2, regex='^J')):

if book:
books.append({'name': book})
return books

必填参数

可以将 ... 用作第一个参数值,在声明必填参数。

1
2
3
4
5
6
@app.get('/books/')
def root(book: str = Query(..., min_length=2, regex='^J')):

if book:
books.append({'name': book})
return books

参数列表多个值

查询参数可以声明时接受多个值

1
2
3
4
5
6
7
8
books = ['python', 'fastapi']

@app.get('/books/')
def root(book: Optional[list] = Query(None)):

if book:
books.extend(book)
return books

结果

1
2
3
4
5
http://127.0.0.1:8000/books/
["python","fastapi"]

http://127.0.0.1:8000/books/?book=java&book=c
["python","fastapi","java","c"]

还有下面的校验,用到再说

  • alia
  • title
  • description
  • deprecated
123…40
唐胡璐

唐胡璐

i just wanna live while i am alive

393 posts
42 categories
74 tags
RSS
LinkedIn Weibo GitHub E-Mail
Creative Commons
© 2022 唐胡璐
Powered by Hexo
|
Theme — NexT.Pisces v5.1.4