FastAPI对接Next.js请求端点报500及序列化错误排查
FastAPI 对接 Next.js 数据请求异常排查
相关代码
后端FastAPI路由原始代码
@router.get('/public',response_class=HTMLResponse) def job_list_view(request:Request): async def get_todos() -> dict: todos = [ { "id": "1", "item": "Read a book." }, { "id": "2", "item": "Cycle around town." } ] return { "data": todos }
前端Next.js请求原始代码
export async function getServerSideProps() { const jobs = await fetch('http://localhost:8000/public/') console.log(jobs) const res = jobs.json() return { props: { jobs: res }, } }
异常表现
- Postman直接访问接口返回
Internal Server Error - 前端抛出两类报错:
- FetchError:
http://localhost:8000/public/响应体为无效JSON,位置0处存在意外tokenI - 序列化错误:
getServerSideProps在/路由返回的.jobs为[object Promise]对象,无法序列化为JSON,仅支持返回可JSON序列化的数据类型
- FetchError:
前端报错详情
error - unhandledRejection: FetchError: invalid json response body at http://localhost:8000/public/ reason: Unexpected token I in JSON at position 0 error - Error: Error serializing `.jobs` returned from `getServerSideProps` in "/". Reason: `object` ("[object Promise]") cannot be serialized as JSON. Please only return JSON serializable data types.
后端报错详情
FastAPI控制台抛出AttributeError,核心错误为'dict' object has no attribute 'encode',完整堆栈如下:
INFO: 127.0.0.1:63133 - "GET /public/ HTTP/1.1" 500 Internal Server Error ERROR: Exception in ASGI application Traceback (most recent call last): File "D:\TEMP\job_search - SQL\venv\lib\site-packages\uvicorn\protocols\http\h11_impl.py", line 366, in run_asgi result = await app(self.scope, self.receive, self.send) File "D:\TEMP\job_search - SQL\venv\lib\site-packages\uvicorn\middleware\proxy_headers.py", line 75, in __call__ return await self.app(scope, receive, send) File "D:\TEMP\job_search - SQL\venv\lib\site-packages\fastapi\applications.py", line 269, in __call__ await super().__call__(scope, receive, send) File "D:\TEMP\job_search - SQL\venv\lib\site-packages\starlette\applications.py", line 124, in __call__ await self.middleware_stack(scope, receive, send) File "D:\TEMP\job_search - SQL\venv\lib\site-packages\starlette\middleware\errors.py", line 184, in __call__ raise exc File "D:\TEMP\job_search - SQL\venv\lib\site-packages\starlette\middleware\errors.py", line 162, in __call__ await self.app(scope, receive, _send) File "D:\TEMP\job_search - SQL\venv\lib\site-packages\starlette\middleware\cors.py", line 84, in __call__ await self.app(scope, receive, send) File "D:\TEMP\job_search - SQL\venv\lib\site-packages\starlette\middleware\authentication.py", line 48, in __call__ await self.app(scope, receive, send) File "D:\TEMP\job_search - SQL\venv\lib\site-packages\starlette\exceptions.py", line 93, in __call__ raise exc File "D:\TEMP\job_search - SQL\venv\lib\site-packages\starlette\exceptions.py", line 82, in __call__ await self.app(scope, receive, sender) File "D:\TEMP\job_search - SQL\venv\lib\site-packages\fastapi\middleware\asyncexitstack.py", line 21, in __call__ raise e File "D:\TEMP\job_search - SQL\venv\lib\site-packages\fastapi\middleware\asyncexitstack.py", line 18, in __call__ await self.app(scope, receive, send) File "D:\TEMP\job_search - SQL\venv\lib\site-packages\starlette\routing.py", line 670, in __call__ await route.handle(scope, receive, send) File "D:\TEMP\job_search - SQL\venv\lib\site-packages\starlette\routing.py", line 266, in handle await self.app(scope, receive, send) File "D:\TEMP\job_search - SQL\venv\lib\site-packages\starlette\routing.py", line 65, in app response = await func(request) File "D:\TEMP\job_search - SQL\venv\lib\site-packages\fastapi\routing.py", line 251, in app response = actual_response_class(response_data, **response_args) File "D:\TEMP\job_search - SQL\venv\lib\site-packages\starlette\responses.py", line 49, in __init__ self.body = self.render(content) File "D:\TEMP\job_search - SQL\venv\lib\site-packages\starlette\responses.py", line 57, in render return content.encode(self.charset) AttributeError: 'dict' object has no attribute 'encode'
根因分析
- 后端响应类型配置错误:接口指定
response_class=HTMLResponse,该响应类会尝试对返回内容调用字符串的.encode()方法编码,但路由实际返回的是dict对象,没有encode方法,直接触发500错误。FastAPI默认返回HTML格式的500错误页,页面开头为Internal Server Error,首字符为I,这就是前端解析JSON时提示Unexpected token I的来源。 - 后端逻辑冗余无效:路由方法内部定义了异步函数
get_todos()但从未调用,即使修复响应类型问题,也无法返回预期的待办事项数据。 - 前端Promise未等待:
Response.json()是异步方法,返回Promise对象,原始代码没有加await等待解析完成,直接把Promise对象传给props,触发序列化错误。
修复方案
后端修复
- 接口返回JSON数据时无需指定
HTMLResponse,FastAPI默认会自动将dict、列表等数据结构序列化为JSON格式返回,也可显式指定response_class=JSONResponse - 移除路由内部未调用的嵌套异步函数,直接在路由方法内实现数据返回逻辑
修复后代码:
from fastapi import APIRouter, Request router = APIRouter() @router.get('/public') async def job_list_view(request: Request): todos = [ {"id": "1", "item": "Read a book."}, {"id": "2", "item": "Cycle around town."} ] return {"data": todos}
前端修复
- 调用
res.json()时添加await关键字,等待JSON解析完成后再赋值给变量
修复后代码:
export async function getServerSideProps() { const res = await fetch('http://localhost:8000/public/') const jobs = await res.json() return { props: { jobs }, } }
内容的提问来源于stack exchange,提问作者Julie An
相关产品推荐
相关产品推荐

