在Web开发中,路由(Routing) 是指将URL(统一资源定位符)映射到应用程序中特定处理函数的过程。
/user/john)在Flask中,路由使用@app.route()装饰器定义。让我们从一个最简单的例子开始:
/处理网站的根路径(首页)
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)
一个视图函数可以处理多个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 '欢迎来到我们的网站!'
动态路由允许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> |
/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}'
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}'
Flask路由可以指定处理的HTTP方法。默认只处理GET请求。
/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
# 更清晰的方式:为不同方法创建不同的函数
@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}'
Flask提供了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)
<!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>
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>
'''
| 选项 | 描述 | 示例 |
|---|---|---|
methods |
接受的HTTP方法列表 | methods=['GET', 'POST'] |
defaults |
视图函数参数的默认值 | defaults={'page': 1} |
subdomain |
子域名 | subdomain='api' |
strict_slashes |
是否严格匹配结尾斜杠 | strict_slashes=False |
redirect_to |
重定向到其他URL | redirect_to='/new-url' |
# 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']
蓝图(Blueprint)是Flask中用于组织大型应用的模块化方法。
# 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
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 # 启动文件
url_for()生成URL/api/v1/)# 良好的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
/products 显示商品列表/products/<id> 显示单个商品/products/category/<category> 显示分类商品/products?search=keywordurl_for()创建一个导航菜单,包含所有路由的链接你已经学习了Flask路由的核心概念:
url_for()函数