Flask 之 Request 对象详解:全面掌握请求数据处理

在 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 设置 httponlysecure
  • 使用装饰器统一处理验证逻辑

❌ 避免

复制代码
# ❌ 不安全:未验证
username = request.form['username']

# ❌ 危险:直接拼接文件名
file.save(f"uploads/{request.files['file'].filename}")

# ❌ 不推荐:未处理异常
data = request.get_json(force=True)  # 可能抛出 BadRequest

十一、总结

request 对象是 Flask 应用与客户端通信的桥梁。掌握它的各种用法,不仅能提升开发效率,更能增强应用的安全性和健壮性。

🔑 核心要点回顾

  1. request 是上下文局部对象,线程安全。
  2. 支持多种数据格式:query、form、json、files、headers、cookies。
  3. 善用 @before_request 和装饰器进行预处理。
  4. 始终验证输入,防止注入攻击。
  5. 文件上传需严格校验类型和路径。
相关推荐
MaxHua18 分钟前
数据库入门指南与实战进阶-Mysql篇
后端
用户40993225021221 分钟前
FastAPI的死信队列处理机制:为何你的消息系统需要它?
后端·ai编程·trae
深盾安全23 分钟前
Python 装饰器详解
python
前端小趴菜0531 分钟前
python - 数据类型转换
python
用户48221371677542 分钟前
C++——纯虚函数、抽象类
后端
张同学的IT技术日记1 小时前
必看!用示例代码学 C++ 基础入门,快速掌握基础知识,高效提升编程能力
后端
跟橙姐学代码1 小时前
学Python必须迈过的一道坎:类和对象到底是什么鬼?
前端·python
林太白1 小时前
Nuxt3 功能篇
前端·javascript·后端
卡洛斯(编程版1 小时前
(1) 哈希表全思路-20天刷完Leetcode Hot 100计划
python·算法·leetcode
得物技术1 小时前
营销会场预览直通车实践|得物技术
后端·架构·测试