【问题标题】:Calling FastAPI Post Request from a HTML form action in different folder not working从不同文件夹中的 HTML 表单操作调用 FastAPI 发布请求不起作用
【发布时间】:2021-11-15 02:16:41
【问题描述】:

我想从公式提交中执行 fastApi post 方法,我在网上搜索了有关从不同文件夹调用的所有内容,但到目前为止一无所获。我的项目结构如下:

├── index.html
├── assets
└── backend
    ├── main.py
    └── ...

main.py 发布方法:

@app.post(path="/uploadImages")
async def extract_data_from_images(images: List[UploadFile] = File(...)):
    return images[0].filename + " Hello World"

在 index.html 中,表单应该触发 main.py 中定义的 post 方法。但它什么也没做。

<form id="uploadform" action="/uploadImages" method="post" enctype="multipart/form-data">
    <input type="file" id="images" name="images" multiple data-max="2" accept="image/jpg, image/jpeg, image/png" >
    <input type="submit">
</form>
<textarea>Here should be data returned from FastAPI response</textarea>

我还尝试将操作路径设置为“./backend/main/uploadImages”、“./backend/uploadImages”、“./backend/main.py/uploadImages”,它们都不起作用。

通常使用 post 方法中给出的示例代码,浏览器必须打开一个带有显示文件名 +“Hello World”的 json 原始字符串的新站点,或者不是?如果没有,我怎样才能让它在给定 index.html 的 textarea 中返回?

来自 Fastapi 文档的测试显示字符串,因此它不是服务器端错误。

提前致谢

【问题讨论】:

  • 您必须使用 Javascript 从 API 请求中检索内容(使用 fetchXMLHttpRequest,然后将返回的内容插入到 textarea。您的端点 (@987654327 @) 根据您的 @app.post 装饰器是正确的。
  • 我只是通过硬编码网络服务器的绝对路径来工作,但很明显这效率不高。至于javascript fetch:所以提交应该在那里完成而不是使用表单?
  • 啊,如果您只是打开 HTML 页面,那将无法正常工作。相反,您应该在 FastAPI 中使用TemplateResponse 来传递 HTML 内容——这样它将从与接受客户端请求的主机相同的主机传递。如果您想将这些托管在不同的服务器上 - 是的,在这种情况下,您必须提供整个 URL 到帖子应该发生的位置(或将其提供给获取 - 但在这种情况下,您还需要 CORS 支持才能让Javascript 发出跨域请求)。
  • 如果您只是希望输出显示在此处,则 Javascript 部分是必需的 - 但如果您使用模板,则可以将数据插入模板并直接返回(通常使用{{ varname }} in Jinja2).

标签: python html forms fastapi


【解决方案1】:

您可以添加另一个端点来为您的 HTML 页面提供服务,例如:

ma​​in.py:

import uvicorn

from datetime import datetime
from typing import List

from fastapi import FastAPI, File, Request, UploadFile
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates

app = FastAPI()
templates = Jinja2Templates(directory=".") # Change this path accordingly


@app.get("/", response_class=HTMLResponse)
def index(request: Request):
    return templates.TemplateResponse("index.html", {"request": request})


@app.post(path="/uploadImages")
async def extract_data_from_images(images: List[UploadFile] = File(...)):
    return f"Name: {images[0].filename}, now: {datetime.now()}"


if __name__ == "__main__":
    uvicorn.run("main:app", host="localhost", port=8000, reload=True)

然后您可以从 JavaScript 发出请求:

index.html:

<!doctype html>

<html lang="en">
<head>
  <meta charset="utf-8">

  <title>Test</title>
</head>

<body>
  <form id="uploadform" action="/uploadImages" method="post" enctype="multipart/form-data">
    <input type="file" id="images" name="images" multiple data-max="2" accept="image/jpg, image/jpeg, image/png" >
    <input type="submit" id="submit">
  </form>

  <textarea id="box">Here should be data returned from FastAPI response</textarea>

  <script>
    const box = document.getElementById("box");

    uploadform.onsubmit = async (e) => {
      e.preventDefault();

      let res = await fetch("/uploadImages", {
        method: "POST",
        body: new FormData(uploadform),
      });

      if (res.ok) {
        let result = await res.text();
        box.innerHTML = result;
      } else {
        box.innerHTML = `Response error: ${res.status}`;
      };
    };
  </script>
</body>
</html>

【讨论】:

  • 是的,谢谢,它应该可以工作,但是由于跨域资源共享沿“未捕获(承诺)类型错误:尝试获取资源时出现网络错误”并允许 Firefox 权限,匿名 onsubmit 函数不会触发也无济于事
  • 在我的示例中,两个端点都来自同一个域,因此情况并非如此。你用什么 URL 来测试这个?
  • 是的,就像我说的我的本地 ip http://192.168.178.99:8000/uploadImages 用于表单和获取
  • 使用我的示例时,只需转到 http://localhost:8000/,这将显示 HTML 表单,该表单将使用后端 API 的相对路径,否则请检查 FastAPI docs 的 CORS。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多