文章目录
from : https://www.bilibili.com/video/BV1oE411M7tF
一、响应模型
No.18
1、
from typing import List
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str = None
price: float
tax: float = None
tags: List[str] = []
@app.post("/items/", response_model =Item)
async def create_item(item: Item):
return item
if __name__ == '__main__':
import uvicorn
uvicorn.run(app, host="127.0.0.1", port=8000)
import requests
import json
def test1():
url = 'http://127.0.0.1:8000/items/'
body = {
"name": "string",
"description": "string",
"price": 0,
"tax": 0,
"tags": ['1']
}
data = json.dumps(body)
ret = requests.post(url=url, data=data)
print(ret)
print(ret.text) # {"name":"string","description":"string","price":0.0,"tax":0.0,"tags":["1"]}
if __name__ == "__main__":
test1()
2
需要安装
pip install pydantic[email]
from fastapi import FastAPI
from pydantic import BaseModel, EmailStr
app = FastAPI()
class UserIn(BaseModel):
username: str
password: str
email: EmailStr
full_name: str = None
class UserOut(BaseModel):
username: str
email: EmailStr
full_name: str = None
@app.post("/user/", response_model=UserOut)
async def create_user(*, user: UserIn):
return user
def test1():
url = 'http://127.0.0.1:8000/user/'
body = {
"username": "string",
"password": "string",
"email": "user@example.com",
"full_name": "string"
}
data = json.dumps(body)
ret = requests.post(url=url, data=data)
print(ret)
print(ret.text) # {"username":"string","email":"user@example.com","full_name":"string"}
三、响应状态码
from fastapi import FastAPI
from starlette import status
app = FastAPI()
@app.post("/items/", status_code=201)
async def create_item(name: str):
return {"name": name}
'''
post http://127.0.0.1:8000/items/?name=123
<Response [201]>
{"name":"123"}
'''
@app.get("/items2/", status_code=201)
async def create_item2(name: str):
return {"name": name}
'''
get http://127.0.0.1:8000/items2/?name=123
<Response [201]>
{"name":"123"}
'''
@app.post("/items3/", status_code=status.HTTP_404_NOT_FOUND)
async def create_item3(name: str):
print('HTTP_404:', status.HTTP_404_NOT_FOUND)
return {"name": name}
'''
post http://127.0.0.1:8000/items3/?name=123
<Response [404]>
{"name":"123"}
'''
@app.get("/i/", status_code=status.HTTP_404_NOT_FOUND)
async def i(name: str):
print('HTTP_404:', status.HTTP_404_NOT_FOUND)
return {"name": name}
'''
get http://127.0.0.1:8000/i/?name=123
<Response [404]>
{"name":"123"}
'''
if __name__ == '__main__':
import uvicorn
uvicorn.run(app, host="127.0.0.1", port=8000)
四、错误处理
1、基本错误处理
from fastapi import FastAPI, HTTPException
app = FastAPI()
items = {"foo": "The Foo Wrestlers"}
@app.get("/items/{item_id}")
async def read_item(item_id: str):
if item_id not in items:
raise HTTPException(status_code=404, detail="Item not found")
pass
pass
return {"item": items[item_id]}
'''
get http://127.0.0.1:8000/items/hi
<Response [404]>
{"detail":"Item not found"}
get http://127.0.0.1:8000/items/foo
<Response [200]>
{"item":"The Foo Wrestlers"}
'''
# 添加自定义标题
@app.get("/items-header/{item_id}")
async def read_item_header(item_id: str):
if item_id not in items:
raise HTTPException(
status_code=404,
detail="Item not found",
headers={"X-Error": "There goes my error"},
# X-Error 自定义。例如,对于某些类型的安全性。OAuth 2.0和某些安全实用程序在内部需要/使用此功能。
)
return {"item": items[item_id]}
'''
get http://127.0.0.1:8000/items-header/foo
<Response [200]>
{"item":"The Foo Wrestlers"}
get http://127.0.0.1:8000/items-header/jack
<Response [404]>
{"detail":"Item not found"}
'''
2、安装自定义异常处理程序
使用装饰器
from fastapi import FastAPI
# from fastapi import FastAPI, Request
# from fastapi.responses import JSONResponse
from starlette.requests import Request
from starlette.responses import JSONResponse
# 安装自定义异常处理程序
# 自定义异常UnicornException
class UnicornException(Exception): # Exception 常规错误的基类
def __init__(self, name: str):
self.name = name
app = FastAPI()
@app.exception_handler(UnicornException)
async def unicorn_exception_handler(request: Request, exc: UnicornException):
return JSONResponse(
status_code=404, # 418 I'm a teapot
content={"message": f"Oops! {exc.name} did something. There goes a rainbow..."},
)
@app.get("/unicorns/{name}")
async def read_unicorn(name: str):
if name == "yolo":
raise UnicornException(name=name)
return {"unicorn_name": name}
'''
get http://127.0.0.1:8000/unicorns/jack
<Response [200]>
{"unicorn_name":"jack"}
get http://127.0.0.1:8000/unicorns/yolo
<Response [404]>
{"message":"Oops! yolo did something. There goes a rainbow..."}
'''
3、覆盖原有错误
from fastapi import FastAPI, HTTPException
from fastapi.exceptions import RequestValidationError
# from fastapi.responses import PlainTextResponse
from starlette.responses import PlainTextResponse
from starlette.exceptions import HTTPException as StarletteHTTPException
app = FastAPI()
# 覆盖默认的异常处理程序
@app.exception_handler(StarletteHTTPException) # 重写HTTPException错误处理程序
async def http_exception_handler(request, exc):
return PlainTextResponse(str(exc.detail), status_code=exc.status_code)
@app.exception_handler(RequestValidationError) # 导入超越请求验证异常
async def validation_exception_handler(request, exc):
return PlainTextResponse(str(exc), status_code=400)
@app.get("/items/{item_id}")
async def read_item(item_id: int):
if item_id == 3:
raise HTTPException(status_code=418, detail="Nope! I don't like 3.") # 418 I'm a teapot
return {"item_id": item_id}
'''
get http://127.0.0.1:8000/items/4
<Response [200]>
{"item_id":4}
get http://127.0.0.1:8000/items/3
<Response [418]>
Nope! I don't like 3.
'''
4、请求验证错误
from fastapi import FastAPI
# from fastapi import FastAPI, Request, status
from starlette.requests import Request
from starlette import status
from fastapi.encoders import jsonable_encoder
from fastapi.exceptions import RequestValidationError
# from fastapi.responses import JSONResponse
from starlette.responses import JSONResponse
from pydantic import BaseModel
app = FastAPI()
@app.exception_handler(RequestValidationError) # 请求验证错误
async def validation_exception_handler(request: Request, exc: RequestValidationError):
return JSONResponse(
# 拿出错误重写
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
content=jsonable_encoder({"detail": exc.errors(), "body": exc.body}),
)
class Item(BaseModel):
title: str
size: int
@app.post("/items/")
async def create_item(item: Item):
return item
def test1():
url = 'http://127.0.0.1:8000/items/'
body = {
"title": "string",
"size": 0
}
data = json.dumps(body)
ret = requests.post(url=url, data=data)
# ret = requests.post(url=url)
print(ret)
print(ret.text) # {"title":"string","size":0}
...
body = {
"title": "string",
"size": "a" # 修改此处类型为非 int
}
...
<Response [422]>
{"detail":[{"loc":["body","size"],"msg":"value is not a valid integer","type":"type_error.integer"}],"body":{"title":"string","size":"a"}}
5、抽出异常和错误
from fastapi import FastAPI, HTTPException
from fastapi.exception_handlers import (
http_exception_handler,
request_validation_exception_handler,
)
from fastapi.exceptions import RequestValidationError
from starlette.exceptions import HTTPException as StarletteHTTPException
app = FastAPI()
# FastAPI、starlette都有自己的HTTPException,
# 区别是,FastAPI的HTTPException允许您添加要包含在响应头。OAuth 2.0和某些安全实用程序在内部需要/使用此功能。
@app.exception_handler(StarletteHTTPException)
async def custom_http_exception_handler(request, exc):
print(f"OMG! An HTTP error!: {exc}")
return await http_exception_handler(request, exc) # 重用Starlette的异常处理程序
@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request, exc):
print(f"OMG! The client sent invalid data!: {exc}")
return await request_validation_exception_handler(request, exc) # 重用FastAPI的异常处理程序
@app.get("/items/{item_id}")
async def read_item(item_id: int):
if item_id == 3:
raise HTTPException(status_code=418, detail="Nope! I don't like 3.") # 418 I'm a teapot
return {"item_id": item_id}
'''
get http://127.0.0.1:8000/items/3
<Response [418]>
{"detail":"Nope! I don't like 3."}
get http://127.0.0.1:8000/items/ab
<Response [422]>
{"detail":[{"loc":["path","item_id"],"msg":"value is not a valid integer","type":"type_error.integer"}]}
'''
五、json 兼容编码器
from datetime import datetime
from fastapi import FastAPI
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel
fake_db = {}
class Item(BaseModel):
title: str
timestamp: datetime # datetime 是 python 格式
description: str = None
app = FastAPI()
@app.put("/items/{id}")
def update_item(id: str, item: Item):
json_compatible_item_data = jsonable_encoder(item)
# jsonable_encoder实际上由FastAPI在内部用于转换数据。但这在许多其他情况下很有用。
fake_db[id] = json_compatible_item_data
print(json_compatible_item_data)
print(type(json_compatible_item_data))
print(fake_db)
print(type(fake_db))
# return fake_db
def test1():
url = 'http://127.0.0.1:8000/items/a'
body = {
"title": "string",
"timestamp": "2021-12-15T06:29:52.037Z",
"description": "string"
}
data = json.dumps(body)
ret = requests.put(url=url, data=data)
print(ret)
print(ret.text) # {"title":"string","size":0}
'''
{'title': 'string', 'timestamp': '2021-12-15T06:29:52.037000+00:00', 'description': 'string'}
<class 'dict'>
{'ab': {'title': 'string', 'timestamp': '2021-12-15T06:32:18.631000+00:00', 'description': 'string'}, 'a': {'title': 'string', 'timestamp': '2021-12-15T06:29:52.037000+00:00', 'description': 'string'}}
<class 'dict'>
'''
六、Body 的更新
1、put
put 更新已有的,没有的不添加
from typing import List
from fastapi import FastAPI
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str = None
description: str = None
price: float = None
tax: float = 10.5
tags: List[str] = []
items = {
"foo": {"name": "Foo", "price": 50.2},
"bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
"baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
}
@app.get("/items/{item_id}", response_model=Item)
async def read_item(item_id: str):
return items[item_id]
@app.put("/items/{item_id}", response_model=Item)
async def update_item(item_id: str, item: Item):
update_item_encoded = jsonable_encoder(item)
items[item_id] = update_item_encoded
print(items)
return update_item_encoded
def test1():
url = 'http://127.0.0.1:8000/items/foo'
body = {
# "name": "string",
"description": "string",
"price": 5,
"tax": 11,
"tags": []
}
data = json.dumps(body)
ret = requests.put(url=url, data=data)
# ret = requests.post(url=url)
print(ret)
print(ret.text) # {"name":null,"description":"string","price":5.0,"tax":11.0,"tags":[]}
2、 patch
局部更新
from typing import List
from fastapi import FastAPI
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str = None
description: str = None
price: float = None
tax: float = 10.5
tags: List[str] = []
items = {
"foo": {"name": "Foo", "price": 50.2},
"bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
"baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
}
@app.get("/items/{item_id}", response_model=Item)
async def read_item(item_id: str):
return items[item_id]
# patch 数据的局部更新
@app.patch("/items/{item_id}", response_model=Item)
async def update_item(item_id: str, item: Item):
stored_item_data = items[item_id] # 存储项目数据
stored_item_model = Item(**stored_item_data) # 存储项目模型
update_data = item.dict(exclude_unset=True)
print('update_data', update_data)
updated_item = stored_item_model.copy(update=update_data)
# print('update_item', update_item)
print('updated_item', jsonable_encoder(updated_item))
items[item_id] = jsonable_encoder(updated_item)
print('items[item_id]', items[item_id])#
return updated_item
def test1():
url = 'http://127.0.0.1:8000/items/foo'
body = {
# "name": "string",
"description": "string",
"price": 5,
"tax": 11,
"tags": []
}
data = json.dumps(body)
ret = requests.patch(url=url, data=data)
# ret = requests.post(url=url)
print(ret)
print(ret.text) # {"name":"Foo","description":"string","price":5.0,"tax":11.0,"tags":[]}
伊织 2021-12-15(三)
凌晨下了小雨,白天又没下了,期望下雨
没去上班