【问题标题】:How to serve static files in FastAPI如何在 FastAPI 中提供静态文件
【发布时间】:2020-10-08 20:17:13
【问题描述】:

我正在尝试提供 package_docs 目录中的静态文件。当我在浏览器中打开时:

http://127.0.0.1:8001/packages/docs/index.html,页面正在运行。

但我想打开页面:http://127.0.0.1:8001/packages/docs/

没有源文件。并且输出是404 Not Found

app.mount("/packages/docs", 
    StaticFiles(directory=pkg_resources.resource_filename(__name__, 'package_docs')
    ), 
    name="package_docs")

@app.get("/packages/docs/.*", include_in_schema=False)
def root():
    return HTMLResponse(pkg_resources.resource_string(__name__, "package_docs/index.html"))


app.include_router(static.router)
app.include_router(jamcam.router, prefix="/api/v1/cams", tags=["jamcam"])

如何更改我的代码?任何建议都会有所帮助。提前谢谢你。

【问题讨论】:

  • @justin-malloy 发布的答案似乎是正确的,您只需在 StaticFiles() 调用中包含 html=True 即可。

标签: python-3.x routes static-files fastapi pkg-resources


【解决方案1】:

你需要使用FastAPI的TemplateResponse(其实是Starlette的):

from fastapi import Request
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates

app.mount("/static", StaticFiles(directory="static"), name="static")

templates = Jinja2Templates(directory="package_docs")

@app.get("/items/{id}")
async def example(request: Request):
    return templates.TemplateResponse("index.html", {"request": request})

Request 作为 Jinja2 上下文中键值对的一部分。因此,您还必须将其声明为查询参数。并且你必须指定一个你想用 Jinja ("your.html", {"request": request}) 渲染它的 html 文件

还可以直接返回 HTMLResponse,您可以使用 HTMLResponse from fastapi.responses

from fastapi.responses import HTMLResponse

@app.get("/items/", response_class=HTMLResponse)
async def read_items():
    return """
    <html>
        <head>
            <title></title>
        </head>
        <body>
        </body>
    </html>
    """

您可以从FastAPI Custom Responses 阅读有关自定义响应的更多信息

【讨论】:

  • 这个解决方案可以很好地与 javascript 框架(例如 svelte)配合使用,因为您可以使用 jinja 渲染 index.html,如 bundle.js?{{bundle_version}} 页面将是最新的
  • 最后一个示例中缺少右引号 """ 最后一行。
  • 如果您使用前端模板,这将中断,例如AngularJs 仅供参考。
【解决方案2】:

如果app.mount 不是一个选项, 您始终可以手动读取文件并使用其内容进行响应...

例如,如果您的静态文件在/site 中,那么:

from os.path import isfile
from fastapi import Response
from mimetypes import guess_type


@app.get("/site/{filename}")
async def get_site(filename):
    filename = './site/' + filename

    if not isfile(filename):
        return Response(status_code=404)

    with open(filename) as f:
        content = f.read()

    content_type, _ = guess_type(filename)
    return Response(content, media_type=content_type)


@app.get("/site/")
async def get_site_default_filename():
    return await get_site('index.html')

【讨论】:

    【解决方案3】:

    Starlette 中有一个 html 选项可以在 FastAPI 中使用。 Starlette Documentation

    这会让你有这样的东西:

    app.mount("/site", StaticFiles(directory="site", html = True), name="site")
    

    这会将 /site 解析为 /site/index.html,将 /site/foo/ 解析为 /site/foo/index.html 等。

    如果您想以不使用“directory = /foo”处理的方式更改文件夹名称,其他答案可以帮助您重定向,但如果您只想加载关联的 .html 文件,这是最简单的选项。

    【讨论】:

    • 谢谢!这应该是选择的答案,FastAPI 确实应该将其包含在他们的文档中。我将 html=True 放在该 StaticFiles() 函数之外并出现错误。
    【解决方案4】:

    来自docs

    第一个“/static”指的是这个“子应用程序”将被“挂载”到的子路径。所以,任何以“/static”开头的路径都会被它处理。

    这意味着您将目录挂载到 http://127.0.0.1:8001/packages/docs/ ,但是您需要在 URL 中指定一个文件,或者像您一样添加一个处理程序。但问题是,由于您首先安装了路径,因此它不会考虑包含部分路径的以下路径。

    一种可能是先指定http://127.0.0.1:8001/packages/docs/ 的路径,以便由fastapi 处理,然后挂载文件夹,提供静态文件。

    另外,我会将请求http://127.0.0.1:8001/packages/docs/ 的用户重定向到http://127.0.0.1:8001/packages/docs/index.html

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-12-03
      • 1970-01-01
      • 2021-12-09
      • 2015-11-03
      相关资源
      最近更新 更多