当前位置: 首页 > news >正文

第4课:Flask请求与响应对象深度解析

📋 目录

  • 课程目标
  • Flask 请求与响应机制
  • request 对象详解
  • 处理不同类型的请求数据
  • response 对象与响应类型
  • 错误处理与状态码
  • 完整实战示例
  • 课后练习
  • 学习小结与下节预告

📘 课程目标

通过本课学习,你将:

  • 深入理解 Flask 的请求-响应处理机制
  • 熟练使用 request 对象获取各种类型的请求数据
  • 掌握 response 对象的构造与自定义
  • 学会处理 URL 参数、表单数据、JSON 数据和文件上传
  • 掌握返回不同类型响应的最佳实践
  • 理解 HTTP 状态码与错误处理

🔄 Flask 请求与响应机制

Flask 作为 Web 框架,其核心是处理 HTTP 请求并返回响应。理解这个流程对掌握 Flask 至关重要:

用户发起请求 → Flask 路由匹配 → 视图函数处理 → 构造响应 → 返回给用户

Flask 提供了强大的 requestresponse 对象来简化这个过程。


🔍 request 对象详解

request 是 Flask 提供的全局代理对象,封装了当前 HTTP 请求的所有信息。

from flask import Flask, requestapp = Flask(__name__)

核心属性与方法一览

分类属性/方法说明示例
基本信息request.methodHTTP 方法GET, POST, PUT
request.path请求路径/api/users
request.url完整 URLhttp://localhost:5000/api/users?id=1
request.remote_addr客户端 IP127.0.0.1
参数获取request.argsURL 查询参数?name=flask&version=2.0
request.form表单数据POST 表单提交的数据
request.get_json()JSON 数据API 请求的 JSON 载荷
request.files文件上传文件对象字典
请求头request.headers所有请求头{'Content-Type': 'application/json'}
request.content_type内容类型application/json
原始数据request.data原始请求体二进制数据
request.get_data()请求体数据解码后的数据

📥 处理不同类型的请求数据

1. 处理 URL 查询参数(GET 请求)

URL 查询参数常用于搜索、筛选和分页功能。

@app.route('/search')
def search():"""搜索功能示例"""keyword = request.args.get('q', '')  # 获取搜索关键词,默认为空字符串page = request.args.get('page', 1, type=int)  # 获取页码,默认为1,转换为整数per_page = request.args.get('per_page', 10, type=int)  # 每页数量if not keyword:return "请输入搜索关键词", 400return f"""<h2>搜索结果</h2><p>关键词:{keyword}</p><p>第 {page} 页,每页 {per_page} 条</p><p>搜索URL:{request.url}</p>"""# 示例访问:/search?q=flask&page=2&per_page=20

2. 处理表单数据(POST 请求)

HTML 表单提交是 Web 应用最常见的数据交互方式。

@app.route('/register', methods=['GET', 'POST'])
def register():"""用户注册示例"""if request.method == 'GET':# 返回注册表单return '''<h2>用户注册</h2><form method="post"><p>用户名:<input type="text" name="username" required></p><p>邮箱:<input type="email" name="email" required></p><p>年龄:<input type="number" name="age" min="1" max="120"></p><p>性别:<input type="radio" name="gender" value="男"> 男<input type="radio" name="gender" value="女"> 女</p><p>兴趣爱好:<input type="checkbox" name="hobbies" value="编程"> 编程<input type="checkbox" name="hobbies" value="运动"> 运动<input type="checkbox" name="hobbies" value="音乐"> 音乐</p><p><button type="submit">注册</button></p></form>'''else:# 处理表单提交username = request.form.get('username')email = request.form.get('email')age = request.form.get('age', type=int)gender = request.form.get('gender')hobbies = request.form.getlist('hobbies')  # 获取多选框的值# 简单验证if not username or not email:return "用户名和邮箱不能为空", 400return f"""<h2>注册成功!</h2><p>用户名:{username}</p><p>邮箱:{email}</p><p>年龄:{age}</p><p>性别:{gender}</p><p>兴趣爱好:{', '.join(hobbies) if hobbies else '无'}</p>"""

3. 处理 JSON 数据(API 开发)

JSON 是现代 Web API 的标准数据格式。

from flask import jsonify@app.route('/api/users', methods=['POST'])
def create_user():"""创建用户 API"""# 检查请求内容类型if not request.is_json:return jsonify({'error': '请求必须是 JSON 格式'}), 400data = request.get_json()# 验证必需字段required_fields = ['username', 'email']for field in required_fields:if field not in data:return jsonify({'error': f'缺少必需字段:{field}'}), 400# 模拟创建用户user = {'id': 123,'username': data['username'],'email': data['email'],'age': data.get('age'),'created_at': '2024-12-28 10:00:00'}return jsonify({'message': '用户创建成功','user': user}), 201@app.route('/api/users/<int:user_id>', methods=['PUT'])
def update_user(user_id):"""更新用户信息 API"""data = request.get_json()if not data:return jsonify({'error': '请提供要更新的数据'}), 400# 模拟更新操作updated_user = {'id': user_id,'username': data.get('username', '原用户名'),'email': data.get('email', '原邮箱'),'updated_at': '2024-12-28 10:30:00'}return jsonify({'message': '用户更新成功','user': updated_user})

4. 处理文件上传

import os
from werkzeug.utils import secure_filename@app.route('/upload', methods=['GET', 'POST'])
def upload_file():"""文件上传示例"""if request.method == 'GET':return '''<h2>文件上传</h2><form method="post" enctype="multipart/form-data"><p>选择文件:<input type="file" name="file" required></p><p>文件描述:<input type="text" name="description"></p><p><button type="submit">上传</button></p></form>'''if 'file' not in request.files:return '未选择文件', 400file = request.files['file']description = request.form.get('description', '')if file.filename == '':return '未选择文件', 400if file:# 安全的文件名处理filename = secure_filename(file.filename)return f"""<h2>文件上传成功!</h2><p>文件名:{filename}</p><p>文件大小:{len(file.read())} 字节</p><p>文件类型:{file.content_type}</p><p>描述:{description}</p>"""

📤 response 对象与响应类型

Flask 视图函数可以返回多种类型的响应,框架会自动处理或手动构造 Response 对象。

1. 字符串响应(最简单)

@app.route('/')
def home():return "欢迎来到 Flask 学习之旅!"@app.route('/html')
def html_response():return """<h1>HTML 响应</h1><p>这是一个 <strong>HTML</strong> 响应示例。</p>"""

2. JSON 响应(API 开发推荐)

@app.route('/api/status')
def api_status():"""API 状态检查"""return jsonify({'status': 'success','message': 'API 服务正常运行','version': '1.0.0','timestamp': '2024-12-28 10:00:00'})@app.route('/api/error-demo')
def api_error():"""API 错误响应示例"""return jsonify({'status': 'error','message': '权限不足','error_code': 'PERMISSION_DENIED'}), 403

3. 重定向响应

from flask import redirect, url_for@app.route('/old-page')
def old_page():"""重定向到新页面"""return redirect(url_for('new_page'))@app.route('/new-page')
def new_page():return "<h1>这是新页面</h1><p>您已被重定向到这里。</p>"@app.route('/external-redirect')
def external_redirect():"""重定向到外部网站"""return redirect('https://flask.palletsprojects.com/')

4. 自定义响应对象

from flask import Response, make_response@app.route('/custom-response')
def custom_response():"""自定义响应示例"""content = "这是自定义响应内容"response = Response(content,status=200,headers={'X-Custom-Header': 'Flask-Demo','Content-Type': 'text/plain; charset=utf-8'})return response@app.route('/cookie-demo')
def cookie_demo():"""设置 Cookie 示例"""resp = make_response("Cookie 已设置")resp.set_cookie('username', 'flask_user', max_age=60*60*24)  # 24小时有效return resp

⚠️ 错误处理与状态码

合适的错误处理是 Web 应用的重要组成部分。

@app.route('/api/users/<int:user_id>')
def get_user(user_id):"""获取用户信息(含错误处理)"""# 模拟用户数据users = {1: '张三', 2: '李四', 3: '王五'}if user_id not in users:return jsonify({'error': '用户不存在','user_id': user_id}), 404return jsonify({'user_id': user_id,'username': users[user_id],'status': 'active'})@app.errorhandler(404)
def not_found(error):"""全局 404 错误处理"""if request.path.startswith('/api/'):# API 路径返回 JSON 错误return jsonify({'error': '接口不存在'}), 404else:# 普通路径返回 HTML 错误页面return f"""<h1>页面未找到</h1><p>您访问的页面 <code>{request.path}</code> 不存在。</p><a href="/">返回首页</a>""", 404@app.errorhandler(500)
def internal_error(error):"""全局 500 错误处理"""return jsonify({'error': '服务器内部错误'}), 500

🚀 完整实战示例

下面是一个综合运用请求与响应处理的完整示例:

from flask import Flask, request, jsonify, redirect, url_for, make_response
import json
from datetime import datetimeapp = Flask(__name__)# 模拟数据存储
users = []
next_user_id = 1@app.route('/')
def index():"""首页 - 展示所有功能"""return f"""<h1>🎓 Flask 请求与响应示例</h1><h2>功能导航</h2><ul><li><a href="{url_for('search')}?q=flask&page=1">搜索示例</a></li><li><a href="{url_for('register')}">用户注册</a></li><li><a href="{url_for('upload_file')}">文件上传</a></li><li><a href="{url_for('api_status')}">API 状态</a></li><li><a href="{url_for('get_users')}">用户列表 API</a></li></ul><h2>API 测试</h2><p>使用 Postman 或 curl 测试以下 API:</p><ul><li><strong>POST</strong> /api/users - 创建用户</li><li><strong>GET</strong> /api/users - 获取用户列表</li><li><strong>PUT</strong> /api/users/&lt;id&gt; - 更新用户</li><li><strong>DELETE</strong> /api/users/&lt;id&gt; - 删除用户</li></ul>"""# ... (前面的搜索、注册、上传功能保持不变) ...@app.route('/api/users', methods=['GET', 'POST'])
def users_api():"""用户管理 API"""global next_user_idif request.method == 'GET':# 获取用户列表,支持分页page = request.args.get('page', 1, type=int)per_page = request.args.get('per_page', 10, type=int)start = (page - 1) * per_pageend = start + per_pagepaginated_users = users[start:end]return jsonify({'users': paginated_users,'total': len(users),'page': page,'per_page': per_page,'pages': (len(users) + per_page - 1) // per_page})elif request.method == 'POST':# 创建新用户if not request.is_json:return jsonify({'error': '请求必须是 JSON 格式'}), 400data = request.get_json()# 验证必需字段if not data.get('username') or not data.get('email'):return jsonify({'error': '用户名和邮箱不能为空'}), 400# 检查用户名是否已存在if any(u['username'] == data['username'] for u in users):return jsonify({'error': '用户名已存在'}), 409# 创建用户user = {'id': next_user_id,'username': data['username'],'email': data['email'],'age': data.get('age'),'created_at': datetime.now().isoformat()}users.append(user)next_user_id += 1response = make_response(jsonify({'message': '用户创建成功','user': user}), 201)response.headers['Location'] = f'/api/users/{user["id"]}'return response@app.route('/api/users/<int:user_id>', methods=['GET', 'PUT', 'DELETE'])
def user_detail_api(user_id):"""单个用户操作 API"""# 查找用户user = next((u for u in users if u['id'] == user_id), None)if not user:return jsonify({'error': '用户不存在'}), 404if request.method == 'GET':return jsonify({'user': user})elif request.method == 'PUT':if not request.is_json:return jsonify({'error': '请求必须是 JSON 格式'}), 400data = request.get_json()# 更新用户信息if 'username' in data:user['username'] = data['username']if 'email' in data:user['email'] = data['email']if 'age' in data:user['age'] = data['age']user['updated_at'] = datetime.now().isoformat()return jsonify({'message': '用户更新成功','user': user})elif request.method == 'DELETE':users.remove(user)return '', 204  # No Contentif __name__ == '__main__':print("🚀 Flask 应用启动中...")print("📱 访问地址:http://127.0.0.1:5000")app.run(debug=True, host='127.0.0.1', port=5000)

📝 课后练习

🎯 基础练习

练习 1:表单数据处理增强版

题目:
创建一个 /contact 路由,支持 GET(显示联系表单)和 POST(处理表单数据)。表单包含:姓名、邮箱、主题、消息内容。POST 处理时需要验证所有字段不为空,并返回美观的确认页面。

答案:

@app.route('/contact', methods=['GET', 'POST'])
def contact():if request.method == 'GET':return '''<h2>联系我们</h2><form method="post" style="max-width: 400px;"><p><label>姓名:</label><br><input type="text" name="name" required style="width: 100%; padding: 5px;"></p><p><label>邮箱:</label><br><input type="email" name="email" required style="width: 100%; padding: 5px;"></p><p><label>主题:</label><br><input type="text" name="subject" required style="width: 100%; padding: 5px;"></p><p><label>消息内容:</label><br><textarea name="message" required style="width: 100%; height: 100px; padding: 5px;"></textarea></p><p><button type="submit" style="background: #007bff; color: white; padding: 10px 20px; border: none;">发送消息</button></p></form>'''else:name = request.form.get('name')email = request.form.get('email')subject = request.form.get('subject')message = request.form.get('message')# 验证必填字段required_fields = {'姓名': name, '邮箱': email, '主题': subject, '消息内容': message}missing_fields = [field for field, value in required_fields.items() if not value]if missing_fields:return f"<h2>错误</h2><p>以下字段不能为空:{', '.join(missing_fields)}</p>", 400return f'''<h2>✅ 消息发送成功!</h2><div style="border: 1px solid #ddd; padding: 20px; max-width: 500px;"><p><strong>姓名:</strong>{name}</p><p><strong>邮箱:</strong>{email}</p><p><strong>主题:</strong>{subject}</p><p><strong>消息内容:</strong></p><div style="background: #f8f9fa; padding: 10px; border-left: 3px solid #007bff;">{message.replace(chr(10), '<br>')}</div><p style="color: #666; margin-top: 20px;">我们会尽快回复您的消息。</p></div>'''
练习 2:智能计算器 API

题目:
实现 /api/calc 路由,支持 GET 和 POST 请求。GET 时从 URL 参数获取操作数和运算符,POST 时从 JSON 获取。支持加减乘除运算,需要完整的错误处理。

答案:

@app.route('/api/calc', methods=['GET', 'POST'])
def calculator_api():if request.method == 'GET':# 从 URL 参数获取数据try:a = float(request.args.get('a', 0))b = float(request.args.get('b', 0))operation = request.args.get('op', 'add')except ValueError:return jsonify({'error': '参数必须是有效数字'}), 400else:# 从 JSON 获取数据if not request.is_json:return jsonify({'error': '请求必须是 JSON 格式'}), 400data = request.get_json()try:a = float(data.get('a', 0))b = float(data.get('b', 0))operation = data.get('operation', 'add')except (ValueError, TypeError):return jsonify({'error': '参数必须是有效数字'}), 400# 执行计算operations = {'add': ('+', lambda x, y: x + y),'subtract': ('-', lambda x, y: x - y),'multiply': ('×', lambda x, y: x * y),'divide': ('÷', lambda x, y: x / y if y != 0 else None)}if operation not in operations:return jsonify({'error': f'不支持的运算类型:{operation}','supported_operations': list(operations.keys())}), 400symbol, calc_func = operations[operation]result = calc_func(a, b)if result is None:return jsonify({'error': '除数不能为零'}), 400return jsonify({'operands': {'a': a, 'b': b},'operation': operation,'expression': f'{a} {symbol} {b} = {result}','result': result,'timestamp': datetime.now().isoformat()})
练习 3:多功能数据处理

题目:
创建 /api/data 路由,根据 Content-Type 自动处理不同格式的数据:

  • application/json:处理 JSON 数据
  • application/x-www-form-urlencoded:处理表单数据
  • text/plain:处理纯文本数据

答案:

@app.route('/api/data', methods=['POST'])
def process_data():content_type = request.content_typeif content_type == 'application/json':data = request.get_json()return jsonify({'data_type': 'JSON','content_type': content_type,'received_data': data,'data_keys': list(data.keys()) if isinstance(data, dict) else None})elif content_type == 'application/x-www-form-urlencoded':data = dict(request.form)return jsonify({'data_type': 'Form Data','content_type': content_type,'received_data': data,'form_fields': list(data.keys())})elif content_type == 'text/plain':data = request.get_data(as_text=True)return jsonify({'data_type': 'Plain Text','content_type': content_type,'text_content': data,'text_length': len(data),'word_count': len(data.split()) if data else 0})else:return jsonify({'error': f'不支持的内容类型:{content_type}','supported_types': ['application/json','application/x-www-form-urlencoded', 'text/plain']}), 415

🧠 进阶思考题

  1. 请求中间件:如何实现一个中间件来记录所有请求的详细信息?
  2. 响应缓存:如何为某些 API 响应添加缓存控制头?
  3. 内容协商:如何根据客户端的 Accept 头返回不同格式的响应?

参考答案:

  1. 请求日志中间件:
@app.before_request
def log_request_info():app.logger.info(f"""请求信息:- 方法: {request.method}- URL: {request.url}- IP: {request.remote_addr}- User-Agent: {request.headers.get('User-Agent')}- Content-Type: {request.content_type}""")@app.after_request
def log_response_info(response):app.logger.info(f"响应状态码: {response.status_code}")return response
  1. 响应缓存控制:
@app.route('/api/cached-data')
def cached_data():data = {'timestamp': datetime.now().isoformat(), 'data': 'cached content'}response = make_response(jsonify(data))response.cache_control.max_age = 300  # 缓存5分钟response.cache_control.public = Truereturn response
  1. 内容协商:
@app.route('/api/flexible')
def flexible_response():data = {'message': 'Hello, World!', 'timestamp': datetime.now().isoformat()}accept_header = request.headers.get('Accept', '')if 'application/json' in accept_header:return jsonify(data)elif 'text/html' in accept_header:return f"<h1>{data['message']}</h1><p>时间:{data['timestamp']}</p>"elif 'text/plain' in accept_header:return f"{data['message']}\n时间:{data['timestamp']}"else:return jsonify(data)  # 默认返回 JSON

🔎 知识点总结

知识点核心概念实际应用
request 对象封装 HTTP 请求信息获取用户输入、文件上传、API 参数
URL 参数request.args.get()搜索、筛选、分页功能
表单数据request.form.get()用户注册、登录、数据提交
JSON 数据request.get_json()RESTful API、前后端分离
文件上传request.files头像上传、文档管理
响应类型字符串、JSON、重定向页面展示、API 返回、页面跳转
错误处理状态码、错误页面用户体验、API 规范

在这里插入图片描述
在这里插入图片描述

🚀 学习小结与下节预告

本课收获

通过本课学习,你已经掌握了:

  • Flask 请求-响应处理的完整流程
  • 各种类型请求数据的获取方法
  • 不同响应类型的构造技巧
  • 错误处理和状态码的最佳实践
  • 实际 Web 应用开发的核心技能

下节课预告

下节课我们将学习:

  • Flask 会话管理(Session):用户登录状态保持
  • Cookie 操作:客户端数据存储
  • Flash 消息:页面间消息传递
  • 用户认证系统:登录、注册、权限控制

💡 学习建议

  1. 多练习:用 Postman 测试不同类型的 API 请求
  2. 多思考:每种数据类型适用于什么场景?
  3. 多探索:尝试组合使用不同的请求和响应类型
  4. 多总结:对比不同框架的请求处理方式

保持学习热情,继续 Flask 之旅! 🎯


📚 扩展阅读

  • Flask 官方文档 - 请求对象
  • HTTP 状态码详解
  • RESTful API 设计最佳实践
  • Web 安全基础知识
http://www.dtcms.com/a/264048.html

相关文章:

  • 【Python】Flask网页
  • React Native 0.79.4 中 [RCTView setColor:] 崩溃问题完整解决方案
  • JavaEE初阶第六期:解锁多线程,从 “单车道” 到 “高速公路” 的编程升级(四)
  • 无法将“pytest”项识别为 cmdlet、函数、脚本文件或可运行程序的名称
  • NLP——RNN变体LSTM和GRU
  • 【Linux】进程
  • ELK日志分析系统(filebeat+logstash+elasticsearch+kibana)
  • Pycharm安装第三方库
  • 【实战】 容器中Spring boot项目 Graphics2D 画图中文乱码解决方案
  • 脑机新手指南(二十一)基于 Brainstorm 的 MEG/EEG 数据分析(上篇)
  • ChatGPT + GitHub Copilot + Cursor 实战提升编程效率
  • Oracle 常用函数
  • WPF中Style和Template异同
  • 【CodeTop】每日练习 2025.7.1
  • 使用 Conda 工具链创建 Poetry 本地虚拟环境全记录——基于《Python 多版本与开发环境治理架构设计》
  • 迅为高情性6TOPS算力的RK3576开发板NPU rknn-model-zoo例程演示
  • Windows VMWare Centos Docker部署Springboot + mybatis + MySql应用
  • Windows版minio下载安装使用教程
  • 最大子数组和-力扣
  • 微软开源GitHub Copilot Chat,AI编程领域迎新突破
  • ChatGPT、DeepSeek等大语言模型助力高效办公、论文与项目撰写、数据分析、机器学习与深度学习建模
  • 康养休闲旅游服务实训室建设方案:数字化赋能与全链条实训创新
  • 在vscode中进行git推送拉取的详细方法
  • InnoDB索引
  • 深入解析NumPy的核心函数np.array()
  • ip网络基础
  • k8s一键部署tongweb企业版7049m6(by why+lqw)
  • 计网学习笔记第1章 计算机网络体系结构(灰灰题库)
  • 智能通信领域论文投稿常见问题与解决方案——基于数百篇CA检索稿件的实证分析
  • 【算法】动态规划 矩阵 :62. 不同路径