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"}
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)
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 协议中的用途来命名这些方法就足够了。整个类都变成了一个装饰器。
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)
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
。
添加回答
举报