术→技巧, 研发

FastAPI学习之模板引擎

钱魏Way · · 101 次浏览

FastAPI 完全可以接入模板引擎来开发传统网站!虽然 FastAPI 以构建高性能 API 著称,但它基于 Starlette 框架,天然支持模板渲染和静态文件托管。

模板引擎选型

引擎 特点 安装命令
Jinja2 语法简洁,广泛使用 pip install jinja2
Mako 高性能,支持嵌入 Python pip install mako
Chameleon XML 风格,适合复杂模板 pip install chameleon

推荐使用 Jinja2(与 FastAPI 集成最方便)

Jinja2 集成步骤

项目结构

.
├── main.py
├── templates/
│   ├── index.html
│   └── includes/
│       └── header.html
└── static/
    ├── css/
    └── js/

安装依赖

pip install jinja2

配置模板引擎

from fastapi import FastAPI, Request
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates

app = FastAPI()

# 挂载静态文件目录
app.mount("/static", StaticFiles(directory="static"), name="static")

# 初始化模板引擎
templates = Jinja2Templates(directory="templates")

基础模板渲染

创建模板文件 templates/index.html

<!DOCTYPE html>
<html>
<head>
    <title>{{ title }}</title>
    <link rel="stylesheet" href="{{ url_for('static', path='/css/style.css') }}">
</head>
<body>
    <h1>{{ message }}</h1>
</body>
</html>

路由渲染模板

@app.get("/")
async def read_root(request: Request):
    return templates.TemplateResponse(
        "index.html",
        {
            "request": request,  # 必须包含 request 对象
            "title": "首页",
            "message": "欢迎使用 FastAPI!"
        }
    )

高级模板功能

模板继承

templates/base.html:

<!DOCTYPE html>
<html>
<head>
    <title>{% block title %}{% endblock %}</title>
    {% block head %}{% endblock %}
</head>
<body>
    {% include "includes/header.html" %}
    {% block content %}{% endblock %}
</body>
</html>
templates/page.html:
{% extends "base.html" %}

{% block title %}子页面{% endblock %}

{% block content %}
<h1>这是子页面内容</h1>
{% endblock %}

循环和条件

<ul>
{% for item in items %}
    <li {% if loop.first %}class="first"{% endif %}>
        {{ item.name }} - {{ item.price }}
    </li>
{% endfor %}
</ul>

{% if user %}
    <p>欢迎回来,{{ user.username }}!</p>
{% else %}
    <p>请先登录</p>
{% endif %}

表单处理

模板 templates/form.html

<form method="post">
    <input type="text" name="username" required>
    <input type="password" name="password" required>
    <button type="submit">登录</button>
</form>

路由处理

from fastapi import Form

@app.get("/login")
async def login_form(request: Request):
    return templates.TemplateResponse("form.html", {"request": request})

@app.post("/login")
async def do_login(
    username: str = Form(...),
    password: str = Form(...)
):
    # 验证逻辑
    return {"username": username}

异步支持

FastAPI 支持在路由中异步渲染模板:

@app.get("/async-page")
async def async_demo(request: Request):
    # 模拟异步操作(如数据库查询)
    data = await fetch_data_from_db()
    return templates.TemplateResponse("async.html", {"request": request, "data": data})

自定义过滤器

# 在模板引擎初始化后添加
def reverse_filter(s: str):
    return s[::-1]

templates.env.filters["reverse"] = reverse_filter
模板中使用:
<p>{{ "hello" | reverse }}</p>  <!-- 输出 olleh -->

错误页面处理

from fastapi.exceptions import HTTPException

@app.exception_handler(404)
async def not_found_exception_handler(request: Request, exc: HTTPException):
    return templates.TemplateResponse(
        "404.html",
        {"request": request},
        status_code=404
    )

性能优化

  • 模板缓存:Jinja2 默认启用缓存,生产环境无需额外配置。
  • 静态文件 CDN:通过mount 托管静态文件,或使用 Nginx/CDN 加速。
  • 异步渲染:对 I/O 密集型操作(如数据库查询)使用async/await 避免阻塞。

模板预加载

# 在应用启动时预编译模板
@app.on_event("startup")
async def preload_templates():
    templates.env.compile_templates(
        "templates", 
        "compiled_templates.zip"
    )

异步渲染(需要第三方扩展)

# 使用 jinja2.asyncenv.AsyncEnvironment
from jinja2 import FileSystemLoader
from jinja2.asyncenv import AsyncEnvironment

async_templates = AsyncEnvironment(
    loader=FileSystemLoader("templates"),
    enable_async=True
)

@app.get("/async-page")
async def async_page(request: Request):
    template = async_templates.get_template("async_page.html")
    content = await template.render_async(request=request)
    return HTMLResponse(content)

完整示例

from fastapi import FastAPI, Request, Form
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates

app = FastAPI()
app.mount("/static", StaticFiles(directory="static"), name="static")
templates = Jinja2Templates(directory="templates")

@app.get("/")
async def home(request: Request):
    return templates.TemplateResponse(
        "index.html",
        {
            "request": request,
            "users": [
                {"name": "Alice", "age": 30},
                {"name": "Bob", "age": 25}
            ]
        }
    )

@app.get("/contact")
async def contact(request: Request):
    return templates.TemplateResponse(
        "contact.html",
        {"request": request}
    )

@app.post("/submit-form")
async def submit_form(
    name: str = Form(...),
    email: str = Form(...)
):
    return {"name": name, "email": email}

最佳实践

  • 模板组织:
    • 使用includes/ 目录存放可复用组件
    • 通过html 实现模板继承
  • 静态文件管理:
    • 使用url_for(‘static’, path=’…’) 生成静态文件 URL
    • 为 CSS/JS 添加版本号防止缓存
  • 安全注意事项:
    • 启用 Jinja2 自动转义:autoescape=True
    • 避免直接渲染用户输入内容
  • 开发热重载:
templates = Jinja2Templates(
    directory="templates",
    auto_reload=True  # 开发环境启用
)

通过以上步骤,可以轻松在 FastAPI 中实现动态页面渲染。建议根据项目复杂度选择合适的功能组合,小型项目使用基础模板渲染即可,复杂项目可结合模板继承和自定义过滤器等功能。

局限性及替代方案

  • 适用性
    • ✅ 适合简单页面或混合模式(API + 少量页面)。
    • ❌ 复杂前端交互建议搭配前端框架(如 React/Vue)。
  • 替代方案
    • 纯 API 模式:FastAPI 只提供数据,前端使用js/Nuxt.js 渲染。
    • 全栈框架:若需更多内置功能(如 ORM、Admin 后台),可考虑 Django。

通过集成 Jinja2 模板引擎,FastAPI 可快速实现服务端渲染的网站开发,同时保留其异步高性能特性。适合轻量级全栈项目或需要 API 与页面混合交付的场景。对于复杂前端,建议采用前后端分离架构,用 FastAPI 专注提供 API 服务。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注