Flask URL规则与变量 #
一、URL规则概述 #
1.1 URL规则语法 #
Flask的URL规则支持静态路径和动态变量:
python
# 静态URL
@app.route('/about')
# 动态URL(变量)
@app.route('/user/<username>')
# 带类型转换的动态URL
@app.route('/post/<int:id>')
1.2 规则组成 #
| 组成部分 | 说明 | 示例 |
|---|---|---|
| 静态部分 | 固定的URL路径 | /user/ |
| 动态部分 | 可变的URL变量 | <username> |
| 类型转换器 | 指定变量类型 | int: |
二、动态变量 #
2.1 基本变量 #
python
@app.route('/user/<username>')
def show_user(username):
return f'用户: {username}'
# 访问 /user/zhangsan → 用户: zhangsan
# 访问 /user/lisi → 用户: lisi
2.2 多个变量 #
python
@app.route('/user/<username>/post/<int:post_id>')
def show_post(username, post_id):
return f'用户 {username} 的文章 {post_id}'
# 访问 /user/zhangsan/post/1 → 用户 zhangsan 的文章 1
2.3 变量命名规则 #
python
# 有效的变量名
@app.route('/user/<username>') # 正确
@app.route('/user/<user_name>') # 正确
@app.route('/user/<userName>') # 正确
# 无效的变量名
# @app.route('/user/<user-name>') # 错误:不能包含连字符
# @app.route('/user/<123user>') # 错误:不能以数字开头
三、内置类型转换器 #
3.1 string转换器 #
python
# string是默认转换器,接受不包含斜杠的字符串
@app.route('/user/<string:username>')
def show_user(username):
return f'用户: {username}'
# 等价于
@app.route('/user/<username>')
def show_user(username):
return f'用户: {username}'
# 匹配示例
# /user/zhangsan → 正常
# /user/zhang-san → 正常
# /user/zhang/san → 404(包含斜杠)
3.2 int转换器 #
python
@app.route('/post/<int:post_id>')
def show_post(post_id):
return f'文章ID: {post_id}'
# post_id 自动转换为整数类型
# 匹配示例
# /post/1 → 正常,post_id = 1
# /post/123 → 正常,post_id = 123
# /post/abc → 404(不是整数)
# /post/-1 → 404(只接受正整数)
3.3 float转换器 #
python
@app.route('/price/<float:amount>')
def show_price(amount):
return f'价格: ¥{amount:.2f}'
# amount 自动转换为浮点数
# 匹配示例
# /price/99.99 → 正常,amount = 99.99
# /price/100 → 正常,amount = 100.0
# /price/abc → 404(不是浮点数)
3.4 path转换器 #
python
@app.route('/file/<path:filename>')
def download(filename):
return f'下载文件: {filename}'
# path可以包含斜杠
# 匹配示例
# /file/docs/readme.txt → filename = docs/readme.txt
# /file/static/css/style.css → filename = static/css/style.css
# /file/a/b/c/d.txt → filename = a/b/c/d.txt
3.5 uuid转换器 #
python
import uuid
@app.route('/item/<uuid:item_id>')
def show_item(item_id):
return f'项目ID: {item_id}'
# item_id 自动转换为UUID对象
# 匹配示例
# /item/123e4567-e89b-12d3-a456-426614174000 → 正常
# /item/abc → 404(不是有效的UUID)
3.6 转换器对比 #
| 转换器 | 类型 | 匹配规则 | 示例 |
|---|---|---|---|
| string | str | 不含斜杠的任意字符串 | hello |
| int | int | 正整数 | 123 |
| float | float | 正浮点数 | 99.99 |
| path | str | 含斜杠的任意字符串 | a/b/c |
| uuid | UUID | UUID格式字符串 | 123e4567-… |
四、自定义转换器 #
4.1 创建自定义转换器 #
python
from werkzeug.routing import BaseConverter
class MobileConverter(BaseConverter):
"""手机号转换器"""
def to_python(self, value):
# 验证手机号格式
if not value.isdigit() or len(value) != 11:
raise ValueError('无效的手机号')
return value
def to_url(self, value):
# 生成URL时调用
return str(value)
# 注册转换器
app.url_map.converters['mobile'] = MobileConverter
# 使用自定义转换器
@app.route('/user/mobile/<mobile:phone>')
def user_by_phone(phone):
return f'手机号: {phone}'
4.2 列表转换器 #
python
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
# tags = ['python', 'flask', 'web']
# 使用url_for生成URL
url_for('show_tags', tags=['python', 'flask'])
# 输出: /tags/python,flask
4.3 正则转换器 #
python
import re
class RegexConverter(BaseConverter):
"""正则表达式转换器"""
def __init__(self, url_map, *items):
super().__init__(url_map)
self.regex = items[0]
app.url_map.converters['regex'] = RegexConverter
# 匹配4位数字
@app.route('/year/<regex("[0-9]{4}"):year>')
def show_year(year):
return f'年份: {year}'
# 匹配邮箱
@app.route('/email/<regex("[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}"):email>')
def show_email(email):
return f'邮箱: {email}'
4.4 枚举转换器 #
python
class EnumConverter(BaseConverter):
"""枚举转换器:只接受预定义的值"""
def __init__(self, url_map, *items):
super().__init__(url_map)
self.items = items
self.regex = '|'.join(items)
def to_python(self, value):
if value not in self.items:
raise ValueError(f'无效的值: {value}')
return value
app.url_map.converters['enum'] = EnumConverter
@app.route('/status/<enum("active","inactive","pending"):status>')
def show_status(status):
return f'状态: {status}'
# 只匹配 active, inactive, pending
五、URL规则高级用法 #
5.1 可选变量 #
python
# Flask不直接支持可选变量,但可以定义多个路由
@app.route('/user')
@app.route('/user/<username>')
def show_user(username=None):
if username:
return f'用户: {username}'
return '所有用户'
# /user → 所有用户
# /user/zhangsan → 用户: zhangsan
5.2 默认值 #
python
@app.route('/page')
@app.route('/page/<int:page_num>')
def show_page(page_num=1):
return f'第 {page_num} 页'
# /page → 第 1 页
# /page/2 → 第 2 页
5.3 复杂URL规则 #
python
# 文件路径
@app.route('/static/<path:filepath>')
def serve_static(filepath):
return f'静态文件: {filepath}'
# API版本
@app.route('/api/v<int:version>/users')
def api_users(version):
return f'API v{version} 用户列表'
# 多层级路径
@app.route('/category/<category>/subcategory/<subcategory>/item/<int:item_id>')
def show_item(category, subcategory, item_id):
return f'{category}/{subcategory}/{item_id}'
5.4 URL规则中的斜杠 #
python
# 严格匹配
@app.route('/about')
def about():
return '关于'
# /about → 正常
# /about/ → 404
# 自动重定向
@app.route('/about/')
def about():
return '关于'
# /about → 重定向到 /about/
# /about/ → 正常
六、URL构建 #
6.1 url_for函数 #
python
from flask import url_for
@app.route('/user/<username>')
def profile(username):
return f'用户: {username}'
with app.test_request_context():
# 基本用法
print(url_for('profile', username='zhangsan'))
# 输出: /user/zhangsan
# 添加查询参数
print(url_for('profile', username='zhangsan', page=1))
# 输出: /user/zhangsan?page=1
# 添加锚点
print(url_for('profile', username='zhangsan', _anchor='comments'))
# 输出: /user/zhangsan#comments
# 外部URL
print(url_for('profile', username='zhangsan', _external=True))
# 输出: http://localhost:5000/user/zhangsan
6.2 蓝图中的url_for #
python
from flask import Blueprint, url_for
auth = Blueprint('auth', __name__, url_prefix='/auth')
@auth.route('/login')
def login():
return '登录'
# 使用蓝图名作为前缀
url_for('auth.login') # /auth/login
url_for('auth.login', next='/dashboard') # /auth/login?next=/dashboard
6.3 静态文件URL #
python
# 静态文件URL
url_for('static', filename='css/style.css')
# 输出: /static/css/style.css
url_for('static', filename='js/main.js')
# 输出: /static/js/main.js
七、URL规则调试 #
7.1 查看所有规则 #
python
# 打印所有URL规则
for rule in app.url_map.iter_rules():
print(f'规则: {rule.rule}')
print(f'端点: {rule.endpoint}')
print(f'方法: {rule.methods}')
print(f'参数: {rule.arguments}')
print('---')
7.2 匹配测试 #
python
from werkzeug.routing import Rule
# 测试URL是否匹配
with app.test_request_context():
adapter = app.url_map.bind('')
# 匹配URL
endpoint, args = adapter.match('/user/zhangsan')
print(f'端点: {endpoint}') # profile
print(f'参数: {args}') # {'username': 'zhangsan'}
7.3 路由冲突检测 #
python
def check_route_conflicts():
rules = {}
conflicts = []
for rule in app.url_map.iter_rules():
key = (rule.rule, frozenset(rule.methods - {'HEAD', 'OPTIONS'}))
if key in rules:
conflicts.append({
'rule': rule.rule,
'endpoints': [rules[key], rule.endpoint]
})
else:
rules[key] = rule.endpoint
return conflicts
八、最佳实践 #
8.1 URL设计原则 #
python
# RESTful风格
@app.route('/users') # 获取用户列表
@app.route('/users/<int:id>') # 获取单个用户
@app.route('/users', methods=['POST']) # 创建用户
# 资源嵌套
@app.route('/users/<int:user_id>/posts') # 用户的文章列表
@app.route('/users/<int:user_id>/posts/<int:post_id>') # 用户的单篇文章
# 避免过深的嵌套(建议不超过2层)
# 不推荐
# /users/<int:user_id>/posts/<int:post_id>/comments/<int:comment_id>
# 推荐
# /posts/<int:post_id>/comments/<int:comment_id>
8.2 类型选择 #
| 场景 | 推荐类型 | 示例 |
|---|---|---|
| 用户名 | string | /user/<username> |
| ID | int | /post/<int:id> |
| 价格 | float | /price/<float:amount> |
| 文件路径 | path | /file/<path:filename> |
| 唯一标识 | uuid | /item/<uuid:id> |
8.3 命名规范 #
python
# 推荐:有意义的变量名
@app.route('/user/<username>')
@app.route('/post/<int:post_id>')
@app.route('/category/<category_name>')
# 不推荐:无意义的变量名
@app.route('/user/<x>')
@app.route('/post/<int:id1>')
九、总结 #
9.1 核心要点 #
| 要点 | 说明 |
|---|---|
| 动态变量 | 使用<name>语法 |
| 类型转换 | string、int、float、path、uuid |
| 自定义转换器 | 继承BaseConverter |
| URL构建 | 使用url_for函数 |
| 匹配规则 | 按顺序匹配,静态优先 |
9.2 下一步 #
现在你已经掌握了URL规则与变量,接下来让我们学习 HTTP方法,了解如何处理不同的HTTP请求!
最后更新:2026-03-28