5.4 做一个带后端服务的页面
目标:把「可点击的前端」和「在服务器上跑逻辑」组合起来,输出一个对外可用的小产品。
到目前为止,你做的都是纯前端项目或脚本。这一节将带你进入全栈开发,理解前后端分离架构,掌握 API 设计和部署。
典型案例:上传 Word/PPT 自动转换为 Markdown、批量生成 Banner、图片识别返回结果、文本转语音等。
🎯 成品要求
- 后端有清晰的接口文档(Swagger / Postman / README)
- 前端可以调用后端、展示结果并给出状态反馈
- 至少有一个部署在公网可访问的地址(Replit、Render、自建服务器等)
- 有基础的安全措施(鉴权、限流、日志)
🧰 准备工作
1. 后端运行环境
选择一个你熟悉的平台:
- Replit(推荐新手):在线编辑+一键部署,免费额度足够学习使用
- Render:支持 Docker,有免费套餐
- 自建服务器(进阶):AWS EC2、DigitalOcean、阿里云等
2. 参考项目
微软开源的 Markitdown:
- GitHub: https://github.com/microsoft/markitdown
- 功能:将 Office 文档、PDF、图片等转换为 Markdown
- 技术栈:Python
- 适合学习后端服务的基础架构
3. 前端技术栈
- Next.js:适合 SEO 友好的页面
- SvelteKit:轻量快速
- 纯 HTML + Tailwind:最简单,适合快速验证
🛠️ 实践步骤
步骤 1:部署基础服务
1.1 在 Replit 创建项目
- 访问 https://replit.com/
- 登录后点击 + Create Repl
- 选择 Import from GitHub
- 填入仓库地址:
https://github.com/microsoft/markitdown - 等待依赖自动安装
或者:选择 Python 模板,手动克隆仓库:
git clone https://github.com/microsoft/markitdown.git
cd markitdown
pip install -r requirements.txt1.2 测试基础功能
在 Replit 的 Shell 中运行:
# 测试命令行工具
python -m markitdown test.docx
# 应该输出 Markdown 格式的文本步骤 2:描述后端需求
现在需要把这个命令行工具封装成 Web API。
完整 Prompt 示例:
请基于 Markitdown 库创建一个 FastAPI 后端服务:
## 核心功能
1. 文件上传接口:支持上传 .docx、.pptx、.pdf、.xlsx、.jpg/.png 等文件
2. URL 转换接口:提供 URL,服务端下载后转换
3. 转换结果处理:
- 生成 Markdown 文件
- 存储到 /public/output 目录
- 返回可下载的链接(如 /download/{file_id}.md)
- 30 分钟后自动清理文件
## 技术架构
- 使用 FastAPI 框架
- 支持异步处理(大文件转换可能较慢)
- 文件 ID 使用 UUID 生成
- 添加 /docs 路由(自动生成的 Swagger 文档)
## 接口设计
POST /convert/file
- 请求:multipart/form-data,包含文件
- 响应:{file_id, download_url, status}
POST /convert/url
- 请求:{url: "https://example.com/file.pdf"}
- 响应:{file_id, download_url, status}
GET /download/{file_id}
- 返回 Markdown 文件
- 设置 Content-Disposition 头让浏览器下载
GET /health
- 健康检查接口
- 返回:{status: "ok", version: "1.0.0"}
## 错误处理
- 文件类型不支持:返回 400 Bad Request
- 文件过大(>10MB):返回 413 Payload Too Large
- 转换失败:返回 500 并记录详细错误日志
请生成完整的可运行代码,包括 main.py 和必要的配置文件。步骤 3:运行与联调
3.1 启动服务
在 Replit 中点击 Run 按钮,或在终端运行:
uvicorn main:app --host 0.0.0.0 --port 8000 --reload3.2 访问 Swagger 文档
打开浏览器,访问 Replit 提供的预览地址(如 https://your-repl.username.repl.co),然后访问:
https://your-repl.username.repl.co/docs你会看到自动生成的 API 文档界面。
3.3 测试接口
方式一:通过 Swagger UI
- 在
/docs页面找到POST /convert/file - 点击 Try it out
- 上传一个测试文件(如
test.docx) - 点击 Execute
- 查看响应,应该包含
download_url
方式二:通过 curl
# 上传文件
curl -X POST https://your-repl.username.repl.co/convert/file \
-F "[email protected]"
# 响应示例
{
"file_id": "a1b2c3d4-5678-90ab-cdef-1234567890ab",
"download_url": "/download/a1b2c3d4-5678-90ab-cdef-1234567890ab.md",
"status": "success"
}
# 下载结果
curl https://your-repl.username.repl.co/download/a1b2c3d4-5678-90ab-cdef-1234567890ab.md \
-o output.md方式三:通过 Python 脚本
import requests
# 上传文件
with open('test.docx', 'rb') as f:
response = requests.post(
'https://your-repl.username.repl.co/convert/file',
files={'file': f}
)
print(response.json())3.4 调试常见问题
| 问题 | 症状 | 解决方案 |
|---|---|---|
| ImportError | 提示找不到模块 | 检查 requirements.txt,运行 pip install -r requirements.txt |
| 权限错误 | 无法写入 /public/output | 创建目录 mkdir -p public/output,检查文件权限 |
| 转换失败 | 返回 500 错误 | 查看 Replit Console 的详细错误日志,检查文件格式是否支持 |
| CORS 错误 | 前端调用时报跨域 | 在 FastAPI 中添加 CORS 中间件(见下方代码) |
添加 CORS 支持:
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # 生产环境改为具体域名
allow_methods=["*"],
allow_headers=["*"],
)步骤 4:加上鉴权与监控
4.1 简单的 API Key 鉴权
from fastapi import Header, HTTPException
async def verify_api_key(x_api_key: str = Header(...)):
if x_api_key != os.getenv("API_KEY"):
raise HTTPException(status_code=401, detail="Invalid API Key")
return x_api_key
@app.post("/convert/file", dependencies=[Depends(verify_api_key)])
async def convert_file(...):
...在 Replit 的 Secrets 中添加环境变量:
API_KEY=your_secret_key_here前端调用时带上 Header:
fetch('/convert/file', {
headers: {
'X-API-Key': 'your_secret_key_here'
},
...
})4.2 添加请求日志
import logging
from datetime import datetime
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
@app.post("/convert/file")
async def convert_file(file: UploadFile, request: Request):
client_ip = request.client.host
logger.info(f"[{datetime.now()}] {client_ip} - Upload: {file.filename}")
# 转换逻辑
...
logger.info(f"[{datetime.now()}] {client_ip} - Success: {file_id}")4.3 限流(防止滥用)
使用 slowapi:
pip install slowapifrom slowapi import Limiter, _rate_limit_exceeded_handler
from slowapi.util import get_remote_address
limiter = Limiter(key_func=get_remote_address)
app.state.limiter = limiter
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
@app.post("/convert/file")
@limiter.limit("5/minute") # 每分钟最多 5 次请求
async def convert_file(request: Request, ...):
...步骤 5:构建前端控制台
5.1 在 Cursor 创建前端项目
npx create-next-app@latest markitdown-frontend
cd markitdown-frontend5.2 给 AI 描述前端需求
Prompt 示例:
请创建一个文件转换的前端界面,使用 Next.js + Tailwind CSS:
## 功能需求
1. 文件上传区域:
- 支持拖拽上传
- 显示文件名、大小、类型
- 限制文件大小(最大 10MB)
- 支持的格式:.docx, .pptx, .pdf, .xlsx, .jpg, .png
2. URL 输入区域:
- 输入框 + 提交按钮
- 验证 URL 格式
3. 进度显示:
- 上传进度条
- 转换状态(上传中/转换中/完成/失败)
- 预计剩余时间(可选)
4. 结果展示:
- 转换成功:显示下载按钮 + 预览区(显示 Markdown 内容)
- 转换失败:显示错误原因
5. 历史记录:
- 显示最近 10 次转换记录
- 支持重新下载
## 技术要求
- 使用 Next.js App Router
- Tailwind CSS 样式
- 调用 API:`https://your-backend.repl.co`
- API Key 存储在 .env.local
- 响应式设计,支持移动端
请生成完整的代码。5.3 配置环境变量
创建 .env.local:
NEXT_PUBLIC_API_URL=https://your-backend.repl.co
NEXT_PUBLIC_API_KEY=your_secret_key_here5.4 前后端联调
-
启动前端开发服务器:
pnpm run dev -
测试功能:
- 上传文件
- 输入 URL
- 查看转换结果
- 下载 Markdown
-
遇到问题时:
- 打开浏览器 DevTools → Network 标签
- 查看请求和响应
- 检查 CORS、鉴权、参数格式
步骤 6:部署
6.1 后端部署(Replit)
- 在 Replit 点击 Deploy
- 选择 Autoscale deployment 或 Reserved VM
- 部署后会得到固定域名(如
https://your-app.username.repl.co)
或者使用 Render:
- 登录 https://render.com/
- 点击 New → Web Service
- 连接 GitHub 仓库
- 设置启动命令:
uvicorn main:app --host 0.0.0.0 --port $PORT - 添加环境变量(API_KEY 等)
- 点击 Create Web Service
6.2 前端部署(Vercel)
# 确保前端可以正常构建
pnpm run build
# 初始化 Git
git init
git add .
git commit -m "feat: frontend for markitdown service"
# 推送到 GitHub
gh repo create markitdown-frontend --public --source=. --push
# 在 Vercel 部署
# 登录 https://vercel.com/ → New Project → 选择仓库 → Deploy6.3 更新环境变量
部署后,在 Vercel 项目设置中更新环境变量:
NEXT_PUBLIC_API_URL=https://your-backend.repl.co
NEXT_PUBLIC_API_KEY=your_secret_key_here✅ 质量检查
功能完整性
- Swagger 文档完整,可直接测试
- 支持的文件类型都能正常转换
- 大文件处理有进度反馈
错误处理
- 不支持的格式返回友好提示
- 文件过大时给出明确错误
- 网络错误时有重试机制
安全性
- 有 API Key 或其他鉴权机制
- 限制请求频率(如 5 次/分钟)
- 敏感信息不暴露在前端代码中
- 上传的文件会定期清理
性能
- 大文件转换不阻塞其他请求
- 有合理的超时设置
- 临时文件自动清理(避免磁盘满)
🔁 进阶练习
练习 1:增加 Webhook 回调
转换完成后通知用户:
import requests
@app.post("/convert/file")
async def convert_file(file: UploadFile, callback_url: str = None):
# 转换逻辑
...
# 完成后回调
if callback_url:
requests.post(callback_url, json={
"file_id": file_id,
"status": "success",
"download_url": download_url
})练习 2:记录调用日志到数据库
使用 Supabase 或 PlanetScale:
from supabase import create_client
supabase = create_client(
os.getenv("SUPABASE_URL"),
os.getenv("SUPABASE_KEY")
)
@app.post("/convert/file")
async def convert_file(...):
...
# 记录到数据库
supabase.table("conversion_logs").insert({
"file_id": file_id,
"filename": file.filename,
"file_size": file.size,
"client_ip": request.client.host,
"created_at": datetime.now().isoformat()
}).execute()练习 3:做成 SaaS 产品
- 添加用户注册登录(NextAuth.js)
- 设计定价计划(免费 10 次/月,付费无限制)
- 接入支付(Stripe / Lemon Squeezy)
- 添加使用统计仪表盘
- 结合 5.5 SEO 网站 做营销页面
💡 常见问题
Q:Replit 部署后一段时间不访问就停止了? A:免费版会休眠。升级到付费版或使用 Render/Railway 等平台。
Q:如何调试后端代码?
A:在代码中添加 print() 或 logger.info(),查看 Replit Console 的输出。也可以在 VS Code 中用 Remote SSH 连接 Replit。
Q:前端如何显示上传进度?
A:使用 XMLHttpRequest 或 fetch 配合 ReadableStream,监听 progress 事件。
Q:如何处理大文件上传超时? A:
- 增加服务器超时时间
- 改为分片上传
- 使用对象存储(如 S3、Cloudflare R2)直传
🎯 本节小结
完成这个项目后,你应该掌握了:
✅ 如何设计和实现 RESTful API ✅ 如何使用 FastAPI 构建后端服务 ✅ 如何实现前后端分离架构 ✅ 如何部署全栈应用到公网 ✅ 基础的安全措施(鉴权、限流、日志)
把这个服务分享给朋友,收集真实反馈! 接下来进入 5.5 SEO 友好的网站,学习如何搭建可以获取搜索流量的网站。