FastAPI:从视图名称(路由名称)中检索 URL
Posted
技术标签:
【中文标题】FastAPI:从视图名称(路由名称)中检索 URL【英文标题】:FastAPI: Retrieve URL from view name ( route name ) 【发布时间】:2020-12-20 06:58:03 【问题描述】:假设我有以下观点,
from fastapi import FastAPI
app = FastAPI()
@app.get('/hello/')
def hello_world():
return "msg": "Hello World"
@app.get('/hello/number/')
def hello_world_number(number: int):
return "msg": "Hello World Number", "number": number
我一直在 Flask 和 Django 中使用这些函数
Flask:url_for(...)
Django: reverse(...)
那么,如何以类似的方式获取/构建hello_world
和hello_world_number
的URL?
【问题讨论】:
【参考方案1】:我们有位于starlette 包内的Router.url_path_for(...)
方法
方法一:使用FastAPI
实例
当您能够在当前上下文中访问FastAPI
实例时,此方法很有用。 (感谢@Yagizcan Degirmenci)
from fastapi import FastAPI
app = FastAPI()
@app.get('/hello/')
def hello_world():
return "msg": "Hello World"
@app.get('/hello/number/')
def hello_world_number(number: int):
return "msg": "Hello World Number", "number": number
print(app.url_path_for('hello_world'))
print(app.url_path_for('hello_world_number', **"number": 1))
print(app.url_path_for('hello_world_number', **"number": 2))
# Results
/hello/
/hello/1/
/hello/2/
缺点
如果我们使用APIRouter
,router.url_path_for('hello_world')
可能不起作用,因为router
不是FastAPI
类的实例。也就是说,我们必须有 FastAPI
实例来解析 URL
方法 2:Request
实例
当您能够访问Request
实例(传入请求)时,此方法很有用,通常是在视图中。
from fastapi import FastAPI, Request
app = FastAPI()
@app.get('/hello/')
def hello_world():
return "msg": "Hello World"
@app.get('/hello/number/')
def hello_world_number(number: int):
return "msg": "Hello World Number", "number": number
@app.get('/')
def named_url_reveres(request: Request):
return
"URL for 'hello_world'": request.url_for("hello_world"),
"URL for 'hello_world_number' with number '1'": request.url_for("hello_world_number", **"number": 1),
"URL for 'hello_world_number' with number '2''": request.url_for("hello_world_number", **"number": 2)
# Result Response
"URL for 'hello_world'": "http://0.0.0.0:6022/hello/",
"URL for 'hello_world_number' with number '1'": "http://0.0.0.0:6022/hello/1/",
"URL for 'hello_world_number' with number '2''": "http://0.0.0.0:6022/hello/2/"
缺点
我们必须在每个(或必需的)视图中包含request
参数来解析 URL,这可能会给开发人员带来丑的感觉。
【讨论】:
【参考方案2】:url_for 存在,但由支持 FastApi 的服务器 starlette 提供: https://www.starlette.io/routing/#reverse-url-lookups
【讨论】:
【参考方案3】:其实你不需要重新发明***。 FastAPI 支持这种开箱即用的 (实际上是 Starlette),而且效果很好。
app = FastAPI()
@app.get("/hello/number/")
def hello_world_number(number: int):
return "msg": "Hello World Number", "number": number
如果你有这样的端点,你可以简单地使用
In: app.url_path_for("hello_world_number", number=3)
In: app.url_path_for("hello_world_number", number=50)
Out: /hello/3/
Out: /hello/50/
在 FastAPI 中,APIRouter 和 FastAPI(APIRoute) 继承自 Router(Starlette's) 所以,如果你有这样的APIRouter,你可以继续使用这个功能
router = APIRouter()
@router.get("/hello")
def hello_world():
return "msg": "Hello World"
In: router.url_path_for("hello_world")
Out: /hello
【讨论】:
当您有多个路由器文件并希望在不同的文件中获得url_path_for
路由时,您会建议哪种方法?我的main.py
做了一堆app.include_router
来获取所有路线。谢谢!
@Shawn 我在基于视图函数的视图中使用了return fastapi.responses.RedirectResponse(url=request.url_for(name='account'), status_code=status.HTTP_302_FOUND)
您的方法返回路径,而不是 URL。 flask.url_for()
返回绝对 URL 值【参考方案4】:
如果在多个 APIRouter 下定义了相同的函数名,request.url_for
和 router.url_path_for
将返回第一个匹配的函数名(按 include_router 的顺序)。
如果有人需要,这是一种在函数名称冲突时使用 APIRouter 标签获取正确 URL 的方法:第 1 步: 将其放入您的__init__.py
:
def url_of(request: Request, name: str, **path_params: dict):
from fastapi.routing import APIRoute
from starlette.routing import NoMatchFound
tag, tid, fname = None, name.find('.'), name
if tid > 0:
tag = name[:tid]
fname = name[tid + 1:]
url_no_tag = None
for route in request.app.router.routes:
if not isinstance(route, APIRoute):
continue
if fname == route.name and (not tag or tag in route.tags):
try:
url_path = route.url_path_for(fname, **path_params)
url_no_tag = url_path.make_absolute_url(base_url=request.base_url)
if tag:
return url_no_tag
except NoMatchFound:
pass
if url_no_tag:
return url_no_tag
return request.url_for(name, **path_params)
第二步:为APIRouters添加标签:
router = APIRouter(prefix='/user', tags=['user'])
@router.get('/')
def login():
return 'login page'
第 3 步: 在任意位置检索 url:
@router2.get('/test')
def test(request: Request):
return RedirectResponse(url_of(request, 'user.login') + '?a=1')
2021/07/10 将 url_as
重命名为 url_of
【讨论】:
它工作正常。但是当父方法和目标方法具有相同的路由时,您只能重定向响应。例如,如果测试是 post 方法,则意味着您只能调用 post 方法,而不能使用 get 方法调用post 方法请求对象。 这个答案旨在解决'jpg'提出的函数名构建URL的问题。 RedirectResponse 是如何使用构建的 URL 的示例。 RedirectResponse 以 307 作为默认状态码返回(在重定向期间以相同的方式发起新请求)。如果 'test' 需要 POST 并且 'login' 是 GET,我们可以将 status_code 参数设置为 302:RedirectResponse(url_as(request, 'user.login') + '?a=1', status_code=302)
。 url_as
也可以以其他方式使用。其实我把url_as
注册为jinja2中的全局模板方法@NAGARAJS
如果request.url_for
中的request
是传入请求实例,则不需要实现url_of(...)
函数,因为request
对象拥有所有路由信息。
request.url_for
没有充分测试,url_for
确实可以通过函数名获取应用的所有url。但是如果在多个 APIRouter 下定义了相同的函数名,url_for
会返回第一个匹配的函数名(按照 include_router 的顺序)。 url_of
提供了一种在函数名冲突时获取带有 APIRouter 标签的正确 url 的方法。答案已更新。谢谢@JPG【参考方案5】:
如果您需要在模板中解析 URL,Starlette (so FastAPI) 定义了
url_for()
函数在默认 Jinja 环境中使用 request
上下文实例和url_for
方法:
https://github.com/encode/starlette/blob/master/starlette/templating.py#L63
在模板中使用它: url_for('hello_world_number', number=42)
。
【讨论】:
没有提到url_for
是在模板上下文中导出的。以上是关于FastAPI:从视图名称(路由名称)中检索 URL的主要内容,如果未能解决你的问题,请参考以下文章