zoukankan      html  css  js  c++  java
  • FastAPI 进阶知识(三) 错误处理

    作者:麦克煎蛋   出处:https://www.cnblogs.com/mazhiyong/ 转载请保留这段声明,谢谢!

    如果使用API时有错误发生,你需要通知给客户端(Web端或者API使用者)这个错误信息。

    常见的错误信息为:

    • 客户端没有权限进行相关的操作。
    • 客户端找不到对应的路径操作。
    • 客户端找不到对应的资源。
    • 其他。

    这些错误信息的HTTP状态码一般为400错误(400~499)。

    一、HTTPException

    我们用HTTPException模块返回带错误信息的Response。

    HTTPException是一个普通的Python异常,同时带有与API访问有关的附加数据。

    1、导入模块

    from fastapi import HTTPException

    2、抛出异常

    在代码中抛出HTTPException

    raise HTTPException(status_code=404, detail="Item not found")

    这里, 参数detail除了可以传递字符串,还可以传递任何可以转换成JSON格式的数据,如dict、list等。

    代码示例:

    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")
    return {"item": items[item_id]}

    二、添加自定义头信息

    有时候针对HTTP错误,在一些场景下,我们需要添加自定义头信息。

    from fastapi import FastAPI, HTTPException
    
    app = FastAPI()
    
    items = {"foo": "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"},
            )
        return {"item": items[item_id]}

    三、自定义异常处理器

    借助 the same exception utilities from Starlette,我们可以添加自定义异常处理器。

    假设我们有个自定义异常 UnicornException,我们想在全局范围内处理这个异常。

    借助 @app.exception_handler(),就可以实现我们的目标。

    from fastapi import FastAPI, Request
    from fastapi.responses import JSONResponse
    
    
    class UnicornException(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=418,
            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}

    这里如果我们请求 /unicorns/yolo,路径操作函数就会抛出异常 UnicornException,这个异常会被我们的异常处理器unicorn_exception_handler捕获到。

    最后我们收到的HTTP错误码就是418,并且错误内容为:

    {"message": "Oops! yolo did something. There goes a rainbow..."}

    四、重写缺省异常处理器

    FastAPI有一些缺省的异常处理器。当我们抛出HTTPException异常或者当请求有非法数据的时候,这些处理器负责返回默认的JSON结果。

    我们可以重写这些异常处理器。

    1、重写请求校验异常处理器

    当一个请求包含非法数据的时候,FastAPI内部会抛出RequestValidationError异常,并且有默认的异常处理器来处理。

    我们可以用 @app.exception_handler(RequestValidationError) 来重写这个异常处理器。

    from fastapi import FastAPI, HTTPException
    from fastapi.exceptions import RequestValidationError
    from fastapi.responses import PlainTextResponse
    
    app = FastAPI()
    
    @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.")
        return {"item_id": item_id}

    如果我们请求 /items/foo,那么返回结果就不是默认的:

    {
        "detail": [
            {
                "loc": [
                    "path",
                    "item_id"
                ],
                "msg": "value is not a valid integer",
                "type": "type_error.integer"
            }
        ]
    }

    而是:

    1 validation error
    path -> item_id
      value is not a valid integer (type=type_error.integer)

    同时RequestValidationError有个body字段,包含了请求内容的原文。

    from fastapi import FastAPI, Request, status
    from fastapi.encoders import jsonable_encoder
    from fastapi.exceptions import RequestValidationError
    from fastapi.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

    如果我们传递了不合法的数据:

    {
      "title": "towel",
      "size": "XL"
    }

    那么我们收到的返回结果如下:

    {
      "detail": [
        {
          "loc": [
            "body",
            "item",
            "size"
          ],
          "msg": "value is not a valid integer",
          "type": "type_error.integer"
        }
      ],
      "body": {
        "title": "towel",
        "size": "XL"
      }
    }

    2、重写HTTPException异常处理器

    同样的方法,我们可以重写HTTPException异常处理器。

    例如,你可能想返回纯文本格式而不是JSON格式的错误信息。

    from fastapi import FastAPI, HTTPException
    from fastapi.responses import PlainTextResponse
    from starlette.exceptions import HTTPException as StarletteHTTPException
    
    app = FastAPI()
    
    
    @app.exception_handler(StarletteHTTPException)
    async def http_exception_handler(request, exc):
        return PlainTextResponse(str(exc.detail), status_code=exc.status_code)
    
    @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.")
    return {"item_id": item_id}

    如果请求 /items/3,这时候返回的错误信息为:

    Nope! I don't like 3.

    如果没有自定义异常处理器http_exception_handler,返回的错误信息为:

    {
        "detail": "Nope! I don't like 3."
    }

    五、重用缺省异常处理器 

    我们可以导入并且重用缺省的异常处理器。

    我们从fastapi.exception_handlers导入缺省异常处理器。

    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()
    
    
    @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)
    
    
    @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)
    
    
    @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.")
        return {"item_id": item_id}

    在示例中,我们在抛出异常之前添加了一条日志输出。我们可以根据业务需求灵活的重用缺省异常处理器。

    六、FastAPI HTTPException 对比 Starlette HTTPException

    FastAPI HTTPException 继承自 Starlette's HTTPException

    唯一的区别是,FastAPI HTTPException允许你在response添加头信息。主要在内部用于OAuth 2.0以及一些安全相关的功能。

    因此,通常我们在代码中抛出FastAPI HTTPException异常。

    但是,当我们注册异常处理器的时候,我们应该注册为Starlette HTTPException

    这样,当Starlette的内部代码或者Starlette扩展插件抛出Starlette HTTPException时,我们的处理器才能正常捕获和处理这个异常。

    如果我们要在代码中同时使用这两个类,为了避免命名冲突,我们可以重命名其中一个类。

    from fastapi import HTTPException
    from starlette.exceptions import HTTPException as StarletteHTTPException
  • 相关阅读:
    题解:SPOJ1026 Favorite Dice
    机器学习笔记
    机器学习第二次作业
    机器学习第一次作业
    2019软工实践总结作业
    软工2019作业6——软件评测
    2019软工作业五-实现师门树
    软件工程 “校园汇” 个人IDEA竞选分析与总结
    软工2019作业四
    软工2019作业三
  • 原文地址:https://www.cnblogs.com/mazhiyong/p/12965973.html
Copyright © 2011-2022 走看看