Flask路由基础

路由是Web应用的骨架,它将URL映射到具体的处理函数。本章将深入讲解Flask路由的所有核心概念。

1. 什么是路由?

在Web开发中,路由(Routing) 是指将URL(统一资源定位符)映射到应用程序中特定处理函数的过程。

简单来说: 路由就是告诉Flask,当用户访问某个URL时,应该执行哪个函数。
路由的基本流程:
  1. 用户访问URL(如:/user/john
  2. Flask根据定义的路由规则匹配URL
  3. 找到对应的视图函数
  4. 执行函数并返回响应
  5. 浏览器显示结果
核心概念
  • URL:用户访问的地址
  • 视图函数:处理请求的函数
  • 装饰器:连接URL和函数
  • 响应:返回给用户的内容

2. 基本路由定义

在Flask中,路由使用@app.route()装饰器定义。让我们从一个最简单的例子开始:

示例1:基本路由

URL: /

处理网站的根路径(首页)

from flask import Flask

app = Flask(__name__)

# 基本路由 - 首页
@app.route('/')
def index():
    return '欢迎访问首页!'

# 带固定路径的路由
@app.route('/about')
def about():
    return '这是关于页面'

# 带多级路径的路由
@app.route('/blog/posts')
def blog_posts():
    return '博客文章列表'

if __name__ == '__main__':
    app.run(debug=True)

示例2:多个URL指向同一个视图函数

一个视图函数可以处理多个URL:

URL: //home/index

三个URL都指向同一个处理函数

# 一个函数处理多个URL
@app.route('/')
@app.route('/home')
@app.route('/index')
def home():
    return '这是网站主页,可以通过多个URL访问'

# 或者使用列表形式
@app.route(['/welcome', '/greeting', '/hello'])
def welcome():
    return '欢迎来到我们的网站!'

3. 动态路由参数

动态路由允许URL中包含变量,这些变量会作为参数传递给视图函数。

基本类型转换器:

转换器 描述 示例
string 接受任何不包含斜杠的文本(默认) /user/<string:username>
int 接受正整数 /post/<int:post_id>
float 接受浮点数 /price/<float:amount>
path 类似string,但接受斜杠 /path/<path:subpath>
uuid 接受UUID字符串 /item/<uuid:item_id>

示例3:使用不同类型的参数

URL: /user/john

获取用户名参数

# 字符串参数(默认)
@app.route('/user/<username>')
def show_user_profile(username):
    return f'用户主页: {username}'

# 整数参数
@app.route('/post/<int:post_id>')
def show_post(post_id):
    return f'文章 #{post_id}'

# 浮点数参数
@app.route('/price/<float:price>')
def show_price(price):
    return f'价格: ${price:.2f}'

# 路径参数(允许包含斜杠)
@app.route('/path/<path:subpath>')
def show_subpath(subpath):
    return f'子路径: {subpath}'

# 多个参数
@app.route('/user/<username>/post/<int:post_id>')
def user_post(username, post_id):
    return f'{username} 的文章 #{post_id}'

示例4:参数验证和默认值

from flask import abort

# 带验证的参数
@app.route('/user/<int:user_id>')
def get_user(user_id):
    if user_id < 1:
        abort(400, description="用户ID必须为正整数")

    # 模拟从数据库获取用户
    users = {
        1: "张三",
        2: "李四",
        3: "王五"
    }

    if user_id in users:
        return f"用户: {users[user_id]}"
    else:
        abort(404, description="用户不存在")

# 使用默认值(通过函数参数)
@app.route('/search')
@app.route('/search/<query>')
def search(query='默认搜索'):
    return f'搜索关键词: {query}'

4. HTTP方法处理

Flask路由可以指定处理的HTTP方法。默认只处理GET请求。

常见HTTP方法:
  • GET 获取资源
  • POST 创建资源
  • PUT 更新资源
  • DELETE 删除资源
  • PATCH 部分更新
注意: 在HTML表单中,通常只支持GET和POST方法。 其他方法需要通过JavaScript或API客户端使用。

示例5:处理不同的HTTP方法

URL: /api/user

同一个URL,根据HTTP方法执行不同的操作

from flask import request, jsonify

# RESTful API 示例
@app.route('/api/user', methods=['GET', 'POST'])
@app.route('/api/user/<int:user_id>', methods=['GET', 'PUT', 'DELETE'])
def handle_user(user_id=None):
    """用户资源API"""

    if request.method == 'GET':
        if user_id:
            # 获取单个用户
            return jsonify({'id': user_id, 'name': '张三', 'method': 'GET'})
        else:
            # 获取用户列表
            return jsonify({'users': [1, 2, 3], 'method': 'GET'})

    elif request.method == 'POST':
        # 创建新用户
        data = request.get_json()
        return jsonify({'message': '用户已创建', 'data': data, 'method': 'POST'}), 201

    elif request.method == 'PUT':
        # 更新用户
        data = request.get_json()
        return jsonify({'message': f'用户{user_id}已更新', 'data': data, 'method': 'PUT'})

    elif request.method == 'DELETE':
        # 删除用户
        return jsonify({'message': f'用户{user_id}已删除', 'method': 'DELETE'}), 204

    else:
        # 不支持的请求方法
        return jsonify({'error': '方法不允许'}), 405

示例6:使用不同的函数处理不同方法

# 更清晰的方式:为不同方法创建不同的函数
@app.route('/article/<int:article_id>', methods=['GET'])
def get_article(article_id):
    return f'获取文章 {article_id}'

@app.route('/article', methods=['POST'])
def create_article():
    return '创建新文章'

@app.route('/article/<int:article_id>', methods=['PUT'])
def update_article(article_id):
    return f'更新文章 {article_id}'

@app.route('/article/<int:article_id>', methods=['DELETE'])
def delete_article(article_id):
    return f'删除文章 {article_id}'

5. URL构建与反转

Flask提供了url_for()函数,可以根据视图函数名动态生成URL,这称为URL反转。

为什么使用url_for()?

  • 避免硬编码URL:URL结构变化时只需修改路由定义
  • 自动处理特殊字符:自动进行URL编码
  • 支持外部URL:可以生成完整的绝对URL
  • 更好的可维护性:集中管理URL

示例7:使用url_for()生成URL

在视图函数中生成其他路由的URL
from flask import Flask, url_for, redirect

app = Flask(__name__)

@app.route('/')
def index():
    # 生成其他路由的URL
    user_url = url_for('user_profile', username='john')
    post_url = url_for('show_post', post_id=123)

    return f'''
    <h1>首页</h1>
    <ul>
        <li><a href="{user_url}">访问John的主页</a></li>
        <li><a href="{post_url}">查看文章123</a></li>
        <li><a href="{url_for('about')}">关于我们</a></li>
    </ul>
    '''

@app.route('/user/<username>')
def user_profile(username):
    return f'用户主页: {username}'

@app.route('/post/<int:post_id>')
def show_post(post_id):
    return f'文章 #{post_id}'

@app.route('/about')
def about():
    return '关于页面'

# 重定向到其他路由
@app.route('/old-about')
def old_about():
    # 重定向到新的about页面
    return redirect(url_for('about'))

if __name__ == '__main__':
    app.run(debug=True)

示例8:在模板中使用url_for()


<!DOCTYPE html>
<html>
<head>
    <title>{% block title %}{% endblock %}</title>
</head>
<body>
    <nav>
        <ul>
            <li><a href="{{ url_for('index') }}">首页</a></li>
            <li><a href="{{ url_for('about') }}">关于</a></li>
            <li><a href="{{ url_for('user_profile', username='current') }}">我的主页</a></li>
            <li><a href="{{ url_for('show_post', post_id=1) }}">最新文章</a></li>
        </ul>
    </nav>

    {% block content %}{% endblock %}
</body>
</html>

示例9:生成外部URL和带查询参数的URL

from flask import request

@app.route('/share')
def share():
    # 生成带查询参数的URL
    share_url = url_for('show_post', post_id=42, _external=True)
    share_url_with_params = f"{share_url}?ref=share_button&utm_source=flask"

    return f'''
    <h1>分享链接</h1>
    <p>文章链接: {share_url}</p>
    <p>带跟踪参数的链接: <a href="{share_url_with_params}">点击分享</a></p>

    <!-- 生成当前页面的完整URL -->
    <p>当前页面: {request.url}</p>
    '''

6. 路由选项与装饰器

常用路由选项:

选项 描述 示例
methods 接受的HTTP方法列表 methods=['GET', 'POST']
defaults 视图函数参数的默认值 defaults={'page': 1}
subdomain 子域名 subdomain='api'
strict_slashes 是否严格匹配结尾斜杠 strict_slashes=False
redirect_to 重定向到其他URL redirect_to='/new-url'

示例10:使用路由选项

展示各种路由选项的用法
# defaults参数:为参数提供默认值
@app.route('/blog', defaults={'page': 1})
@app.route('/blog/page/<int:page>')
def show_blog(page):
    return f'博客第 {page} 页'

# strict_slashes:控制是否严格匹配斜杠
@app.route('/about/', strict_slashes=False)
def about_page():
    # 可以同时访问 /about 和 /about/
    return '关于页面'

# redirect_to:永久重定向
@app.route('/old-page', redirect_to='/new-page')
def old_page():
    pass  # 永远不会执行

@app.route('/new-page')
def new_page():
    return '这是新的页面'

# 自定义转换器(高级功能)
from werkzeug.routing import BaseConverter

class ListConverter(BaseConverter):
    """自定义转换器,处理逗号分隔的列表"""

    def to_python(self, value):
        return value.split(',')

    def to_url(self, values):
        return ','.join(str(v) for v in values)

# 注册自定义转换器
app.url_map.converters['list'] = ListConverter

# 使用自定义转换器
@app.route('/tags/<list:tags>')
def show_tags(tags):
    return f'标签: {tags}'  # tags是列表,如 ['python', 'flask', 'web']

7. 蓝图系统

蓝图(Blueprint)是Flask中用于组织大型应用的模块化方法。

为什么使用蓝图?

  • 模块化:将应用拆分为多个模块
  • 可重用性:蓝图可以在多个应用中复用
  • 延迟注册:路由可以稍后注册到应用
  • URL前缀:为蓝图的所有路由添加统一前缀
  • 更好的代码组织:大型项目的必备结构

示例11:创建和使用蓝图

创建博客模块的蓝图
# app/blog/__init__.py
# 创建博客蓝图
from flask import Blueprint

# 创建蓝图实例
# 第一个参数是蓝图名称,第二个是模块名
# url_prefix 为所有路由添加前缀
blog_bp = Blueprint('blog', __name__, url_prefix='/blog')

# 导入路由(避免循环导入)
from app.blog import routes
# app/blog/routes.py
from . import blog_bp
from flask import render_template

# 使用蓝图定义路由(注意:使用@blog_bp.route而不是@app.route)
@blog_bp.route('/')
def index():
    return '博客首页'

@blog_bp.route('/post/<int:post_id>')
def show_post(post_id):
    return f'博客文章 #{post_id}'

@blog_bp.route('/category/<category_name>')
def show_category(category_name):
    return f'分类: {category_name}'

# 蓝图也可以有自己的错误处理
@blog_bp.errorhandler(404)
def blog_not_found(error):
    return '博客模块:页面未找到', 404
# app/__init__.py (主应用文件)
from flask import Flask
from app.blog import blog_bp
from app.auth import auth_bp
from app.admin import admin_bp

def create_app():
    app = Flask(__name__)

    # 注册蓝图
    app.register_blueprint(blog_bp)
    app.register_blueprint(auth_bp)
    app.register_blueprint(admin_bp)

    # 为蓝图添加URL前缀(也可以在注册时指定)
    # app.register_blueprint(auth_bp, url_prefix='/auth')

    return app

示例12:多模块应用结构

myapp/
├── app/
│   ├── __init__.py          # 应用工厂
│   ├── main/
│   │   ├── __init__.py     # 主模块蓝图
│   │   └── routes.py       # 主模块路由
│   ├── blog/
│   │   ├── __init__.py     # 博客蓝图
│   │   ├── routes.py       # 博客路由
│   │   └── models.py       # 博客模型
│   ├── auth/
│   │   ├── __init__.py     # 认证蓝图
│   │   └── routes.py       # 认证路由
│   └── admin/
│       ├── __init__.py     # 管理后台蓝图
│       └── routes.py       # 管理路由
├── config.py               # 配置文件
└── run.py                 # 启动文件

8. 路由最佳实践

应该做的
  • 使用有意义的URL结构
  • 遵循RESTful设计原则
  • 使用url_for()生成URL
  • 为API添加版本前缀(如/api/v1/
  • 使用蓝图组织大型应用
  • 为动态参数添加类型转换器
  • 保持URL简洁且易于理解
应该避免的
  • 不要在URL中使用技术术语
  • 避免过深的嵌套路径
  • 不要硬编码URL
  • 避免使用特殊字符
  • 不要在一个函数中处理太多HTTP方法
  • 避免不一致的URL命名
  • 不要忘记添加错误处理

示例13:RESTful API路由设计

# 良好的RESTful设计
api_bp = Blueprint('api', __name__, url_prefix='/api/v1')

@api_bp.route('/users', methods=['GET'])
def get_users():
    """获取用户列表"""
    return jsonify({'users': []})

@api_bp.route('/users', methods=['POST'])
def create_user():
    """创建新用户"""
    return jsonify({'message': '用户已创建'}), 201

@api_bp.route('/users/<int:user_id>', methods=['GET'])
def get_user(user_id):
    """获取单个用户"""
    return jsonify({'user': {'id': user_id}})

@api_bp.route('/users/<int:user_id>', methods=['PUT'])
def update_user(user_id):
    """更新用户"""
    return jsonify({'message': '用户已更新'})

@api_bp.route('/users/<int:user_id>', methods=['DELETE'])
def delete_user(user_id):
    """删除用户"""
    return jsonify({'message': '用户已删除'}), 204
练习与测试
练习题:
  1. 创建一个商品展示的路由,支持以下功能:
    • 访问 /products 显示商品列表
    • 访问 /products/<id> 显示单个商品
    • 访问 /products/category/<category> 显示分类商品
    • 支持搜索参数:/products?search=keyword
  2. 创建一个用户管理蓝图,包含:
    • 用户注册(POST /register)
    • 用户登录(POST /login)
    • 查看用户资料(GET /profile/<username>)
    • 更新用户资料(PUT /profile/<username>)
  3. 使用url_for()创建一个导航菜单,包含所有路由的链接
提示: 尝试使用不同的HTTP方法、参数类型和蓝图来实现这些功能。
本章总结

你已经学习了Flask路由的核心概念:

  • ✅ 基本路由定义和装饰器使用
  • ✅ 动态路由参数和类型转换器
  • ✅ HTTP方法处理和RESTful设计
  • ✅ URL构建与url_for()函数
  • ✅ 蓝图系统的模块化组织
  • ✅ 路由最佳实践和设计原则