Skip to content

Commit c0bb3c6

Browse files
committed
增加自定义参数字段校验返回自定义格式,自定义错误提示消息
1 parent b8e6298 commit c0bb3c6

4 files changed

Lines changed: 94 additions & 28 deletions

File tree

README.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
11
## FastAPI 框架精讲
2+
3+
# 如何设置虚拟环境
4+
1. cd 项目目录
5+
2. virtualenv venv
6+
3. cd venv/bin
7+
4. source activate
8+
5. cd 项目目录
9+
6. pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt
210
> 整体的介绍 FastAPI,快速上手开发,结合 API 交互文档逐个讲解核心模块的使用
311
412
![drf-tutorial](./coronavirus/static/fastapi-tutorial.png)
@@ -347,4 +355,6 @@ FastAPI 项目中 Jinja2 配置,使用 Jinja2 渲染 COVID-19 查询页面,
347355

348356
#### 9.1 课程总结
349357

350-
回顾课程内容与重难点,总结经验、心得以及扩展建议。
358+
回顾课程内容与重难点,总结经验、心得以及扩展建议。##
359+
> 整体的介绍 FastAPI,快速上手开发,结合 API 交互文档逐个讲解核心模块的使用
360+
框架精讲## h 框架精讲

requirements.txt

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
aiofiles==0.6.0
2+
annotated-types==0.6.0
3+
anyio==3.7.1
24
atomicwrites==1.4.0
35
attrs==20.3.0
46
bcrypt==3.2.0
@@ -11,7 +13,8 @@ cryptography==3.3.1
1113
dnspython==2.0.0
1214
ecdsa==0.14.1
1315
email-validator==1.1.2
14-
fastapi==0.63.0
16+
exceptiongroup==1.2.0
17+
fastapi==0.104.1
1518
h11==0.11.0
1619
idna==2.10
1720
importlib-metadata==3.3.0
@@ -24,18 +27,20 @@ pluggy==0.13.1
2427
py==1.10.0
2528
pyasn1==0.4.8
2629
pycparser==2.20
27-
pydantic==1.7.3
30+
pydantic==2.5.2
31+
pydantic_core==2.14.5
2832
pyparsing==2.4.7
2933
pytest==6.2.1
3034
python-jose==3.2.0
3135
python-multipart==0.0.5
3236
requests==2.25.1
3337
rsa==4.6
3438
six==1.15.0
39+
sniffio==1.3.0
3540
SQLAlchemy==1.3.22
36-
starlette==0.13.6
41+
starlette==0.27.0
3742
toml==0.10.2
38-
typing-extensions==3.7.4.3
43+
typing_extensions==4.8.0
3944
urllib3==1.26.2
4045
uvicorn==0.13.2
4146
zipp==3.4.0

run.py

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@
77
from fastapi import FastAPI, Request
88
from fastapi.middleware.cors import CORSMiddleware
99
from fastapi.staticfiles import StaticFiles
10+
from pydantic import ValidationError
1011

12+
import tutorial.chapter03
1113
from coronavirus import application
1214
from tutorial import app03, app04, app05, app06, app07, app08
1315

14-
# from fastapi.exceptions import RequestValidationError
15-
# from fastapi.responses import PlainTextResponse
16+
from fastapi.exceptions import RequestValidationError
17+
from fastapi.responses import PlainTextResponse,JSONResponse
1618
# from starlette.exceptions import HTTPException as StarletteHTTPException
1719

1820
app = FastAPI(
@@ -37,23 +39,53 @@
3739
# return PlainTextResponse(str(exc.detail), status_code=exc.status_code)
3840
#
3941
#
42+
43+
44+
# @app.exception_handler(ValidationError) # 重写HTTPException异常处理器
45+
# async def http_exception_handler(request, exc):
46+
# print("&&&"*90)
47+
# """
48+
# :param request: 这个参数不能省
49+
# :param exc:
50+
# :return:
51+
# """
52+
# return PlainTextResponse(str(exc.detail), status_code=exc.status_code)
53+
# 1.用户自定义异常类型,只要该类继承了Exception类即可
54+
@app.exception_handler(tutorial.chapter03.ValDtoError)
55+
async def request_validation_exception_handler2(request: Request, exc: tutorial.chapter03.ValDtoError):
56+
print(f"===>参数校验异常{request.method} {request.url}")
57+
return JSONResponse({"message":exc.message})
4058
# @app.exception_handler(RequestValidationError) # 重写请求验证异常处理器
4159
# async def validation_exception_handler(request, exc):
60+
#
4261
# """
4362
# :param request: 这个参数不能省
4463
# :param exc:
4564
# :return:
4665
# """
47-
# return PlainTextResponse(str(exc), status_code=400)
66+
# print("222"*10)
67+
# # 自定义错误输出信息
68+
# errors = []
69+
# for error in exc.errors():
70+
# print(error)
71+
# print(type(error))
72+
# errors.append({
73+
# "loc": error["loc"],
74+
# "msg": error["msg"],
75+
# "type": error["type"]
76+
# })
77+
# # raise HTTPException(status_code=422, detail=errors)
78+
#
79+
# return PlainTextResponse(str(errors), status_code=400)
4880

4981

50-
@app.middleware('http')
51-
async def add_process_time_header(request: Request, call_next): # call_next将接收request请求做为参数
52-
start_time = time.time()
53-
response = await call_next(request)
54-
process_time = time.time() - start_time
55-
response.headers['X-Process-Time'] = str(process_time) # 添加自定义的以“X-”开头的请求头
56-
return response
82+
# @app.middleware('http')
83+
# async def add_process_time_header(request: Request, call_next): # call_next将接收request请求做为参数
84+
# start_time = time.time()
85+
# response = await call_next(request)
86+
# process_time = time.time() - start_time
87+
# response.headers['X-Process-Time'] = str(process_time) # 添加自定义的以“X-”开头的请求头
88+
# return response
5789

5890

5991
app.add_middleware(

tutorial/chapter03.py

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44

55
from datetime import date
66
from enum import Enum
7-
from typing import Optional, List
7+
from typing import Optional, List, Annotated
88

99
from fastapi import APIRouter, Query, Path, Body, Cookie, Header
10-
from pydantic import BaseModel, Field
10+
from pydantic import BaseModel, Field, BeforeValidator
1111

1212
app03 = APIRouter()
1313

@@ -45,7 +45,7 @@ def filepath(file_path: str):
4545

4646
@app03.get("/path_/{num}")
4747
def path_params_validate(
48-
num: int = Path(..., title="Your Number", description="不可描述", ge=1, le=10),
48+
num: int = Path(..., title="Your Number", description="不可描述", ge=1, le=10),
4949
):
5050
return num
5151

@@ -65,30 +65,49 @@ def type_conversion(param: bool = False):
6565
return param
6666

6767

68+
class ValDtoError(Exception):
69+
# 初始化
70+
def __init__(self, message):
71+
self.message = message
72+
73+
# 类一般返回值
74+
def __str__(self):
75+
return "参数校验异常!" + self.message
76+
77+
6878
@app03.get("/query/validations") # 长度+正则表达式验证,比如长度8-16位,以a开头。其它校验方法看Query类的源码
6979
def query_params_validate(
70-
value: str = Query(..., min_length=8, max_length=16, regex="^a"), # ...换成None就变成选填的参数
71-
values: List[str] = Query(["v1", "v2"], alias="alias_name")
80+
value: str = Query(..., min_length=8, max_length=16, regex="^a"), # ...换成None就变成选填的参数
81+
values: List[str] = Query(["v1", "v2"], alias="alias_name")
7282
): # 多个查询参数的列表。参数别名
7383
return value, values
7484

7585

7686
"""Request Body and Fields 请求体和字段"""
7787

7888

89+
def curr_page_v(v: int) -> int:
90+
if 111 > v:
91+
raise ValDtoError('开始页不能小于0!')
92+
return v
93+
94+
7995
class CityInfo(BaseModel):
8096
name: str = Field(..., example="Beijing") # example是注解的作用,值不会被验证
8197
country: str
8298
country_code: str = None # 给一个默认值
8399
country_population: int = Field(default=800, title="人口数量", description="国家的人口数量", ge=800)
84100

101+
currPage: Annotated[int, BeforeValidator(curr_page_v)]
102+
85103
class Config:
86104
schema_extra = {
87105
"example": {
88106
"name": "Shanghai",
89107
"country": "China",
90108
"country_code": "CN",
91109
"country_population": 1400000000,
110+
"price": "210.0"
92111
}
93112
}
94113

@@ -104,11 +123,11 @@ def city_info(city: CityInfo):
104123

105124
@app03.put("/request_body/city/{name}")
106125
def mix_city_info(
107-
name: str,
108-
city01: CityInfo,
109-
city02: CityInfo, # Body可以是多个的
110-
confirmed: int = Query(ge=0, description="确诊数", default=0),
111-
death: int = Query(ge=0, description="死亡数", default=0),
126+
name: str,
127+
city01: CityInfo,
128+
city02: CityInfo, # Body可以是多个的
129+
confirmed: int = Query(ge=0, description="确诊数", default=0),
130+
death: int = Query(ge=0, description="死亡数", default=0),
112131
):
113132
if name == "Shanghai":
114133
return {"Shanghai": {"confirmed": confirmed, "death": death}}
@@ -117,9 +136,9 @@ def mix_city_info(
117136

118137
@app03.put("/request_body/multiple/parameters")
119138
def body_multiple_parameters(
120-
city: CityInfo = Body(..., embed=True), # 当只有一个Body参数的时候,embed=True表示请求体参数嵌套。多个Body参数默认就是嵌套的
121-
confirmed: int = Query(ge=0, description="确诊数", default=0),
122-
death: int = Query(ge=0, description="死亡数", default=0),
139+
city: CityInfo = Body(..., embed=True), # 当只有一个Body参数的时候,embed=True表示请求体参数嵌套。多个Body参数默认就是嵌套的
140+
confirmed: int = Query(ge=0, description="确诊数", default=0),
141+
death: int = Query(ge=0, description="死亡数", default=0),
123142
):
124143
print(f"{city.name} 确诊数:{confirmed} 死亡数:{death}")
125144
return city.dict()

0 commit comments

Comments
 (0)