FastAPI 4 - Response

本文介绍了FastAPI中的响应模型,包括响应状态码、错误处理机制,如基本错误处理、自定义异常处理、覆盖原有错误和请求验证错误。此外,还讲解了json兼容编码器的使用以及Body的更新操作,如PUT和PATCH方法的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


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(三)
凌晨下了小雨,白天又没下了,期望下雨
没去上班

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值