如何使用 <input> FastAPI 和 Jinja2 模板提交 HTML 表单值?



我在尝试将值从 HTML 表单<input>元素传递到表单的action属性并将其发送到 FastAPI 服务器时遇到了以下问题。

以下是 Jinja2 (HTML) 模板的加载方式:

# Test TEMPLATES
@app.get("/test",response_class=HTMLResponse)
async def read_item(request: Request):
return templates.TemplateResponse("index.html", {"request": request})

我的 HTML 表单:

<form action="/disableSubCategory/{{subCatName}}">
<label for="subCatName">SubCategory:</label><br>
<input type="text" id="subCatName" name="subCatName" value=""><br>
<input type="submit" value="Disable">
</form>

要在表单操作中调用的 FastAPI 端点:

# Disable SubCategory
@app.get("/disableSubCategory/{subCatName}")
async def deactivateSubCategory(subCatName: str):
disableSubCategory(subCatName)
return {"message": "SubCategory [" + subCatName + "] Disabled"}

我得到的错误:

"GET /disableSubCategory/?subCatName=Barber HTTP/1.1" 404 Not Found

我试图实现的是以下 FastAPI 调用:

/disableSubCategory/{subCatName} ==> "/disableSubCategory/Barber"

有谁能帮助我了解我做错了什么?

谢谢。 狮子座

选项 1

您可以在后端将类别名称定义为Form参数,并使用 HTML<form>从前端提交 POST 请求,如本答案的方法 1 中所述。

app.py

from fastapi import FastAPI, Form, Request
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
app = FastAPI()
templates = Jinja2Templates(directory='templates')
@app.post('/disable')
def disable_cat(cat_name: str = Form(...)):
return f'{cat_name} category has been disabled.'
@app.get('/', response_class=HTMLResponse)
def main(request: Request):
return templates.TemplateResponse('index.html', {'request': request})

模板/索引.html

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<h1>Disable a category</h1>
<form method="post" action="/disable">
<label for="cat_name">Enter a category name to disable:</label><br>
<input type="text" id="cat_name" name="cat_name">
<input class="submit" type="submit" value="Submit">
</form>
</body>
</html>

选项 2

可以在终结点中将类别名称声明为查询参数,并在前端使用与问题中演示的方法类似的方法,将表单<input>元素中的值转换为查询参数,然后将其添加到 URL 的查询字符串(在action属性中)。

请注意,与上述相反,下面使用 GET 请求(在这种情况下,您需要在后端使用@app.get(),在前端使用<form method="get" ...,这无论如何都是默认方法)。请注意,大多数浏览器会缓存GET请求(即保存在浏览器的历史记录中),因此与POST相比,它们的安全性较低,因为发送的数据是URL的一部分,并且对任何有权访问该设备的人可见。因此,在发送密码或其他敏感信息时不应使用 GET 方法。

app.py

from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
app = FastAPI()
templates = Jinja2Templates(directory='templates')
@app.get('/disable')
def disable_cat(cat_name: str):
return f'{cat_name} category has been disabled.'
@app.get('/', response_class=HTMLResponse)
def main(request: Request):
return templates.TemplateResponse('index.html', {'request': request})

模板/索引.html

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<h1>Disable a category</h1>
<form method="get" id="myForm" action='/disable{{ cat_name }}'>
<label for="cat_name">Enter a category name to disable:</label><br>
<input type="text" id="cat_name" name="cat_name">
<input class="submit" type="submit" value="Submit">
</form>
</body>
</html>

如果您想使用 POST 请求(它比 GET 安全一点,因为参数不存储在浏览器的历史记录中,并且在更新服务器上的内容/状态时更有意义,与请求(不修改)数据时应使用的 GET 相比——您可以使用@app.post()定义 FastAPI 端点,并将上述模板替换为以下内容(类似于本答案的方法 2), 在将表单数据转换为查询参数后,使用 POST 方法提交表单:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script>
document.addEventListener('DOMContentLoaded', (event) => {
document.getElementById("myForm").addEventListener("submit", function (e) {
var myForm = document.getElementById('myForm');
var qs = new URLSearchParams(new FormData(myForm)).toString();
myForm.action = '/disable?' + qs;
});
});
</script>
</head>
<body>
<h1>Disable a category</h1>
<form method="post" id="myForm">
<label for="cat_name">Enter a category name to disable:</label><br>
<input type="text" id="cat_name" name="cat_name">
<input class="submit" type="submit" value="Submit">
</form>
</body>
</html>

选项 3

您仍然可以将其定义为路径参数,并在前端中使用 JavaScript 来修改<form>action属性,方法是将表单<input>元素的值作为路径参数传递给 URL,类似于前面描述的内容。

app.py

from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
app = FastAPI()
templates = Jinja2Templates(directory='templates')
@app.post('/disable/{name}')
def disable_cat(name: str):
return f'{name} category has been disabled.'
@app.get('/', response_class=HTMLResponse)
def main(request: Request):
return templates.TemplateResponse('index.html', {'request': request})

模板/索引.html

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script>
document.addEventListener('DOMContentLoaded', (event) => {
document.getElementById("myForm").addEventListener("submit", function (e) {
var myForm = document.getElementById('myForm');
var catName = document.getElementById('catName').value;
myForm.action = '/disable/' + catName;
});
});
</script>
</head>
<body>
<h1>Disable a category</h1>
<form method="post" id="myForm">
<label for="catName">Enter a category name to disable:</label><br>
<input type="text" id="catName" name="catName">
<input class="submit" type="submit" value="Submit">
</form>
</body>
</html>

选项 4

如果你想防止页面在点击HTML<form>submit按钮时重新加载/重定向,而是在同一页面中获取结果,你可以使用Fetch API,一个JavaScript接口/库,发出异步HTTP请求,类似于这个答案,以及这个答案和这个答案。此外,可以调用Event.preventDefault()函数(如本答案中所述)来防止默认操作。下面的示例基于上一个选项(即选项 3);但是,下面的相同方法(即,发出异步HTTP请求)也可以用于前面演示的选项1和2,如果您想阻止浏览器在提交时刷新页面<form>

app.py

from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
app = FastAPI()
templates = Jinja2Templates(directory='templates')
@app.post('/disable/{name}')
def disable_cat(name: str):
return f'{name} category has been disabled.'
@app.get('/', response_class=HTMLResponse)
def main(request: Request):
return templates.TemplateResponse('index.html', {'request': request})

模板/索引.html

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script>
document.addEventListener('DOMContentLoaded', (event) => {
document.getElementById("myForm").addEventListener("submit", function (e) {
e.preventDefault() // Cancel the default action
var catName = document.getElementById('catName').value;
fetch('/disable/' + catName, {
method: 'POST',
})
.then(resp => resp.text()) // or, resp.json(), etc.
.then(data => {
document.getElementById("response").innerHTML = data;
})
.catch(error => {
console.error(error);
});
});
});
</script>
</head>
<body>
<h1>Disable a category</h1>
<form id="myForm">
<label for="catName">Enter a category name to disable:</label><br>
<input type="text" id="catName" name="catName">
<input class="submit" type="submit" value="Submit">
</form>
<div id="response"></div>
</body>
</html>

只是为了向您提供反馈并跟踪我实施的解决方案。

正如@Chris提到的,我转到了建议的解决方案3。

请在下面找到我的新代码:

== 快速接口 ==

# Test TEMPLATES
@app.get("/test",response_class=HTMLResponse)
async def read_item(request: Request):
return templates.TemplateResponse("index.html", {"request": request})
# Disable SubCategory
@app.post("/disableSubCategory/{subCatName}")
async def deactivateSubCategory(subCatName: str):
disableSubCategory(subCatName)
return {"message": "Sub-Category [" + subCatName + "] Disabled"}
# Enable SubCategory
@app.post("/enableSubCategory/{subCatName}")
async def activateSubCategory(subCatName: str):
enableSubCategory(subCatName)
return {"message": "Sub-Category [" + subCatName + "] Enabled"}

== 网页 ==

<html>
<head>
<title>Item Details</title>
<link href="{{ url_for('static', path='/styles.css') }}" rel="stylesheet">
<script>
document.addEventListener('DOMContentLoaded', (event) => {
document.getElementById("disableSubCategory").addEventListener("submit", function (e) {
var myForm = document.getElementById('disableSubCategory');
var disableSubCatName = document.getElementById('id_disableSubCategory').value;
myForm.action = '/disableSubCategory/' + disableSubCatName;
});
});
</script>
<script>
document.addEventListener('DOMContentLoaded', (event) => {
document.getElementById("enableSubCategory").addEventListener("submit", function (e) {
var myForm2 = document.getElementById('enableSubCategory');
var enableSubCatName = document.getElementById('id_enableSubCategory').value;
myForm2.action = '/enableSubCategory/' + enableSubCatName;
});
});
</script>
</head>
<body>
<form id="disableSubCategory" enctype="multipart/form-data" method="post">
<label for="subCatName">SubCategory:</label><br>
<input type="text" id="id_disableSubCategory" value=""><br>
<input type="submit" value="Disable" id="disable">
</form>
<form id="enableSubCategory" enctype="multipart/form-data" method="post">
<label for="subCatName">SubCategory:</label><br>
<input type="text" id="id_enableSubCategory" value=""><br>
<input type="submit" value="Enable" id="enable">
</form>
</body>
</html>

最新更新