我在尝试将值从 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"
任何人都可以帮助我理解我做错了什么?
谢谢。 狮子座
您可以在后端将类别名称定义为
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})
模板/index.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>
要实现相同的结果,即通过
Form
请求向后端提交 POST
数据,使用 JavaScript 的 Fetch API 代替,您可以在前端使用以下模板(请参阅本答案后面的选项 4)对于类似的方法):
模板/index.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>
<label for="cat_name">Enter a category name to disable:</label><br>
<input type="text" id="cat_name" name="cat_name">
<input type="button" value="Submit" onclick="send()">
<p id="resp"></p>
<script>
function send() {
var resp = document.getElementById("resp");
var formData = new FormData();
const cat_name = document.getElementById("cat_name").value;
formData.append("cat_name", cat_name);
fetch('/disable', {
method: 'POST',
body: formData,
})
.then(response => response.json())
.then(data => {
resp.innerHTML = JSON.stringify(data); // data is a JSON object
})
.catch(error => {
console.error(error);
});
}
</script>
</body>
</html>
您可以在端点中将类别名称声明为查询参数,并在前端使用与问题中演示的类似方法将
<form>
<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})
模板/index.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 端点,并将上面的模板替换为下面的模板(类似于 thisanswer 的方法 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>
您仍然可以将其定义为路径参数(如您的问题所示),并在前端使用 JavaScript 来修改 HTML
action
的 <form>
属性,通过传递 <form>
<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})
模板/index.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>
如果您想在点击 HTML
submit
的 <form>
按钮时阻止页面重新加载/重定向,而是在同一页面中获取结果,您可以使用 Fetch API,如前面简要描述的,在命令发出异步 HTTP 请求,类似于 thisanswer,以及 thisanswer 和 thisanswer。此外,可以调用 Event.preventDefault()
函数,如这个答案中所述,以防止提交 HTML 时的默认操作<form>
。
下面的例子是基于之前的选项(即选项3);但是,如果您希望阻止浏览器在
<form>
提交时刷新页面,下面相同的方法(即发出异步 HTTP 请求)也可以用于前面演示的选项 1 和 2。请注意,此选项将 <input>
值作为路径参数提交到后端,但如果您想将其作为 Form
参数提交,请查看前面选项 1 中给出的相关代码。
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})
模板/index.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。
请在下面找到我的新代码:
== FastAPI ==
# 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 ==
<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>