下载工作台
Flask Web 开发

路由、蓝图与请求上下文

试读上半部分 · 解锁后可读全文

第 2 章 · 路由、蓝图与请求上下文

本章目标:掌握 Flask Blueprint(蓝图)拆分路由模块;熟练使用 url_for 生成 URL 避免硬编码;理解 requestresponse 对象读写查询参数、表单与 JSON;认识 g 请求级全局对象与 before_request 钩子;在 api-demo 中实现 mainapi 两套蓝图,为 REST 章节打基础。

学时建议:4~5 小时(含 1.5 小时跟练)

前置:完成 flask-web ch01create_app、最小路由、flask run)。


2.1 场景说明:拆分「页面」与「API」

api-demo 需要同时提供:

访问方路径前缀蓝图响应类型
运维探活/healthmainJSON
管理页(ch03)/admin/...adminHTML
对外 REST/api/v1/...apiJSON
虚构公网入口https://api.example.com反代到本服务

若所有 @app.route 写在 __init__.py,文件会迅速膨胀。蓝图按功能模块组织,类似 Django 的 App + urls.py

api_demo/
├── routes/
│   ├── main.py      # 根与健康检查
│   ├── api.py       # /api/v1/products 等
│   └── admin.py     # /admin/(ch03 模板)

2.2 蓝图 Blueprint 基础

概念Django 对照Flask 蓝图
模块划分catalog Appapi Blueprint
URL 前缀path("catalog/", include(...))Blueprint(..., url_prefix="/api/v1")
命名空间app_name + nameBlueprint 名 + 端点名

api_demo/routes/api.py

from flask import Blueprint, jsonify, request

bp = Blueprint("api", __name__, url_prefix="/api/v1")


@bp.route("/products", methods=["GET"])
def product_list():
    page = request.args.get("page", 1, type=int)
    per_page = request.args.get("per_page", 10, type=int)
    items = [
        {"id": 1, "name": "示例商品 A", "price": 99.0},
        {"id": 2, "name": "示例商品 B", "price": 199.0},
    ]
    return jsonify({
        "page": page,
        "per_page": per_page,
        "count": len(items),
        "items": items,
    })


@bp.route("/products/<int:product_id>", methods=["GET"])
def product_detail(product_id):
    if product_id == 1:
        return jsonify({"id": 1, "name": "示例商品 A", "price": 99.0})
    return jsonify({"error": "not_found"}), 404

api_demo/routes/admin.py(占位,ch03 接模板):

from flask import Blueprint

bp = Blueprint("admin", __name__, url_prefix="/admin")


@bp.route("/")
def dashboard():
    return "Admin dashboard (templates in ch03)"

更新 api_demo/__init__.py

from flask import Flask

from api_demo.config import config_map


def create_app(config_name=None):
    if config_name is None:
        config_name = "default"

    app = Flask(__name__)
    app.config.from_object(config_map[config_name])

    from api_demo.routes.main import bp as main_bp
    from api_demo.routes.api import bp as api_bp
    from api_demo.routes.admin import bp as admin_bp

    app.register_blueprint(main_bp)
    app.register_blueprint(api_bp)
    app.register_blueprint(admin_bp)

    return app

2.3 url_for 动态 URL

硬编码 href="/api/v1/products" 在修改前缀时需全局替换。url_for 根据端点名生成 URL。

端点命名规则:{蓝图名}.{视图函数名},例如 api.product_list

from flask import url_for

list_url = url_for("api.product_list")           # /api/v1/products
detail_url = url_for("api.product_detail", product_id=1)

api_demo/routes/main.py 增加链接演示:

from flask import Blueprint, url_for

bp = Blueprint("main", __name__)


@bp.route("/")
def index():
    products_api = url_for("api.product_list")
    admin_home = url_for("admin.dashboard")
    return (
        f"api-demo running.<br>"
        f"Products API: {products_api}<br>"
        f"Admin: {admin_home}"
    )


@bp.route("/health")
def health():
    return {
        "status": "ok",
        "service": "api-demo",
        "endpoints": {
            "products": url_for("api.product_list", _external=True),
        },
    }
参数作用
_external=True生成完整 URL(含主机),用于 api.example.com 文档
_method="POST"指定方法生成对应规则

2.4 request 对象

request线程局部的上下文对象,每个 HTTP 请求独立。

属性/方法用途示例
request.methodGET / POST 等if request.method == "POST"
request.args查询参数request.args.get("page", 1, type=int)
request.form表单字段request.form["name"]
request.jsonJSON 体(需 Content-Type)request.get_json()
request.headers请求头request.headers.get("Authorization")
request.path路径/api/v1/products

测试分页:

curl "http://127.0.0.1:5000/api/v1/products?page=2&per_page=5"

2.5 response 与 jsonify

方式适用
返回字符串简单文本
返回 dict / listFlask 自动 JSON 化(2.2+)
jsonify(data)明确 JSON 响应,可设状态码
make_response()自定义头、Cookie
redirect(url_for(...))302 跳转
from flask import jsonify, redirect, request, url_for


@bp.route("/products", methods=["POST"])
def product_create():
    data = request.get_json(silent=True) or {}
    name = data.get("name")
    if not name:
        return jsonify({"error": "name_required"}), 400
    return jsonify({"id": 99, "name": name}), 201


@bp.route("/legacy-products")
def legacy_redirect():
    return redirect(url_for("api.product_list"))

状态码放在 return 元组第二位:return body, 404


以下内容需解锁后阅读

试读已结束。解锁本章 ¥5.00,或开通年度会员畅读全部教程。
年度会员 ¥199.00/年; 小紫 AI 工作台有效会员 ¥99.00/年

正文仅在服务端鉴权后下发,未付费无法获取下半部分内容。