为了账号安全,请及时绑定邮箱和手机立即绑定

如何在类中使用 FastAPI 创建路由

如何在类中使用 FastAPI 创建路由

慕盖茨4494581 2023-07-27 17:55:09
所以我需要在类中拥有一些路由,但路由方法需要具有 attr self(以访问类的属性)。然而,FastAPI 然后假设self是它自己的必需参数并将其作为查询参数放入这就是我所拥有的:app = FastAPI()class Foo:    def __init__(y: int):        self.x = y    @app.get("/somewhere")    def bar(self): return self.x但是,422除非您转到 ,否则该命令将会返回/somewhere?self=something。问题在于,这是selfstr,因此毫无用处。我需要一些我仍然可以访问的方法,self而无需将其作为必需参数。
查看完整描述

5 回答

?
胡子哥哥

TA贡献1825条经验 获得超6个赞

APIRouter这可以通过使用 an的方法来完成add_api_route:


from fastapi import FastAPI, APIRouter



class Hello:


    def __init__(self, name: str):

        self.name = name

        self.router = APIRouter()

        self.router.add_api_route("/hello", self.hello, methods=["GET"])


    def hello(self):

        return {"Hello": self.name}



app = FastAPI()

hello = Hello("World")

app.include_router(hello.router)


例子:


$ curl 127.0.0.1:5000/hello

{"Hello":"World"}

add_api_route的第二个参数 ( endpoint) 具有 type Callable[..., Any],因此任何可调用都应该可以工作(只要 FastAPI 可以找到如何解析其参数 HTTP 请求数据)。此可调用函数在 FastAPI 文档中也称为路径操作函数(下面称为“POF”)。


为什么装饰方法不起作用

警告:如果您对OP答案中的代码不起作用的技术解释不感兴趣,请忽略此答案的其余部分


在类主体中使用 and 朋友装饰方法@app.get是行不通的,因为您将有效地传递Hello.hello,而不是hello.hello(又名self.hello)到add_api_route。绑定和非绑定方法(自 Python 3 起简称为“函数” )具有不同的签名:


import inspect

inspect.signature(Hello.hello)  # <Signature (self)>

inspect.signature(hello.hello)  # <Signature ()>

FastAPI 做了很多魔法来尝试自动将 HTTP 请求中的数据(正文或查询参数)解析为 POF 实际使用的对象。


通过使用未绑定方法(=常规函数)( Hello.hello) 作为 POF,FastAPI 必须:


对包含路由的类的性质做出假设并动态生成self(也称为调用Hello.__init__)。这可能会给 FastAPI 增加很多复杂性,并且 FastAPI 开发人员(可以理解)似乎对支持这个用例不感兴趣。处理应用程序/资源状态的推荐方法似乎是将整个问题推迟到Depends.


以某种方式能够self从调用者发送的 HTTP 请求数据(通常是 JSON)生成对象。这在技术上对于字符串或其他内置函数之外的任何东西都是不可行的,因此实际上不可用。


OP 代码中发生的情况是#2。FastAPI 尝试从 HTTP 请求查询参数中解析Hello.hello(= self,类型) 的第一个参数,显然失败并引发,该参数作为 HTTP 422 响应显示给调用者。HelloRequestValidationError


self从查询参数解析

只是为了证明上面的#2,这里有一个(无用的)示例,说明 FastAPI 何时可以真正self从 HTTP 请求中“解析”:


(免责声明:请勿将以下代码用于任何实际应用)

from fastapi import FastAPI


app = FastAPI()


class Hello(str):

    @app.get("/hello")

    def hello(self):

        return {"Hello": self}

例子:


$ curl '127.0.0.1:5000/hello?self=World'

{"Hello":"World"}


查看完整回答
反对 回复 2023-07-27
?
30秒到达战场

TA贡献1828条经验 获得超6个赞

要创建基于类的视图,您可以使用fastapi-utils中的@cbv装饰器。使用它的动机:

停止在相关端点的签名中一遍又一遍地重复相同的依赖关系。

您的示例可以这样重写:

from fastapi import Depends, FastAPI

from fastapi_utils.cbv import cbv

from fastapi_utils.inferring_router import InferringRouter



def get_x():

    return 10



app = FastAPI()

router = InferringRouter()  # Step 1: Create a router



@cbv(router)  # Step 2: Create and decorate a class to hold the endpoints

class Foo:

    # Step 3: Add dependencies as class attributes

    x: int = Depends(get_x)


    @router.get("/somewhere")

    def bar(self) -> int:

        # Step 4: Use `self.<dependency_name>` to access shared dependencies

        return self.x



app.include_router(router)


查看完整回答
反对 回复 2023-07-27
?
人到中年有点甜

TA贡献1895条经验 获得超7个赞

我不喜欢这样做的标准方法,所以我编写了自己的库。你可以像这样安装它:


$ pip install cbfa

以下是如何使用它的示例:


from typing import Optional

from fastapi import FastAPI

from pydantic import BaseModel

from cbfa import ClassBased



app = FastAPI()

wrapper = ClassBased(app)


class Item(BaseModel):

    name: str

    price: float

    is_offer: Optional[bool] = None


@wrapper('/item')

class Item:

    def get(item_id: int, q: Optional[str] = None):

        return {"item_id": item_id, "q": q}


    def post(item_id: int, item: Item):

        return {"item_name": item.name, "item_id": item_id}

请注意,您不需要在每个方法周围包装装饰器。根据方法在 HTTP 协议中的用途来命名这些方法就足够了。整个类都变成了一个装饰器。


查看完整回答
反对 回复 2023-07-27
?
慕沐林林

TA贡献2016条经验 获得超9个赞

我把路线放到def __init__. 它工作正常。例子:


from fastapi import FastAPI

from fastapi.responses import HTMLResponse


class CustomAPI(FastAPI):

    def __init__(self, title: str = "CustomAPI") -> None:

        super().__init__(title=title)


        @self.get('/')

        async def home():

            """

            Home page

            """

            return HTMLResponse("<h1>CustomAPI</h1><br/><a href='/docs'>Try api now!</a>", status_code=status.HTTP_200_OK)



查看完整回答
反对 回复 2023-07-27
?
喵喔喔

TA贡献1735条经验 获得超5个赞

我刚刚发布了一个项目,允许您使用类实例通过简单的装饰器进行路由处理。cbv很酷,但路由是在类本身上,而不是类的实例上。能够使用类实例可以让您以一种对我来说更简单、更直观的方式进行依赖项注入。


例如,以下内容按预期工作:


from classy_fastapi import Routable, get, delete


class UserRoutes(Routable):

   """Inherits from Routable."""


   # Note injection here by simply passing values

   # to the constructor. Other injection frameworks also 

   # supported as there's nothing special about this __init__ method.

   def __init__(self, dao: Dao) -> None:

      """Constructor. The Dao is injected here."""

      super().__init__()

      self.__dao = Dao


   @get('/user/{name}')

   def get_user_by_name(name: str) -> User:

      # Use our injected DAO instance.

      return self.__dao.get_user_by_name(name)


   @delete('/user/{name}')

   def delete_user(name: str) -> None:

      self.__dao.delete(name)



def main():

    args = parse_args()

    # Configure the DAO per command line arguments

    dao = Dao(args.url, args.user, args.password)

    # Simple intuitive injection

    user_routes = UserRoutes(dao)

    

    app = FastAPI()

    # router member inherited from Routable and configured per the annotations.

    app.include_router(user_routes.router)

您可以在 PyPi 上找到它并通过 进行安装pip install classy-fastapi



查看完整回答
反对 回复 2023-07-27
  • 5 回答
  • 0 关注
  • 460 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信