在FastAPI等现代Web框架中,处理用户表单提交并跳转至结果页面,是一个常见但易出错的需求。许多开发者会遇到这样的困惑:为何在GET路由中直接调用处理POST数据的异步函数,会引发AttributeError: 'Body' object has no attribute 'casefold'这
FastAPI POST数据传递与页面跳转指南
时间:2026-05-25
浏览:496
文章目录
在FastAPI等现代Web框架中,处理用户表单提交并跳转至结果页面,是一个常见但易出错的需求。许多开发者会遇到这样的困惑:为何在GET路由中直接调用处理POST数据的异步函数,会引发AttributeError: 'Body' object has no attribute 'casefold'这类错误?

问题的根源在于对HTTP协议与FastAPI请求生命周期的理解。@app.post与@app.get是两个独立的处理单元。POST请求负责接收、校验和处理请求体(Body)数据,而GET请求通常用于获取资源或渲染页面,其本身不包含请求体。若在GET路由中调用一个期望prompt = Body()参数的函数,FastAPI无法提供请求体,传入参数将变为None或无效对象,后续调用.casefold()方法时便会出错。
正确的解决思路在于职责分离:
- POST接口专注于接收数据、执行业务逻辑并返回结果。
- 前端在获取结果后,负责数据的传递与页面跳转。
- GET页面仅负责展示已准备好的数据。
推荐方案:使用 sessionStorage 暂存与前端跳转
在多种跨请求数据传递方案中,sessionStorage因其轻量、安全且生命周期与浏览器标签页绑定的特性,成为解决此类问题的首选。它无需服务端维护状态,完美契合“提交-跳转-展示”的单次交互场景。
1. 前端逻辑改造:从提交到跳转
首先,需阻止表单或链接的默认跳转行为,改用JavaScript控制流程。
核心JavaScript代码如下:
const el = document.getElementById('btn');
el.addEventListener('click', async function () {
const promptValue = document.getElementById("prompt").value.trim();
if (!promptValue) return;
try {
// 1. 向POST接口发送搜索请求
const response = await fetch("/get_books", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ prompt: promptValue }) // 键名需与后端参数定义匹配
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const bookList = await response.json();
// 2. 关键步骤:将结果存入sessionStorage
sessionStorage.setItem('searchResult', JSON.stringify(bookList));
sessionStorage.setItem('searchPrompt', promptValue);
// 3. 执行页面跳转(URL不携带参数)
window.location.href = '/books';
} catch (err) {
console.error("搜索失败:", err);
// 可在此添加用户友好的错误提示
}
});
2. 优化后端FastAPI接口
后端的POST接口需明确声明参数,并专注于业务处理。
from fastapi import APIRouter, Body, Request
from fastapi.responses import HTMLResponse
@app.post('/get_books')
async def get_books(prompt: str = Body(..., embed=True)): # 明确prompt为必需字符串参数
print(f"Received prompt: '{prompt}'")
book_list = []
for book in BOOKS:
title = book.get('title', '').casefold()
author = book.get('author', '').casefold()
category = book.get('category', '').casefold()
if prompt.casefold() in (title, author, category):
book_list.append(book)
return book_list # FastAPI会自动将列表序列化为JSON响应
注意:
Body(..., embed=True)表示期望接收如{"prompt": "关键词"}的JSON对象。若前端发送的键名不同,例如{info: "xxx"},则后端参数应调整为info: str = Body(..., alias="info")。
3. 更新GET路由:渲染页面而非处理数据
sessionStorage是浏览器端概念,服务端无法直接访问。因此,/books的GET路由任务变得纯粹:返回一个空的HTML模板,由前端自行填充数据。
@app.get('/books', response_class=HTMLResponse)
async def books(request: Request):
# 重要:此处不再调用get_books()函数!
return templates.TemplateResponse("books.html", {"request": request})
数据渲染工作移交至books.html模板的初始化脚本:
实用工具推荐
绿化、压缩、噪点和虚拟水印,自由调节做旧程度。
选择画质和输出格式,预览压缩前后的大小。