Python-2023-11 Web开发之FastAPI实战续¶
内容梗概¶
这是 FastAPI 实战的第二课,目标是为上一课写好的图片格式转换后端补一个前端页面,打通"上传—转换—下载"的完整服务。老师先演示了"遇到不会的任务如何搜索"(upload file pure javascript),然后搭建 backend/frontend 的项目结构,用原生 HTML + JavaScript 写了一个文件上传页面,逐行讲解了 FormData、fetch、async/await 等概念。调试过程中刻意展示了两个典型坑:FastAPI 路径末尾斜杠省略导致 307 重定向、response.json() 漏写 await 导致 undefined。最后引出并解决了前后端分离开发中必然遇到的 **CORS 跨域**问题,用 CORSMiddleware 放行后整个服务跑通。
知识点详解¶
项目结构调整(约 00:00–03:10)¶
- 上节成果:FastAPI 后端可通过命令行启动,
/docs提供测试接口,但缺少上传文件的前端和下载入口——"单独有后端是没有用的"。 - 现代网站常见仓库结构:一个项目分两个文件夹,把原来的后端文件夹改名为
backend,新建frontend放前端代码。 - 前端文件夹中新建
index.html:任何 Web 服务器(nginx/Apache/Tomcat)加载网站时默认到根目录找 index 文件作为首页(index.html / index.php 等,是约定俗成)。
搜索技巧(约 01:03–02:30)¶
- 完成任务的第一步是把需求翻译成搜索关键词。不会框架就搜原生的:
upload file pure javascript;会 Vue/React 就按框架搜。 - 自己实操时要逐个打开结果、比较挑选最优方案;老师课上直接跳到备课时选定的代码段。
HTML 页面基础结构(约 03:30–04:50)¶
- 一个 HTML 页面有一个
html根节点,内含head和body两个子节点;是 XML 式的开始/结束标签结构。 - 顶部
<!DOCTYPE ...>不是注释而是说明:声明本文档是 HTML 格式文档。 head里放基本信息(如title,显示在浏览器标签页上),不直接参与页面显示;页面上看到的内容基本都在body里。- 本页 body 里只有两个元素:选择文件的
input(id/name 为fileupload)和"上传"按钮;绿色的是注释。
JavaScript 上传逻辑逐行拆解(约 04:53–13:30)¶
script 标签内只有一个函数 uploadFile:
- async 异步函数(约 05:22):函数前
async表示异步,不会像排队一样阻塞整个网页,函数在后台"偷偷地跑",页面仍可正常操作;与之相对,await表示**等待/阻塞**在该语句直到操作完成。 - FormData(约 09:40):表单数据对象。登录页的表单(form)包含 username、password 等字段;在 JS 里用
new FormData()创建空表单数据对象,再append字段。 formData.append('file', fileupload.files[0]):字段名为file(要与后端接口参数名对应),内容取页面上 id 为fileupload的 input 元素选中的第 0 号文件(单文件控件取 0 号)。- 文件的二进制数据会经 base64 编码后放进 HTTP 请求(呼应之前学过的 base64——HTTP 不能直接传二进制内容)。
- fetch 发请求(约 12:29):
await fetch(url, {method: 'POST', body: formData})——向指定 URL 发 POST 请求,body 是表单数据;执行完说明上传成功,后面alert弹窗提示。
对接后端地址与 307 重定向坑(约 13:36–22:00)¶
- 复制的示例里请求地址是
upload.php,要改成自己的后端:let url = 'http://127.0.0.1:8000'(localhost 是本地环回域名),路径是/uploadfile/;JS 里字符串拼接和 Python 一样用加号,fetch(url + path, ...)。 - FastAPI 路径末尾斜杠不可省略(约 17:00):省略后请求会得到
307 Temporary Redirect,服务器自动重定向到带斜杠的地址再 200 OK——功能上能成功,但一个请求变两个,不合理,应一开始就写对。 - 善用开发者工具(约 18:28):F12 打开,最常用的两个标签页:
- 控制台 Console:JavaScript 运行报错都显示在这里;
- 网络 Network:实时列出与服务器往来的所有 HTTP 请求,可查看负载、请求/响应标头。
- 演示插曲:上传失败先怀疑跨域,结果是忘了启动后端——排错时记得确认服务在跑。
- 另一个小坑:重复上传同名文件后端报 422(服务器上已存在该文件不能覆盖),删掉旧文件即可。
接收响应并生成下载链接(约 22:00–30:40)¶
- 后端上传接口返回的 JSON 里带有转换结果的
filename(下载地址),前端要接收并显示。 let response = await fetch(...)存下响应,取 JSON:response.json(),再用console.log(相当于 JS 的 print,输出到控制台)查看file.filename。- await 排错(约 27:27):直接打印得到
undefined——因为response.json()也是异步操作,没加await时响应还没解析完就往下执行了;改成let file = await response.json()后正常。理解"代码跑得太快,response 还没回来"的阻塞逻辑。 - 动态创建下载链接(约 28:37):
let a = document.createElement('a')创建超链接元素;a.href = url + file.filename指向转换结果地址;- 把链接文字设为"下载",再
append到 body 末尾。 - 上传成功后页面多出"下载"链接;因为结果是图片,浏览器直接显示而非下载,右键另存为即可得到 PNG。至此"在线图片格式转换服务"一条龙完成。
CORS 跨域问题(约 31:13–39:20)¶
- 老师还原后端到上节课状态(注释掉预先加好的中间件),演示前端报一堆错——引出 CORS(Cross-Origin Resource Sharing,跨源/跨域资源共享)。
- 什么是跨域(约 32:48):浏览器当前访问的域名(如 CSDN 博客页)内的资源引用了另一个域名(如微博图床)的内容,就构成跨域。常见现象:博客图片显示"未经允许链接的外部资源"。
- 为什么有这个机制:图片/资源放在服务器上要付带宽成本,不能任由别的站点白嫖;同时也是一种安全机制。
- 实现原理(约 35:30):浏览器发请求时标头带
origin字段(本地文件打开时为null/none),告诉服务器请求来源;后端根据配置的跨域策略决定是否响应。默认不放行,于是报错。 - 解决方案(约 36:19):搜 "CORS FastAPI",官方文档给出最小方案——引入
CORSMiddleware,用app.add_middleware添加并配置参数: - 中间件 middleware:在处理请求之前预先对请求做处理的组件;
allow_origins是允许来源列表,开发演示时改为通配符*(谁都可以访问);生产环境应指定具体域名(如前端的域名),就不算跨域/被允许跨域。- 前后端分离架构下前端、后端在不同服务器/域名,几乎必然遇到跨域,这是开发中一定会踩的点。
示例与演示¶
- 完整前端
frontend/index.html:两个控件(选择文件、上传按钮)+ 一段 JS(uploadFile 异步函数)。 - 演示流程:启动后端 → 本地双击打开 index.html → F12 打开网络面板 → 选 test.jpg 点上传 → 观察 POST 请求与响应 → 页面出现"下载"链接 → 打开转换后的 PNG 并另存。
- 两次刻意翻车教学:
- 漏写路径末尾斜杠 → 网络面板出现 307 重定向 + 200 两条请求;
response.json()漏写await→ 控制台打印 undefined,加 await 后正常。- 注释/恢复 CORSMiddleware 对比演示跨域报错与修复。
重点与难点¶
- **async/await 的阻塞语义**是本课最易错点:fetch、
response.json()都是异步的,该等的地方必须 await,否则拿到 undefined。 - FastAPI 路由末尾斜杠:虽然会自动 307 重定向,但应直接写对,避免一次操作发两个请求。
- 前端字段名
'file'要与后端UploadFile参数名一致(表单字段名对应关系)。 - F12 开发者工具是 Web 开发的排错主力:Console 看 JS 报错,Network 看 HTTP 请求细节。
- CORS 是前后端分离开发的必经坑:理解 origin 标头与后端白名单机制,知道用 CORSMiddleware 解决;通配符
*仅限开发演示。
关联内容¶
- 直接承接上一课《Python-2023-10 Web开发之FastAPI实战》:本课为其后端补前端,构成完整作业(FastAPI 服务 + 上传下载前端)。
- base64 编码、HTTP 请求标头(header)、JSON 通信等概念与前面章节及后续《爬虫之豆瓣》(请求头、User-Agent、数据接口)互相呼应。
- 前端基础(HTML 结构、表单、CSS 选择器思想)与选修的 Web 课及后续爬虫课中"分析网页结构"相关。
- 本课完成的功能是 Web 开发作业的完整形态,之后课程进入爬虫与可视化部分。