Flask 请求与响应新手实战指南

Flask 请求与响应新手实战指南

在 Web 开发中,请求(Request) 代表用户"要什么",响应(Response) 代表服务器"给什么"。理解这二者的交互是掌握 Flask 的基石。本文将手把手带你从零开始,深入剖析 Flask 中请求与响应的每一个细节,并给出可直接落地的代码范例。

WEB项目地址:演示地址

① 开发环境搭建与首个 Flask 应用启动

创建项目与安装依赖

打开终端,执行以下命令:

bash 复制代码
# 创建项目文件夹
mkdir flask_request_demo
cd flask_request_demo

# 安装 Flask
pip install flask

编写最小启动应用

在项目根目录下创建 app.py 文件,写入以下代码:

python 复制代码
from flask import Flask

# 创建 Flask 核心对象
app = Flask(__name__)

# 定义根路由
@app.route('/')
def hello():
    return 'Hello, Flask! 请求与响应实战开始!'

if __name__ == '__main__':
    # host='0.0.0.0' 允许局域网访问,port 指定端口
    app.run(host='0.0.0.0', port=5000, debug=True)

运行 python app.py,在浏览器访问 http://127.0.0.1:5000,看到欢迎语即代表环境就绪。

💡 新手提示debug=True 开启后,修改代码保存即可自动重启服务,无需手动终止重新运行。

② 解析 HTTP 请求对象的核心属性与方法

在 Flask 中,所有来自客户端的信息都封装在全局对象 request 中。使用前需要从 flask 导入。

常用核心属性一览表

属性/方法 作用 使用场景
request.method 获取 HTTP 方法(GET/POST/PUT 等) 判断请求类型
request.url 完整的请求 URL 日志记录、重定向来源
request.path URL 中的路径部分(不含域名) 路由匹配
request.args 获取 URL 查询字符串参数(?key=value GET 请求参数
request.form 获取 POST 请求的表单数据 登录、注册表单
request.json / request.get_json() 获取 JSON 格式的请求体 API 接口开发
request.files 获取上传的文件对象 头像、附件上传
request.headers 获取请求头信息(字典形式) 认证 Token、User-Agent
request.cookies 获取客户端携带的 Cookie 记住登录状态
request.data 获取原始请求体字节数据 处理非表单、非 JSON 的原始数据

实战演示:打印请求详情

创建 demo_request.py

python 复制代码
from flask import Flask, request

app = Flask(__name__)

@app.route('/debug', methods=['GET', 'POST'])
def debug_request():
    # 1. 获取请求方法
    method = request.method
    
    # 2. 获取完整 URL 和路径
    full_url = request.url
    path = request.path
    
    # 3. 获取请求头中的 User-Agent(浏览器信息)
    user_agent = request.headers.get('User-Agent')
    
    # 4. 获取客户端 IP
    client_ip = request.remote_addr
    
    # 5. 获取查询参数(GET 传参)
    args = request.args.to_dict()  # 转换为普通字典
    
    return f"""
    <h3>请求调试信息</h3>
    <p>方法: {method}</p>
    <p>路径: {path}</p>
    <p>完整 URL: {full_url}</p>
    <p>User-Agent: {user_agent}</p>
    <p>客户端 IP: {client_ip}</p>
    <p>查询参数: {args}</p>
    """

访问 http://127.0.0.1:5000/debug?name=zhang&age=25,你会看到页面动态展示了所有请求细节。

⚠️ 重要request 在视图函数外部无法使用,它只在当前请求上下文中生效。

③ 构建多样化响应内容与状态码设置

视图函数的返回值就是响应对象。Flask 支持多种响应构建方式。

方式一:直接返回字符串(最常用)

python 复制代码
@app.route('/text')
def return_text():
    # 默认状态码 200,Content-Type 为 text/html
    return '<h1>这是一段 HTML 文本</h1>'

方式二:返回元组(带状态码和响应头)

python 复制代码
@app.route('/created')
def return_created():
    # 返回 (内容, 状态码, 响应头字典)
    return '用户创建成功', 201, {'Location': '/users/1'}

方式三:使用 make_response() 构建复杂响应

当需要操作 Cookie 或自定义响应头时,推荐使用 make_response

python 复制代码
from flask import make_response

@app.route('/custom_response')
def custom_response():
    # 1. 创建响应对象
    resp = make_response('<h2>这是一个自定义响应</h2>')
    
    # 2. 设置状态码(默认为 200)
    resp.status_code = 202
    
    # 3. 设置自定义响应头
    resp.headers['X-Server-Name'] = 'Flask-Demo'
    resp.headers['Cache-Control'] = 'no-cache'
    
    # 4. 设置 Cookie(有效期 7 天)
    resp.set_cookie('user_preference', 'dark_mode', max_age=7*24*3600)
    
    return resp

常见的 HTTP 状态码

状态码 含义 使用场景
200 OK(成功) 请求处理成功
201 Created(已创建) 新增数据成功(如注册)
204 No Content(无内容) 删除操作成功
301/302 重定向 页面跳转
400 Bad Request(错误请求) 参数校验不通过
401 Unauthorized(未授权) 未登录访问需权限页面
404 Not Found(未找到) 资源不存在
500 Internal Server Error(内部错误) 服务器代码异常

返回 JSON 数据(前后端分离必备)

使用 Flask 内置的 jsonify() 函数,自动设置正确的 Content-Type: application/json

python 复制代码
from flask import jsonify

@app.route('/api/user/<int:uid>')
def api_user(uid):
    user_data = {
        'id': uid,
        'name': '王小明',
        'email': 'xm.wang@example.com'
    }
    return jsonify(user_data)  # 自动转为 JSON 字符串并返回

④ 处理表单数据与文件上传的完整流程

4.1 处理普通表单数据(POST 请求)

先创建一个包含表单的 HTML 页面(放在 templates/form.html):

html 复制代码
<!DOCTYPE html>
<html>
<body>
    <h2>用户注册</h2>
    <form action="/register" method="POST">
        <label>用户名:</label><input type="text" name="username"><br>
        <label>密码:</label><input type="password" name="password"><br>
        <label>年龄:</label><input type="number" name="age"><br>
        <input type="submit" value="提交">
    </form>
</body>
</html>

后端处理逻辑:

python 复制代码
from flask import Flask, request, render_template

app = Flask(__name__)

@app.route('/register', methods=['GET', 'POST'])
def register():
    if request.method == 'GET':
        # GET 请求:显示表单页面
        return render_template('form.html')
    
    # POST 请求:处理表单提交
    # 使用 .get() 安全获取,避免键不存在时报错
    username = request.form.get('username', '游客')
    password = request.form.get('password', '')
    age = request.form.get('age', 0)
    
    # 简单校验
    if not username or len(username) < 3:
        return '用户名至少 3 个字符', 400
    
    return f'注册成功!欢迎 {username},年龄 {age}'

4.2 文件上传实战

创建一个文件上传表单(templates/upload.html):

html 复制代码
<form action="/upload" method="POST" enctype="multipart/form-data">
    <input type="file" name="avatar">
    <input type="submit" value="上传头像">
</form>

后端上传处理(安全注意事项见代码注释):

python 复制代码
import os
from werkzeug.utils import secure_filename

# 配置上传文件夹和允许的扩展名
UPLOAD_FOLDER = 'uploads'
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'}

app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.config['MAX_CONTENT_LENGTH'] = 2 * 1024 * 1024  # 限制文件最大为 2MB

# 确保上传目录存在
os.makedirs(UPLOAD_FOLDER, exist_ok=True)

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 == 'GET':
        return render_template('upload.html')
    
    # 检查是否有文件被上传
    if 'avatar' not in request.files:
        return '没有找到文件字段', 400
    
    file = request.files['avatar']
    
    # 如果用户未选择文件,file.filename 为空字符串
    if file.filename == '':
        return '未选择任何文件', 400
    
    # 校验文件扩展名
    if not allowed_file(file.filename):
        return '仅支持 png, jpg, jpeg, gif 格式', 400
    
    # 安全处理文件名(防止恶意路径攻击)
    safe_filename = secure_filename(file.filename)
    
    # 保存文件到 uploads 文件夹
    save_path = os.path.join(app.config['UPLOAD_FOLDER'], safe_filename)
    file.save(save_path)
    
    return f'文件上传成功!保存为:{save_path}'

🔒 安全核心secure_filename() 会过滤掉 ../ 等危险字符,务必使用!同时必须限制 MAX_CONTENT_LENGTH 防止超大文件耗尽服务器磁盘。

⑤ 实现动态路由参数提取与类型转换

动态路由参数是 URL 的一部分(如 /user/1001 中的 1001),它们通过视图函数的形参 直接传入,与查询字符串(?key=val)有本质区别。

路由参数与查询字符串对比

对比项 路由参数(Path Params) 查询字符串(Query String)
位置 写在 URL 路径中 写在 ? 之后
示例 /user/1001 /user?id=1001
获取方式 视图函数形参 def user(user_id) request.args.get('id')
适用场景 资源定位(如 RESTful API) 过滤、排序、分页

类型转换器详解

Flask 内置的转换器让参数自动变成指定类型,省去手动转换的麻烦。

python 复制代码
@app.route('/user/<int:user_id>')
def get_user(user_id):
    # user_id 已经是 int 类型,可以直接参与运算
    return f'查询到用户 ID:{user_id},类型是 {type(user_id).__name__}'

@app.route('/product/<string:product_code>')
def get_product(product_code):
    # 默认就是 string,可省略
    return f'商品编码:{product_code}'

@app.route('/path/<path:sub_path>')
def get_sub_path(sub_path):
    # path 可以匹配斜杠,如 /path/a/b/c 会得到 'a/b/c'
    return f'子路径:{sub_path}'

@app.route('/float/<float:price>')
def get_price(price):
    return f'价格:{price:.2f}'

同时使用路由参数和查询参数

python 复制代码
@app.route('/orders/<int:order_id>')
def get_orders(order_id):
    # 路由参数获取订单 ID
    # 查询参数获取分页信息
    page = request.args.get('page', default=1, type=int)
    size = request.args.get('size', default=10, type=int)
    
    return f'订单 {order_id} 的第 {page} 页,每页 {size} 条'

访问 /orders/888?page=3&size=5 会得到:订单 888 的第 3 页,每页 5 条

⑥ 自定义错误页面与异常捕获机制

在生产环境中,默认的白色错误页面对用户极不友好。Flask 允许我们全局拦截并美化错误。

主动终止请求并抛出错误

使用 abort() 函数可以随时中断请求并返回指定状态码:

python 复制代码
from flask import abort

@app.route('/dashboard')
def dashboard():
    # 模拟检查登录状态(此处假设未登录)
    is_logged_in = False
    if not is_logged_in:
        # 返回 401 并终止执行
        abort(401, description='请先登录再访问仪表盘')
    return '这是仪表盘'

全局捕获并自定义错误页面

通过 @app.errorhandler(状态码) 装饰器统一处理:

python 复制代码
from flask import render_template

# 捕获 404 错误
@app.errorhandler(404)
def not_found_error(error):
    # 返回自定义 HTML 页面,状态码必须明确为 404
    return render_template('404.html'), 404

# 捕获 500 内部服务器错误
@app.errorhandler(500)
def internal_error(error):
    return render_template('500.html'), 500

# 捕获 401 未授权
@app.errorhandler(401)
def unauthorized_error(error):
    # 也可以返回 JSON 格式(适用于前后端分离)
    return jsonify({'code': 401, 'msg': str(error)}), 401

捕获任意异常(兜底方案)

如果要捕获所有未预料的异常,可以这样做:

python 复制代码
@app.errorhandler(Exception)
def handle_all_exceptions(error):
    # 记录错误日志(此处省略日志代码)
    return '系统维护中,请稍后再试', 500

⚠️ 注意render_template() 后必须加上状态码(如 , 404),否则 Flask 会默认返回 200。

⑦ 使用重定向与会话管理优化用户交互

7.1 重定向(Redirect)

redirect() 用于将用户跳转到其他页面,url_for() 根据函数名反向生成 URL,二者常搭配使用。

python 复制代码
from flask import redirect, url_for

@app.route('/')
def index():
    # 重定向到 /login 页面
    return redirect(url_for('login_page'))

@app.route('/login')
def login_page():
    return '这里是登录页面,请登录'

url_for() 也支持传递动态路由参数:

python 复制代码
@app.route('/profile/<int:uid>')
def profile(uid):
    return f'用户 {uid} 的个人主页'

@app.route('/go_profile')
def go_profile():
    # 生成 /profile/1001 的 URL
    target_url = url_for('profile', uid=1001)
    return redirect(target_url)

7.2 会话管理(Session)

Session 服务端存储用户数据的机制,在 Flask 中通过加密 Cookie 实现,使用前必须配置 SECRET_KEY

基础用法
python 复制代码
from flask import session
import os

# 配置秘钥(务必使用随机字符串,切勿硬编码在代码中提交到 Git)
app.secret_key = os.urandom(24)  # 开发环境
# 生产环境应从环境变量读取:app.secret_key = os.getenv('SECRET_KEY')
实现"记住登录状态"完整示例
python 复制代码
@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 == '123456':
            # 将用户信息写入 Session
            session['user_id'] = 10001
            session['username'] = username
            # 设置永久有效期(默认 31 天)
            session.permanent = True
            return redirect(url_for('dashboard'))
        else:
            return '用户名或密码错误', 400
    
    # GET 请求展示登录页
    return render_template('login.html')

@app.route('/dashboard')
def dashboard():
    # 检查 Session 中是否有用户信息
    if 'username' in session:
        return f'欢迎回来,{session["username"]}'
    else:
        return redirect(url_for('login'))

@app.route('/logout')
def logout():
    # 清除 Session 中的用户数据(移除特定键)
    session.pop('username', None)
    session.pop('user_id', None)
    # 或者清除整个 Session:session.clear()
    return redirect(url_for('login'))

🔐 安全提示

  1. SECRET_KEY 必须足够复杂且保密,生产环境建议使用环境变量。
  2. 永远不要在前端 Session 中存储密码等敏感信息。

⑧ 常见请求报错分析与快速排查技巧

报错 1:请求数据获取失败(BadRequestKeyError

现象BadRequestKeyError: 'key_name' not found in request

原因 :直接使用索引方式获取参数(如 request.form['username']),但该键不存在。

解决方案 :统一使用 .get() 方法,并设置合理的默认值。

python 复制代码
# ❌ 错误写法(键不存在会报错)
username = request.form['username']

# ✅ 正确写法(键不存在返回 None 或默认值)
username = request.form.get('username')
username = request.form.get('username', 'default_user')

报错 2:HTTP 405 方法不允许

现象Method Not Allowed. The method is not allowed for the requested URL.

原因 :路由未在 methods 参数中包含当前请求的方法。

解决方案:显式声明所有可能用到的方法。

python 复制代码
# ❌ 只能处理 GET,无法处理 POST
@app.route('/submit')
def submit():
    pass

# ✅ 明确允许 GET 和 POST
@app.route('/submit', methods=['GET', 'POST'])
def submit():
    pass

报错 3:文件上传超出大小限制(RequestEntityTooLarge

现象413 Request Entity Too Large

原因 :上传的文件超过了 MAX_CONTENT_LENGTH 配置值。

解决方案

  1. 调大配置(视业务需求而定)
  2. 前端增加文件大小校验,提前拦截用户
python 复制代码
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024  # 调整为 16MB

报错 4:JSON 解析失败(BadRequest

现象Failed to decode JSON object: Expecting value: line 1 column 1 (char 0)

原因 :客户端发送的请求体不是合法的 JSON 格式,或忘记设置 Content-Type: application/json

解决方案 :使用 try...except 优雅处理,避免程序崩溃。

python 复制代码
@app.route('/api/data', methods=['POST'])
def handle_json():
    try:
        data = request.get_json()
        if data is None:
            return jsonify({'error': '请求体不是 JSON 格式'}), 400
        # 正常处理逻辑
        return jsonify({'status': 'ok'})
    except Exception as e:
        return jsonify({'error': f'JSON 解析失败: {str(e)}'}), 400

快速调试三板斧

  1. 打印到终端 :在代码中插入 print(request.method, request.args, request.form),查看控制台输出。

  2. 使用 Postman/cURL 模拟请求

    bash 复制代码
    curl -X POST http://localhost:5000/upload -F "avatar=@local_image.jpg"
  3. 查看 Flask 内置的异常堆栈 :开启 debug=True 后,访问出错页面会显示详细的彩色错误追踪,直接指出哪一行代码出错。


总结

本文通过 8 个章节,系统梳理了 Flask 中请求与响应的全链路知识:

  • 请求对象 :掌握了 requestargsformjsonfiles 等核心属性
  • 响应构建 :学会了字符串、元组、make_responsejsonify 多种返回方式
  • 动态路由:区分了路由参数与查询参数的差异,灵活使用类型转换器
  • 文件与表单:落地了安全上传文件、表单校验的完整方案
  • 错误与重定向:实现了优雅的自定义错误页面、登录跳转与会话保持

请求与响应是前后端交互的"语言"。掌握好它们,你就拥有了开发任何 Web 功能的基础能力。建议你亲手敲一遍本文的全部代码,体验从接收请求到返回响应的完整闭环。祝你学习顺利!🚀

相关推荐
我不是AI1 小时前
Codex 装好了却用不了?API Key 与 KKFlow 配置精简教程
后端
程序员cxuan2 小时前
A 社官方:我们删掉了 80% 的 skills
人工智能·后端·程序员
苍何2 小时前
AI 短剧出海,门槛已经低到离谱了
后端
程序员黑豆2 小时前
鸿蒙应用开发:@Link 装饰器实现父子组件双向同步
前端·后端·harmonyos
huahailing10242 小时前
Spring Boot 集成 XXL-Job 完整实现方案(支持动态CRUD)
java·spring boot·后端
顶级自由人2 小时前
【前端菜鸟的补课01】Zod 与 PostgreSQL 全栈数据工程教学
前端·后端·程序员
swipe3 小时前
11|(前端转全栈)购物车不能只存在前端:用户维度数据如何在后端落库
前端·后端·全栈
XS0301064 小时前
Spring框架
java·后端·spring
xcLeigh4 小时前
Go入门:main包与main函数的特殊地位
开发语言·后端·golang