FastAPI 的价值不只在于“快”:它把 Python 类型标注、请求校验、异步处理和 OpenAPI 文档连接在一起,让开发者可以用较少的样板代码交付结构清晰的 API。沿着 REST 接口、Jinja2 页面渲染和 URL 短链接项目逐层推进,可以覆盖一个小型 Web 服务从输入校验到页面交互的完整路径。
类型标注同时定义接口契约
在 FastAPI 中,路径参数、查询参数和请求体模型都可以通过 Python 类型声明。框架据此完成数据解析与校验,并生成交互式 API 文档。
例如,一个 URL 创建接口可以用 Pydantic 模型约束输入:
from pydantic import BaseModel, HttpUrl
class ShortenRequest(BaseModel):
url: HttpUrl
HttpUrl 不只是编辑器提示。当客户端提交无法解析的 URL 时,FastAPI 会在业务函数执行前返回校验错误。这能把“检查字段是否存在、判断格式是否合法”从路由代码中移走,让处理函数专注于业务行为。
服务启动后,FastAPI 通常会暴露两套自动生成的接口文档:
/docs:适合直接调用和调试接口的 Swagger UI。/redoc:适合阅读接口结构的 ReDoc 页面。/openapi.json:供客户端生成器和测试工具消费的 OpenAPI 描述。
一个应用可以同时提供 API 和网页
URL 短链接服务至少涉及三类请求:创建短链接、查看创建页面,以及根据短码跳转到原地址。它们可以放在同一个 FastAPI 应用中:REST API 返回 JSON,页面路由通过 Jinja2 返回 HTML,跳转路由则返回重定向响应。
下面是一套可以这样实践的最小项目。示例为了便于运行,使用内存字典保存数据;进程重启后记录会消失,因此不应直接用于生产环境。
目录结构如下:
fastapi-shortener/
├── app.py
├── requirements.txt
└── templates/
└── index.html
requirements.txt:
fastapi
uvicorn[standard]
jinja2
python-multipart
app.py:
import secrets
import string
from fastapi import FastAPI, Form, HTTPException, Request, status
from fastapi.responses import RedirectResponse
from fastapi.templating import Jinja2Templates
from pydantic import BaseModel, HttpUrl
app = FastAPI(title='FastAPI URL Shortener')
templates = Jinja2Templates(directory='templates')
links: dict[str, str] = {}
ALPHABET = string.ascii_letters + string.digits
class ShortenRequest(BaseModel):
url: HttpUrl
class ShortenResponse(BaseModel):
code: str
short_url: str
target_url: str
def create_code(length: int = 7) -> str:
while True:
code = ''.join(secrets.choice(ALPHABET) for _ in range(length))
if code not in links:
return code
def store_url(url: str) -> str:
code = create_code()
links[code] = url
return code
@app.get('/')
def home(request: Request):
return templates.TemplateResponse(
request=request,
name='index.html',
context={'result': None},
)
@app.post('/shorten', response_model=ShortenResponse, status_code=status.HTTP_201_CREATED)
def shorten(payload: ShortenRequest, request: Request):
target_url = str(payload.url)
code = store_url(target_url)
return ShortenResponse(
code=code,
short_url=str(request.url_for('redirect_to_target', code=code)),
target_url=target_url,
)
@app.post('/submit')
def submit(request: Request, url: HttpUrl = Form(...)):
target_url = str(url)
code = store_url(target_url)
return templates.TemplateResponse(
request=request,
name='index.html',
context={
'result': str(request.url_for('redirect_to_target', code=code)),
},
)
@app.get('/{code}', name='redirect_to_target')
def redirect_to_target(code: str):
target_url = links.get(code)
if target_url is None:
raise HTTPException(status_code=404, detail='Short URL not found')
return RedirectResponse(target_url, status_code=status.HTTP_307_TEMPORARY_REDIRECT)
templates/index.html:
<!doctype html>
<html lang='zh-CN'>
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<title>URL Shortener</title>
</head>
<body>
<main>
<h1>创建短链接</h1>
<form action='/submit' method='post'>
<label for='url'>原始 URL</label>
<input id='url' name='url' type='url' required placeholder='https://example.com/article'>
<button type='submit'>生成</button>
</form>
{% if result %}
<p>短链接:<a href='{{ result }}'>{{ result }}</a></p>
{% endif %}
</main>
</body>
</html>
在项目目录中执行:
python -m venv .venv
source .venv/bin/activate
# Windows PowerShell 使用:.venv\Scripts\Activate.ps1
pip install -r requirements.txt
uvicorn app:app --reload
浏览器访问 http://127.0.0.1:8000/ 可以使用表单,访问 http://127.0.0.1:8000/docs 可以调试 REST API。也可以直接执行:
curl -X POST http://127.0.0.1:8000/shorten \
-H 'Content-Type: application/json' \
-d '{"url":"https://example.com/a/long/path"}'
从演示项目走向可部署服务
最小实现展示了 FastAPI 的核心组合方式,但生产化需要补齐几个边界。
持久化与并发。 内存字典无法跨进程共享。使用多个 Uvicorn worker 时,请求可能访问不同的数据副本。可以将链接记录放入 PostgreSQL,并为短码建立唯一索引;如果业务更看重低延迟,也可以使用 Redis,但需要明确持久化和淘汰策略。
短码冲突。 示例通过随机生成和字典检查减少冲突,但数据库写入仍可能出现并发竞争。最终约束应放在数据库唯一索引中,应用捕获冲突后重新生成短码。
跳转安全。 公开短链接服务容易被用于钓鱼、恶意下载或绕过域名过滤。上线前应考虑域名黑名单、举报和封禁机制、访问频率限制,以及后台审计能力。若允许用户指定短码,还要保留系统路径,例如 docs、openapi.json 和管理端前缀。
HTTP 状态码。 示例使用 307 Temporary Redirect,便于以后修改目标地址。如果短链接目标永久固定,可以评估 301 或 308,但浏览器和中间缓存可能长期记住永久重定向,变更和撤销会更困难。
建议的学习与落地顺序
可以先用一个资源的 CRUD API 熟悉路径参数、查询参数、Pydantic 模型和异常处理,再加入 Jinja2 页面,理解同一服务如何同时处理 JSON 与 HTML。随后完成短链接项目,并逐步替换内存存储、增加测试和部署配置。
准备上线时,至少检查以下项目:
- 请求模型是否限制 URL 格式、字段长度和可选值。
- 短码是否有唯一约束,冲突后是否能重试。
- 未知短码是否稳定返回
404。 - 是否设置访问日志、速率限制和滥用处置流程。
- 反向代理是否正确传递主机名和协议,否则生成的短链接可能使用内部地址。
- 数据库迁移、备份和恢复流程是否经过验证。
FastAPI 可以快速搭起接口,但“开发速度快”不等于可以忽略数据一致性、安全和运维。把类型契约、自动文档和简洁路由作为起点,再用数据库约束、测试与监控守住边界,才是这类项目真正可复用的工程价值。