Flask使用redirect()函数实现重定向:
from flask import Flask, redirect, url_for
app = Flask(__name__)
# 简单的重定向示例
@app.route('/')
def index():
# 重定向到home页面
return redirect('/home')
@app.route('/home')
def home():
return '欢迎来到首页'
# 临时重定向(302状态码,默认)
@app.route('/temp')
def temp_redirect():
return redirect('/home', 302)
# 永久重定向(301状态码)
@app.route('/permanent')
def permanent_redirect():
return redirect('/home', 301)
# 重定向到外部URL
@app.route('/external')
def external_redirect():
return redirect('https://www.example.com')
if __name__ == '__main__':
app.run(debug=True)
| 状态码 | 名称 | 说明 | 使用场景 |
|---|---|---|---|
| 301 | 永久重定向 | 永久移动,搜索引擎会更新URL | 网站重构、URL永久变更 |
| 302 | 临时重定向 | 临时移动,搜索引擎不会更新URL | 登录后跳转、表单提交后跳转 |
| 303 | See Other | POST请求后重定向到GET请求 | 避免表单重复提交 |
| 307 | 临时重定向 | 保持请求方法(POST/PUT等) | API重定向需要保持方法时 |
| 308 | 永久重定向 | 保持请求方法(POST/PUT等) | 永久重定向且需要保持方法时 |
url_for()函数是Flask中生成URL的最佳方式,它通过函数名而不是硬编码URL来生成URL。
from flask import Flask, url_for
app = Flask(__name__)
@app.route('/')
def index():
return '首页'
@app.route('/user/<username>')
def user_profile(username):
return f'用户: {username}'
@app.route('/post/<int:post_id>')
def show_post(post_id):
return f'文章ID: {post_id}'
@app.route('/search')
def search():
query = request.args.get('q', '')
return f'搜索: {query}'
# 在视图函数中使用url_for
@app.route('/test_url')
def test_url():
# 生成各个URL
urls = {
'index': url_for('index'),
'user_profile': url_for('user_profile', username='john'),
'show_post': url_for('show_post', post_id=42),
'search': url_for('search', q='python')
}
return str(urls)
if __name__ == '__main__':
with app.test_request_context():
# 在应用上下文外使用url_for
print(url_for('index')) # 输出: /
print(url_for('user_profile', username='alice')) # 输出: /user/alice
print(url_for('show_post', post_id=123)) # 输出: /post/123
print(url_for('search', q='flask')) # 输出: /search?q=flask
<!-- 注意:Blade模板中使用{{ }}转义 -->
<!DOCTYPE html>
<html>
<head>
<title>URL生成示例</title>
</head>
<body>
<!-- 基本链接 -->
<a href="@{{ url_for('index') }}">首页</a>
<!-- 带参数的链接 -->
<a href="{{ url_for('user_profile', username='john') }}">John的个人资料</a>
<!-- 带多个参数的链接 -->
<a href="{{ url_for('search', q='python', page=2) }}">搜索Python,第2页</a>
<!-- 外部URL -->
<a href="{{ url_for('static', filename='css/style.css') }}">CSS文件</a>
<!-- 构建表单action -->
<form action="{{ url_for('login') }}" method="POST">
<!-- 表单内容 -->
</form>
</body>
</html>
from flask import Flask, render_template, request, redirect, url_for, flash
app = Flask(__name__)
app.secret_key = 'secret'
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
username = request.form.get('username')
password = request.form.get('password')
# 验证用户
if username == 'admin' and password == 'password':
flash('登录成功!', 'success')
# POST后重定向到GET,避免刷新时重复提交
return redirect(url_for('dashboard'))
else:
flash('用户名或密码错误', 'error')
# 登录失败时停留在登录页面
return render_template('login.html')
return render_template('login.html')
@app.route('/dashboard')
def dashboard():
return '控制面板'
# 更安全的POST重定向(使用303状态码)
@app.route('/submit-form', methods=['POST'])
def submit_form():
# 处理表单数据...
return redirect(url_for('success_page'), 303)
@app.route('/success')
def success_page():
return '提交成功!'
from flask import Flask, redirect, url_for, request, session, render_template
from functools import wraps
app = Flask(__name__)
app.secret_key = 'super-secret-key'
# 登录装饰器
def login_required(f):
@wraps(f)
def decorated_function(*args, **kwargs):
if 'user_id' not in session:
# 保存用户原本想访问的页面
session['next_url'] = request.url
return redirect(url_for('login'))
return f(*args, **kwargs)
return decorated_function
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
username = request.form.get('username')
password = request.form.get('password')
# 验证用户(这里简化)
if username and password:
session['user_id'] = username
# 重定向到原本要访问的页面或首页
next_url = session.pop('next_url', None)
if next_url:
return redirect(next_url)
return redirect(url_for('dashboard'))
return render_template('login.html')
@app.route('/logout')
def logout():
session.clear()
return redirect(url_for('index'))
@app.route('/dashboard')
@login_required
def dashboard():
return '控制面板(需要登录)'
@app.route('/profile')
@login_required
def profile():
return '个人资料'
@app.route('/')
def index():
return '首页(公开页面)'
from flask import Flask, render_template, redirect, url_for
app = Flask(__name__)
# 自定义错误处理器
@app.errorhandler(404)
def page_not_found(error):
# 可以重定向到自定义404页面
return redirect(url_for('custom_404'))
@app.errorhandler(500)
def internal_error(error):
# 可以重定向到自定义500页面
return redirect(url_for('custom_500'))
@app.route('/404')
def custom_404():
return render_template('404.html'), 404
@app.route('/500')
def custom_500():
return render_template('500.html'), 500
# 或者直接在错误处理器中渲染模板
@app.errorhandler(403)
def forbidden(error):
return render_template('403.html'), 403
from flask import Flask, url_for
app = Flask(__name__)
@app.route('/absolute-url')
def absolute_url_example():
# 生成绝对URL
absolute_url = url_for('index', _external=True)
# 输出: http://localhost:5000/
# 带HTTPS的绝对URL
https_url = url_for('index', _external=True, _scheme='https')
# 输出: https://localhost:5000/
return f"绝对URL: {absolute_url}<br>HTTPS URL: {https_url}"
# 配置应用支持HTTPS重定向
@app.before_request
def before_request():
if request.url.startswith('http://'):
url = request.url.replace('http://', 'https://', 1)
return redirect(url, code=301)
@app.route('/page-with-sections')
def page_with_sections():
return '''
<h1 id="section1">第一部分</h1>
<p>内容...</p>
<h1 id="section2">第二部分</h1>
<p>内容...</p>
<a href="{{ url_for('page_with_sections', _anchor='section1') }}">跳转到第一部分</a>
<a href="{{ url_for('page_with_sections', _anchor='section2') }}">跳转到第二部分</a>
'''
# 生成带锚点的URL
with app.test_request_context():
anchor_url = url_for('page_with_sections', _anchor='comments')
# 输出: /page-with-sections#comments
@app.route('/products')
def products():
page = request.args.get('page', 1, type=int)
category = request.args.get('category', 'all')
sort = request.args.get('sort', 'newest')
# 生成带查询参数的URL
next_page_url = url_for('products',
page=page + 1,
category=category,
sort=sort)
prev_page_url = url_for('products',
page=page - 1 if page > 1 else 1,
category=category,
sort=sort)
return f'''
当前页: {page}<br>
分类: {category}<br>
排序: {sort}<br>
<a href="{next_page_url}">下一页</a>
<a href="{prev_page_url}">上一页</a>
'''
# ❌ 不安全的开放重定向
@app.route('/redirect')
def unsafe_redirect():
url = request.args.get('url', '/')
# 危险!用户可以重定向到任意网站
return redirect(url)
# ✅ 安全的受控重定向
@app.route('/safe-redirect')
def safe_redirect():
page = request.args.get('page', 'home')
# 只允许重定向到预定义的页面
allowed_pages = {
'home': url_for('index'),
'login': url_for('login'),
'dashboard': url_for('dashboard'),
'profile': url_for('profile')
}
target = allowed_pages.get(page, url_for('index'))
return redirect(target)
# ✅ 使用白名单验证外部URL
def is_safe_url(url):
"""检查URL是否安全(只允许重定向到相同主机)"""
from urllib.parse import urlparse, urljoin
from flask import request
ref_url = urlparse(request.host_url)
test_url = urlparse(urljoin(request.host_url, url))
return test_url.scheme in ('http', 'https') and \
ref_url.netloc == test_url.netloc
@app.route('/safe-external-redirect')
def safe_external_redirect():
next_url = request.args.get('next', '/')
if is_safe_url(next_url):
return redirect(next_url)
else:
return redirect(url_for('index'))
# ❌ 可能导致重定向链
@app.route('/old-page')
def old_page():
# 重定向到另一个可能再次重定向的页面
return redirect(url_for('temporary_page'))
@app.route('/temporary-page')
def temporary_page():
# 可能再次重定向
return redirect(url_for('new_page'))
# ✅ 直接重定向到最终目标
@app.route('/old-page-fixed')
def old_page_fixed():
# 直接重定向到最终页面
return redirect(url_for('new_page'))
# 监控重定向链
@app.route('/track-redirect')
def track_redirect():
referrer = request.referrer
if referrer:
# 记录重定向来源
print(f"来自 {referrer} 的重定向")
# 限制重定向深度
redirect_depth = int(request.args.get('depth', 0))
if redirect_depth >= 3:
return '重定向链过长,请直接访问目标页面'
return redirect(url_for('final_page', depth=redirect_depth + 1))
from flask import Flask, render_template, request, redirect, url_for, flash, session
import time
app = Flask(__name__)
app.secret_key = 'registration-example'
# 模拟用户数据库
users = {}
@app.route('/')
def index():
return '<a href="' + url_for('register') + '">注册</a>'
@app.route('/register', methods=['GET', 'POST'])
def register():
if request.method == 'POST':
username = request.form.get('username')
email = request.form.get('email')
password = request.form.get('password')
# 简单验证
if not username or not email or not password:
flash('所有字段都是必填的', 'error')
return redirect(url_for('register'))
if username in users:
flash('用户名已存在', 'error')
return redirect(url_for('register'))
# 保存用户(模拟)
users[username] = {
'email': email,
'password': password,
'created_at': time.time()
}
# 设置会话
session['user_id'] = username
flash('注册成功!', 'success')
# 重定向到验证邮箱页面
return redirect(url_for('verify_email', email=email))
return render_template('register.html')
@app.route('/verify-email/<email>')
def verify_email(email):
# 显示邮箱验证页面
return f'''
<h1>验证您的邮箱</h1>
<p>我们已向 {email} 发送了验证链接</p>
<a href="{url_for('verify_complete')}">我已验证邮箱</a>
'''
@app.route('/verify-complete')
def verify_complete():
if 'user_id' not in session:
return redirect(url_for('login'))
flash('邮箱验证成功!', 'success')
# 重定向到用户资料完善页面
return redirect(url_for('complete_profile'))
@app.route('/complete-profile')
def complete_profile():
if 'user_id' not in session:
return redirect(url_for('login'))
if request.method == 'POST':
# 处理用户资料
flash('资料完善成功!', 'success')
return redirect(url_for('welcome'))
return render_template('complete_profile.html')
@app.route('/welcome')
def welcome():
if 'user_id' not in session:
return redirect(url_for('login'))
user_id = session['user_id']
return f'''
<h1>欢迎,{user_id}!</h1>
<p>您已完成注册流程</p>
<a href="{url_for('dashboard')}">进入控制面板</a>
'''
@app.route('/dashboard')
def dashboard():
if 'user_id' not in session:
return redirect(url_for('login'))
return '控制面板'
@app.route('/logout')
def logout():
session.clear()
flash('您已成功退出', 'info')
return redirect(url_for('index'))
redirect()用于将客户端重定向到新的URL,而url_for()用于生成URL。通常它们一起使用:
# 使用url_for生成URL,然后重定向到该URL
return redirect(url_for('target_page'))
<!-- 在Jinja2模板中 -->
<a href="{{ url_for('user_profile', username='john', page=2) }}">
第2页
</a>
<!-- 在Blade模板中需要使用@转义 -->
<a href="{{ url_for('user_profile', username='john', page=2) }}">
第2页
</a>