在 Flask 框架中,request
是处理客户端请求数据的核心工具。它封装了 HTTP 请求的全部信息,让你能够轻松访问:
- 查询参数(Query Parameters)
- 表单数据(Form Data)
- JSON 载荷(JSON Payload)
- 文件上传(File Uploads)
- 请求头(Headers)
- Cookies
- 客户端 IP
- URL 信息
本文将带你系统性地掌握 request
对象的使用技巧,涵盖基础用法、高级特性、安全实践和最佳模式,助你写出更健壮、更安全、更可维护的 Flask 应用。
一、request
对象基础:全局但线程安全
1.1 导入与基本使用
from flask import Flask, request
app = Flask(__name__)
@app.route('/')
def index():
return f'请求方法: {request.method}, URL: {request.url}'
📌 关键特性:
request
是一个 上下文全局对象(Context-local),在每个请求中独立存在。- 尽管看起来像全局变量,但它是线程安全 的,每个请求都有自己的
request
实例。 - 只能在视图函数或
@before_request
钩子中使用。
1.2 生命周期与上下文
@app.before_request
def log_request_info():
print(f"👉 请求开始: {request.method} {request.url}")
@app.route('/hello')
def hello():
# request 在此可用
return "Hello World!"
@app.after_request
def add_header(resp):
# 仍可访问 request
resp.headers['X-Request-Method'] = request.method
return resp
✅ Flask 使用 Local Proxy 模式实现请求上下文隔离。
二、请求方法与 URL 信息
2.1 常用属性一览
@app.route('/info')
def request_info():
return {
'method': request.method, # GET, POST, PUT, DELETE...
'url': request.url, # 完整URL: https://example.com/info?q=1
'base_url': request.base_url, # 基础URL: https://example.com/info
'path': request.path, # 路径: /info
'host': request.host, # 主机: example.com
'scheme': request.scheme, # 协议: http 或 https
'remote_addr': request.remote_addr, # 客户端IP(可能不准确)
'full_path': request.full_path, # 路径+查询: /info?q=1
'query_string': request.query_string # 查询字符串: b'q=1'
}
2.2 安全获取客户端 IP(处理代理)
def get_client_ip():
"""考虑反向代理(如 Nginx)的情况"""
x_forwarded_for = request.headers.get('X-Forwarded-For')
if x_forwarded_for:
# X-Forwarded-For: client, proxy1, proxy2
return x_forwarded_for.split(',')[0].strip()
x_real_ip = request.headers.get('X-Real-IP')
if x_real_ip:
return x_real_ip
return request.remote_addr or 'unknown'
@app.route('/ip')
def show_ip():
return {'client_ip': get_client_ip()}
✅ 建议在 Nginx 中配置:
深色版本
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
三、查询参数(Query Parameters)
3.1 基本获取
@app.route('/search')
def search():
q = request.args.get('q') # 返回字符串或 None
page = request.args.get('page', 1, type=int) # 默认值 + 类型转换
category = request.args.get('category', 'all')
return f'搜索: {q}, 页码: {page}, 分类: {category}'
3.2 获取所有参数
@app.route('/params')
def all_params():
# 转为字典
all_args = request.args.to_dict()
# 获取多值参数(如 ?tag=python&tag=web)
tags = request.args.getlist('tag')
return {
'all_params': all_args,
'tags': tags
}
3.3 参数验证
@app.route('/validate')
def validated_search():
query = request.args.get('q', '').strip()
if not query:
return {'error': '搜索关键词不能为空'}, 400
page = request.args.get('page', 1, type=int)
if page < 1:
page = 1
limit = min(request.args.get('limit', 20, type=int), 100) # 限制最大值
return {'query': query, 'page': page, 'limit': limit}
四、表单数据(Form Data)
4.1 处理 HTML 表单
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
username = request.form['username'] # KeyError if missing
password = request.form.get('password') # 返回 None if missing
remember = 'remember' in request.form # 复选框是否选中
if not username or not password:
return '用户名和密码不能为空', 400
# 验证逻辑...
return f'欢迎,{username}!'
# GET 请求:返回登录页面
return '''
<form method="post">
<input type="text" name="username" placeholder="用户名" required><br>
<input type="password" name="password" placeholder="密码" required><br>
<input type="checkbox" name="remember"> 记住我<br>
<input type="submit" value="登录">
</form>
'''
4.2 获取所有表单数据
@app.route('/form-data', methods=['POST'])
def form_data():
form_dict = request.form.to_dict()
hobbies = request.form.getlist('hobbies') # 多选框
return {
'form': form_dict,
'hobbies': hobbies
}
4.3 文件上传处理
import os
from werkzeug.utils import secure_filename
UPLOAD_FOLDER = 'uploads'
ALLOWED_EXTENSIONS = {'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'}
def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
if 'file' not in request.files:
return '没有文件上传', 400
file = request.files['file']
if file.filename == '':
return '未选择文件', 400
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
file.save(os.path.join(UPLOAD_FOLDER, filename))
return f'文件 {filename} 上传成功!'
return '''
<form method="post" enctype="multipart/form-data">
<input type="file" name="file" required>
<input type="submit" value="上传">
</form>
'''
✅ secure_filename()
防止路径遍历攻击。
五、JSON 数据处理(API 开发核心)
5.1 处理 JSON 请求
from flask import jsonify
@app.route('/api/user', methods=['POST'])
def create_user():
# 检查 Content-Type
if not request.is_json:
return {'error': 'Content-Type must be application/json'}, 400
# 获取 JSON 数据
data = request.get_json()
if not data:
return {'error': '无效的 JSON 数据'}, 400
name = data.get('name')
email = data.get('email')
age = data.get('age', 0)
if not name or not email:
return {'error': '姓名和邮箱为必填项'}, 400
# 创建用户逻辑...
user = save_user_to_db(name, email, age)
return jsonify({
'message': '用户创建成功',
'user': user
}), 201
5.2 获取原始数据
@app.route('/raw', methods=['POST'])
def raw_data():
raw_bytes = request.get_data()
# 或
stream_data = request.stream.read()
return {
'length': len(raw_bytes),
'type': str(type(raw_bytes)), # <class 'bytes'>
'content': raw_bytes.decode('utf-8', errors='ignore')
}
六、请求头(Headers)
6.1 读取请求头
@app.route('/headers')
def show_headers():
headers = dict(request.headers) # 转为普通字典
user_agent = request.headers.get('User-Agent')
content_type = request.headers.get('Content-Type')
auth = request.headers.get('Authorization')
return {
'user_agent': user_agent,
'content_type': content_type,
'has_auth': bool(auth),
'all_headers': headers
}
6.2 自定义头处理(如 API Key)
@app.route('/api/data')
def api_data():
api_key = request.headers.get('X-API-Key')
if not api_key or api_key != 'your-secret-key-123':
return {'error': '未授权'}, 401
return {'data': '敏感信息'}
七、Cookies 处理
7.1 读取 Cookies
@app.route('/profile')
def profile():
username = request.cookies.get('username')
theme = request.cookies.get('theme', 'light') # 默认值
if not username:
return '请先登录', 302, {'Location': '/login'}
return f'欢迎,{username}!当前主题:{theme}'
7.2 设置 Cookies(需结合响应对象)
from flask import make_response
@app.route('/set-cookie')
def set_cookie():
resp = make_response('Cookie 已设置')
resp.set_cookie('username', 'alice', max_age=3600) # 1小时
resp.set_cookie('theme', 'dark', max_age=3600)
resp.set_cookie('session_id', 'abc123', httponly=True, secure=True) # 安全设置
return resp
✅ 推荐设置 httponly=True
防 XSS,secure=True
(HTTPS 环境)。
八、高级技巧与最佳实践
8.1 请求验证装饰器
from functools import wraps
def validate_json(*required_fields):
def decorator(f):
@wraps(f)
def decorated(*args, **kwargs):
if not request.is_json:
return {'error': 'JSON required'}, 400
data = request.get_json()
if not data:
return {'error': 'Invalid JSON'}, 400
for field in required_fields:
if field not in data:
return {'error': f'缺少字段: {field}'}, 400
return f(*args, **kwargs)
return decorated
return decorator
@app.route('/api/product', methods=['POST'])
@validate_json('name', 'price')
def create_product():
data = request.get_json()
return {'message': '商品创建成功', 'product': data}
8.2 请求预处理(@before_request
)
@app.before_request
def before_request():
app.logger.info(f"📥 {request.method} {request.url}")
# 维护模式
if app.config.get('MAINTENANCE'):
if request.endpoint not in ['maintenance', 'health']:
return '系统维护中...', 503
# API 版本检查
if request.path.startswith('/api/'):
version = request.headers.get('X-API-Version')
if version and version < '2.0':
return {'error': 'API 版本已弃用'}, 410
8.3 处理复杂表单结构
@app.route('/complex', methods=['POST'])
def complex_form():
user_data = {
'name': request.form.get('user.name'),
'email': request.form.get('user.email'),
'profile': {
'age': request.form.get('user.profile.age', type=int),
'city': request.form.get('user.profile.city')
}
}
skills = request.form.getlist('skills') # ['Python', 'Flask']
return {
'user': user_data,
'skills': skills
}
九、request
对象属性速查表
|-----------------------|--------------------------|-------------------------------------|
| 属性/方法 | 说明 | 示例 |
| request.method
| 请求方法 | 'GET'
, 'POST'
|
| request.args
| 查询参数(ImmutableMultiDict) | request.args.get('page')
|
| request.form
| 表单数据 | request.form['name']
|
| request.files
| 上传文件 | request.files['avatar']
|
| request.json
| JSON 数据(只读) | request.json.get('id')
|
| request.get_json()
| 获取 JSON(可 force) | data = request.get_json()
|
| request.headers
| 请求头 | request.headers.get('User-Agent')
|
| request.cookies
| Cookies | request.cookies.get('session')
|
| request.data
| 原始字节数据 | request.get_data()
|
| request.values
| 所有输入(args + form) | request.values.get('search')
|
| request.url
| 完整 URL | https://...?q=test
|
| request.remote_addr
| 客户端 IP | '192.168.1.1'
|
十、安全与最佳实践
✅ 推荐做法
- 使用
get()
而非直接索引(避免KeyError
) - 对用户输入进行验证与清理
- 限制文件上传类型和大小
- 使用
secure_filename()
处理文件名 - 敏感 Cookie 设置
httponly
和secure
- 使用装饰器统一处理验证逻辑
❌ 避免
# ❌ 不安全:未验证
username = request.form['username']
# ❌ 危险:直接拼接文件名
file.save(f"uploads/{request.files['file'].filename}")
# ❌ 不推荐:未处理异常
data = request.get_json(force=True) # 可能抛出 BadRequest
十一、总结
request
对象是 Flask 应用与客户端通信的桥梁。掌握它的各种用法,不仅能提升开发效率,更能增强应用的安全性和健壮性。
🔑 核心要点回顾:
request
是上下文局部对象,线程安全。- 支持多种数据格式:query、form、json、files、headers、cookies。
- 善用
@before_request
和装饰器进行预处理。 - 始终验证输入,防止注入攻击。
- 文件上传需严格校验类型和路径。