Flask 请求与响应
Flask 请求与响应 Web 应用的核心是「接收请求,返回响应」,本章全面讲解 Flask 中如何读取请求数据和构造各种类型的响应。 所有示例都可以在开发服务器中直接运行验证。 请求对象——request Flask
Web 应用的核心是「接收请求,返回响应」,本章全面讲解 Flask 中如何读取请求数据和构造各种类型的响应。
所有示例都可以在开发服务器中直接运行验证。
请求对象——request
Flask 通过全局代理对象 request 提供所有请求信息。
虽然它是「全局」对象,但 Flask 内部通过线程隔离机制确保每个请求获取的是自己的数据。
实例
from flask import Flask, request
app = Flask(__name__)
@app.route("/debug")
def debug():
# 输出请求的各种信息,帮助理解 request 对象
return f"""
<h1>请求调试信息</h1>
<ul>
<li>方法:{request.method}</li>
<li>路径:{request.path}</li>
<li>完整 URL:{request.url}</li>
<li>主机:{request.host}</li>
<li>客户端 IP:{request.remote_addr}</li>
<li>User-Agent:{request.headers.get('User-Agent')}</li>
</ul>
"""
访问 http://127.0.0.1:5000/debug?foo=bar 即可看到请求的详细信息。

获取查询参数——request.args
URL 中 ? 后面的参数称为查询参数(Query String)。
使用 request.args 读取,它像一个字典:
实例
def search():
# request.args.get() 安全获取参数,key 不存在时返回 None
keyword = request.args.get("keyword", "") # 获取查询关键词
page = request.args.get("page", "1") # 获取页码,默认为 1
return f"""
<h1>搜索结果</h1>
<p>关键词:{keyword}</p>
<p>页码:{page}</p>
"""
# 测试:/search?keyword=flask&page=2
# 测试:/search?keyword=RUNOOB
推荐使用 .get() 方法而不是直接下标访问 request.args["key"],因为后者在 key 不存在时会抛出 KeyError,导致返回 400 错误页面。
获取表单数据——request.form
处理 POST 请求提交的表单数据,使用 request.form:
实例
def register():
if request.method == "POST":
# 使用表单中 name="username" 字段的值
username = request.form.get("username", "")
password = request.form.get("password", "")
if not username or not password:
return "<h1>错误</h1><p>用户名和密码不能为空</p>", 400
return f"<h1>注册成功</h1><p>欢迎 {username} 加入 RUNOOB!</p>"
# GET 请求时显示注册表单
return """
<h1>注册</h1>
<form method="post">
<p>用户名:<input type="text" name="username"></p>
<p>密码:<input type="password" name="password"></p>
<p><input type="submit" value="注册"></p>
</form>
"""
注意:处理表单的 HTML 页面中,<form> 必须设置 method="post",否则数据会通过 URL 的查询字符串发送(GET 方式),敏感信息会暴露在 URL 中。
获取 JSON 数据——request.json
现代 Web 开发中,JSON 是最常见的数据格式。
当客户端发送 Content-Type: application/json 的请求时,使用 request.json 获取解析后的数据:
实例
def create_user():
# request.json 返回已经解析为 Python dict 的 JSON 数据
data = request.json # 例如客户端发送 {"name": "runoob", "email": "test@runoob.com"}
# 安全获取字段
name = data.get("name", "未知")
email = data.get("email", "未提供")
# 直接返回 dict,Flask 自动转换为 JSON 响应
return {
"message": "用户创建成功",
"name": name,
"email": email
}
可以用 curl 或 Postman 测试:
$ curl -X POST http://127.0.0.1:5000/api/user \
-H "Content-Type: application/json" \
-d '{"name": "runoob", "email": "test@runoob.com"}'
{
"message": "用户创建成功",
"name": "runoob",
"email": "test@runoob.com"
}
文件上传
处理上传的文件使用 request.files,配合 Werkzeug 提供的 secure_filename() 确保文件名安全:
实例
from werkzeug.utils import secure_filename # 过滤危险的文件名字符
# 配置上传目录(实际项目中应从配置读取)
UPLOAD_DIR = "uploads"
@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
# secure_filename 过滤掉路径遍历等危险字符
# 例如 "../../etc/passwd" 会被处理为 "etc_passwd"
filename = secure_filename(file.filename)
# 确保上传目录存在
os.makedirs(UPLOAD_DIR, exist_ok=True)
# 保存文件
file.save(os.path.join(UPLOAD_DIR, filename))
return f"文件 {filename} 上传成功"
# GET 请求显示上传表单
return """
<h1>上传文件</h1>
<form method="post" enctype="multipart/form-data">
<p><input type="file" name="file"></p>
<p><input type="submit" value="上传"></p>
</form>
"""
表单如果有文件上传字段,<form> 标签必须设置 enctype="multipart/form-data",否则文件数据不会被浏览器发送。
响应类型完全指南
视图函数可以返回多种类型的数据,Flask 会自动将它们转换为合适的 HTTP 响应。
理解这个转换规则是掌握 Flask 的关键。
返回字符串
返回的字符串作为 HTML 响应体,状态码默认 200,Content-Type 为 text/html。
返回字典或列表(JSON)
自动调用 jsonify() 转换为 JSON 响应,Content-Type 为 application/json。
实例
def get_posts():
# 返回 dict,Flask 自动转为 JSON
return {
"status": "ok",
"data": [
{"id": 1, "title": "Flask 入门教程"},
{"id": 2, "title": "RESTful API 设计"},
]
}
@app.get("/api/tags")
def get_tags():
# 返回 list,Flask 自动转为 JSON
return ["Python", "Flask", "Web 开发", "RUNOOB"]
返回元组——控制状态码和响应头
元组格式非常实用,有三种形式:
实例
@app.get("/not-found")
def not_found():
return "<h1>页面不存在</h1>", 404
# 形式 2:(body, headers_dict)
@app.get("/custom-header")
def custom_header():
return "OK", {"X-Custom-Header": "runoob"}
# 形式 3:(body, status_code, headers_dict)
@app.post("/api/login")
def api_login():
# 返回 token 和自定义状态码
return {"token": "abc123", "user": "runoob"}, 201, {"X-RateLimit": "100"}
重定向
使用 redirect() 函数将用户引导到另一个 URL:
实例
app = Flask(__name__)
@app.route("/")
def index():
# 访问首页时重定向到登录页
return redirect(url_for("login"))
@app.route("/login")
def login():
return "<h1>请先登录</h1>"
@app.route("/old-page")
def old_page():
# 旧页面永久迁移到新页面,使用 301 状态码
return redirect(url_for("new_page"), code=301)
@app.route("/new-page")
def new_page():
return "<h1>这是新页面</h1>"
默认重定向状态码为 303 See Other,适用于表单提交后重定向(PRG 模式)。
对于永久迁移场景,应使用 code=301。
终止请求——abort
使用 abort() 立即终止当前请求并返回 HTTP 错误:
实例
@app.route("/post/<int:post_id>")
def view_post(post_id):
# 假设文章 ID 只有 1-100
if post_id < 1 or post_id > 100:
# 立即返回 404,后续代码不会执行
abort(404)
return f"<h1>文章 #{post_id}</h1>"
@app.route("/admin")
def admin():
# 未授权访问时返回 403
abort(403, description="你没有权限访问此页面")
make_response——手动控制响应
当需要手动设置 Cookie 或自定义响应头时,使用 make_response():
实例
@app.route("/set-cookie")
def set_cookie():
# make_response 将视图函数的返回值包装为 Response 对象
resp = make_response("<h1>Cookie 已设置</h1>")
# 在 Response 对象上设置 Cookie
resp.set_cookie("theme", "dark", max_age=60*60*24) # 有效期 24 小时
resp.set_cookie("lang", "zh-CN")
# 也可以自定义响应头
resp.headers["X-Powered-By"] = "RUNOOB-Flask"
return resp
request 常用属性速查表
| 属性 | 类型 | 说明 | 示例值 |
|---|---|---|---|
| request.method | str | HTTP 请求方法 | "GET", "POST" |
| request.path | str | URL 路径(不含域名) | "/search" |
| request.args | MultiDict | URL 查询参数 | ?keyword=flask&page=1 |
| request.form | MultiDict | POST 表单数据 | username=admin |
| request.json | dict 或 None | JSON 请求体(已解析) | {"name": "runoob"} |
| request.files | FileMultiDict | 上传的文件 | request.files["avatar"] |
| request.headers | Headers | 请求头 | request.headers["User-Agent"] |
| request.cookies | dict | 客户端发来的 Cookie | request.cookies.get("theme") |
| request.remote_addr | str | 客户端 IP 地址 | "127.0.0.1" |
