1. Flask 基础认知
Flask 是由 Armin Ronacher 开发的,基于 Werkzeug(WSGI 工具集)和 Jinja2(模板引擎)构建,被称为'微框架(Microframework)'——并非功能薄弱,而是核心极简,可通过扩展灵活扩展功能。对比 Django(大而全的框架),Flask 更适合小型项目、快速原型开发或需要高度定制化的场景。
Python Flask 轻量级 Web 框架的基础知识。Flask 基于 Werkzeug 和 Jinja2 构建,具有轻量灵活、易学易用等特点,适合小型项目及快速原型开发。文章详细讲解了环境搭建、最小应用结构、路由系统(含动态路由与方法限定)、请求与响应处理、Jinja2 模板引擎、会话管理(Cookie/Session)、蓝图模块化开发、配置管理及钩子函数。此外,还涵盖了常用扩展(如 Flask-SQLAlchemy)的使用以及生产环境下的 Gunicorn+Nginx 部署方案,最后简述了上下文、信号及测试等进阶概念。

Flask 是由 Armin Ronacher 开发的,基于 Werkzeug(WSGI 工具集)和 Jinja2(模板引擎)构建,被称为'微框架(Microframework)'——并非功能薄弱,而是核心极简,可通过扩展灵活扩展功能。对比 Django(大而全的框架),Flask 更适合小型项目、快速原型开发或需要高度定制化的场景。
核心特点:
环境搭建: 首先推荐使用虚拟环境隔离依赖(避免全局环境污染):
# 创建虚拟环境(Python 3.6+)
python -m venv flask_env
# 激活虚拟环境(Windows)
flask_env\Scripts\activate
# 激活虚拟环境(Linux/Mac)
source flask_env/bin/activate
# 安装 Flask
pip install flask
# 验证安装
python -c "import flask; print(flask.__version__)"
Flask 的最小应用仅需几行代码即可运行,是理解其核心流程的起点:
# app.py
from flask import Flask
# 初始化 Flask 应用:__name__表示当前模块名,Flask 以此定位静态文件/模板路径
app = Flask(__name__)
# 路由:将 URL 路径(/)与视图函数绑定
@app.route('/')
def index():
# 视图函数:处理请求并返回响应
return 'Hello, Flask!'
# 仅在直接运行该脚本时启动开发服务器
if __name__ == '__main__':
# debug=True:开启调试模式(代码修改自动重启、报错显示详细信息)
app.run(debug=True, host='0.0.0.0', port=5000)
运行脚本后,访问 http://localhost:5000 即可看到'Hello, Flask!',核心要素:
Flask(__name__):创建应用实例,__name__是 Flask 的'应用根目录'标识;@app.route('/'):路由装饰器,映射 URL 与视图函数;app.run():启动开发服务器(仅用于开发环境,生产环境需用 Gunicorn/Nginx)。视图函数(View Function)是 Flask 中绑定到特定 URL 路由的 Python 函数,也是处理 HTTP 请求的核心载体
路由是 Flask 的核心之一,负责将用户请求的 URL 匹配到对应的视图函数,支持多种高级特性:
# 基本路由:固定 URL
@app.route('/about')
def about():
return '关于我们'
# 动态路由:URL 参数(<string:name> 表示接收字符串类型的 name 参数)
@app.route('/user/<string:name>')
# 类型可省略,默认 string;支持 int/float/path(含/的字符串)
def user(name):
return f'Hello, {name}!'
# 多 URL 绑定到同一个视图函数
@app.route('/hi')
@app.route('/hello')
def greet():
return 'Hi there!'
默认路由仅支持 GET 请求,可通过 methods 参数指定支持的 HTTP 方法(GET/POST/PUT/DELETE 等):
@app.route('/login', methods=['GET','POST'])
def login():
if request.method == 'POST':
# 处理 POST 请求(表单提交)
return '处理登录逻辑'
else:
# 处理 GET 请求(返回登录页面)
return '返回登录表单'
strict_slashes:控制 URL 末尾的/是否严格匹配(默认 True,/about/与/about视为不同);redirect_to:路由重定向;@app.route('/old-path', strict_slashes=False, redirect_to='/new-path')
def old_path():
pass
@app.route('/new-path')
def new_path():
return '新路径'
Flask 通过 request 对象封装客户端请求数据,通过返回值/make_response 构建响应。
需先导入 from flask import request,核心属性:
| 属性/方法 | 说明 | 示例 |
|---|---|---|
request.method | 请求方法(GET/POST 等) | if request.method == 'POST' |
request.args | GET 参数(URL 中的?参数) | request.args.get('page', 1) |
request.form | POST 表单数据 | request.form.get('username') |
request.files | 上传的文件 | request.files['avatar'] |
request.cookies | 请求中的 Cookie | request.cookies.get('token') |
request.headers | 请求头信息 | request.headers.get('User-Agent') |
访问 http://localhost:5000/user?id=100&page=2 → 返回'获取到的 GET 参数:用户 ID=100,页码 = 2'。 URL 中的?是路径和 GET 参数的分隔符,& 是多个 GET 参数之间的分隔符
示例:处理 GET/POST 请求
from flask import Flask, request
app = Flask(__name__)
@app.route('/data', methods=['GET','POST'])
def handle_data():
if request.method == 'GET':
# 获取 GET 参数:?name=张三&age=20
name = request.args.get('name','匿名')
# 第二个参数是默认值
age = request.args.get('age',0,type=int)
return f'GET 请求:姓名{name},年龄{age}'
else:
# 获取 POST 表单数据
username = request.form['username']
# 无默认值,不存在则抛异常
password = request.form.get('password')
return f'POST 请求:用户名{username}'
if __name__ == '__main__':
app.run(debug=True)
Flask 支持多种响应类型,核心方式:
Response 对象(默认状态码 200,Content-Type: text/html);make_response 构建自定义响应;jsonify(自动设置 Content-Type: application/json)。示例:
from flask import Flask, make_response, jsonify
app = Flask(__name__)
@app.route('/response1')
def response1():
# 返回元组:内容、状态码、响应头
return '自定义状态码', 404, {'X-Custom-Header':'Flask'}
@app.route('/response2')
def response2():
# 手动构建 Response 对象
resp = make_response('自定义响应')
resp.status_code = 201
resp.set_cookie('token','123456')
# 设置 Cookie
resp.headers['Content-Type']='text/plain'
return resp
@app.route('/json')
def return_json():
# 返回 JSON 数据(推荐用 jsonify,而非 json.dumps)
data = {'name':'张三','age':20}
return jsonify(data)
# 等价于 return make_response(json.dumps(data), 200, {'Content-Type': 'application/json'})
if __name__ == '__main__':
app.run(debug=True)
redirect,配合 url_for(反向生成 URL,避免硬编码);@app.errorhandler(状态码) 装饰器自定义错误页面。from flask import Flask, redirect, url_for, abort
app = Flask(__name__)
@app.route('/index')
def index():
return '首页'
@app.route('/go-index')
def go_index():
# url_for('index'):根据视图函数名生成 URL(/index)
return redirect(url_for('index'))
@app.route('/error')
def error():
# 主动抛出 404 异常
abort(404)
# 自定义 404 错误页面
@app.errorhandler(404)
def page_not_found(e):
# e 是异常对象,包含错误信息
return '页面不存在', 404
# 自定义 500 错误页面
@app.errorhandler(500)
def server_error(e):
return '服务器内部错误', 500
if __name__ == '__main__':
app.run(debug=True)
Flask 内置 Jinja2 模板引擎,用于将 Python 变量渲染到 HTML 中,实现'前后端分离(简易版)',模板文件默认放在项目根目录的 templates 文件夹下。
项目结构:
project/
├── app.py
└── templates/
└── index.html
app.py:
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def index():
# 渲染模板:传递变量 name/age 到模板
return render_template('index.html', name='张三', age=20)
if __name__ == '__main__':
app.run(debug=True)
templates/index.html:
<!DOCTYPE html>
<html>
<head>
<title>Flask 模板</title>
</head>
<body>
<h1>Hello, {{ name }}!</h1>
{# 变量渲染 #}
<p>年龄:{{ age + 1 }}</p>
{# 支持简单表达式 #}
</body>
</html>
{{ 变量名 }},支持字典({{ user.name }}/{{ user['name'] }})、列表({{ list[0] }});{% if/for %},需闭合 {% endif/endfor %};{# 条件判断 #}
{% if age >= 18 %}
<p>成年</p>
{% else %}
<p>未成年</p>
{% endif %}
{# 循环 #}
<ul>
{% for item in ['苹果', '香蕉', '橙子'] %}
<li>{{ loop.index }}: {{ item }}</li>
{# loop.index 是循环索引(从 1 开始) #}
{% endfor %}
</ul>
模板继承:通过 {% extends %}/{% block %} 实现模板复用(核心减少重复代码);
基模板(templates/base.html):
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}默认标题{% endblock %}</title>
</head>
<body>
<div class="header">头部</div>
<div class="content">
{% block content %}{% endblock %}
{# 子模板填充的内容 #}
</div>
<div class="footer">底部</div>
</body>
</html>
子模板(templates/home.html):
{% extends 'base.html' %}
{# 继承基模板 #}
{% block title %}首页{% endblock %}
{# 重写 title 块 #}
{% block content %}
<h1>首页内容</h1>
{% endblock %}
{% macro input(name, type='text',) %}
<input type="{{ type }}" name="{{ name }}" value="{{ value }}">
{% endmacro %}
{# 使用宏 #}
{{ input('username') }}
{{ input('password', type='password') }}
{{ 变量 | 过滤器 }},常用:safe(禁用转义)、length(长度)、upper(大写);{{ '<h1>测试</h1>'|safe }}
{# 渲染为<h1>测试</h1>,而非转义后的字符串 #}
{{ ['a','b']|length }}
{# 输出 2 #}
{{ 'hello'|upper }}
{# 输出 HELLO #}
HTTP 是无状态协议,Flask 通过 Cookie 和 Session 实现状态保持:
Cookie 是存储在客户端浏览器的小型文本文件,Flask 通过 set_cookie/request.cookies 操作:
from flask import Flask, make_response, request
app = Flask(__name__)
@app.route('/set-cookie')
def set_cookie():
resp = make_response('设置 Cookie')
# 设置 Cookie:key=value,max_age 是过期时间(秒)
resp.set_cookie('username','张三', max_age=3600)
return resp
@app.route('/get-cookie')
def get_cookie():
# 获取 Cookie
username = request.cookies.get('username','匿名')
return f'Cookie 中的用户名:{username}'
if __name__ == '__main__':
app.run(debug=True)
Session 是'服务器端的 Cookie':将敏感数据存储在服务器,仅将加密的 session_id 存储在客户端 Cookie 中,使用前需设置密钥(SECRET_KEY)用于加密:
from flask import Flask, session
app = Flask(__name__)
# 必须设置密钥(开发时可随便写,生产环境需用随机强密钥)
app.secret_key = 'your-secret-key-123456'
# 生产环境建议从环境变量读取
@app.route('/set-session')
def set_session():
# 存储数据到 Session
session['username']='张三'
session['age']=20
return 'Session 已设置'
@app.route('/get-session')
def get_session():
# 获取 Session 数据
username = session.get('username','匿名')
age = session.get('age',0)
return f'Session:用户名{username},年龄{age}'
@app.route('/clear-session')
def clear_session():
# 删除单个 Session 键
session.pop('age',None)
# 清空所有 Session
# session.clear()
return 'Session 已清理'
if __name__ == '__main__':
app.run(debug=True)
当项目规模扩大时,单一 app.py 会变得臃肿,蓝图(Blueprint)可将功能拆分为独立模块(如用户模块、商品模块),实现代码解耦。
示例:拆分用户模块和商品模块 项目结构:
project/
├── app.py
├── users/
│ ├── __init__.py
│ └── views.py # 用户模块视图
└── goods/
├── __init__.py
└── views.py # 商品模块视图
users/views.py:
from flask import Blueprint
# 创建蓝图:第一个参数是蓝图名,第二个是模块名,url_prefix 是该蓝图所有路由的前缀
user_bp = Blueprint('user', __name__, url_prefix='/user')
# 蓝图的路由(最终 URL:/user/login)
@user_bp.route('/login')
def login():
return '用户登录'
@user_bp.route('/profile')
def profile():
return '用户个人中心'
goods/views.py:
from flask import Blueprint
goods_bp = Blueprint('goods', __name__, url_prefix='/goods')
@goods_bp.route('/list')
def goods_list():
return '商品列表'
@goods_bp.route('/detail/<int:id>')
def goods_detail(id):
return f'商品详情:{id}'
app.py(注册蓝图):
from flask import Flask
from users.views import user_bp
from goods.views import goods_bp
app = Flask(__name__)
# 注册蓝图到应用
app.register_blueprint(user_bp)
app.register_blueprint(goods_bp)
if __name__ == '__main__':
app.run(debug=True)
访问 http://localhost:5000/user/login(用户登录)、http://localhost:5000/goods/list(商品列表)即可验证,蓝图支持独立的模板/静态文件路径、错误处理等,是 Flask 模块化开发的核心。
Flask 支持多种配置方式,核心是 app.config(字典对象),推荐按环境(开发/测试/生产)分离配置:
from flask import Flask
app = Flask(__name__)
# 方式 1:直接设置
app.config['SECRET_KEY']='123456'
app.config['DEBUG']=True
# 方式 2:从字典加载
config = {'SECRET_KEY':'123456','DEBUG':True}
app.config.update(config)
# 方式 3:从配置类加载(推荐,便于环境分离)
class DevelopmentConfig:
DEBUG = True
SECRET_KEY = 'dev-secret-key'
class ProductionConfig:
DEBUG = False
SECRET_KEY = 'prod-secret-key'
# 生产环境用随机生成的密钥
# 加载配置类
app.config.from_object(DevelopmentConfig)
# 方式 4:从环境变量加载(生产环境推荐,避免硬编码敏感信息)
# app.config.from_envvar('FLASK_CONFIG_FILE') # 读取环境变量指向的配置文件
Flask 提供多个钩子函数,用于在请求处理的不同阶段执行自定义逻辑:
| 钩子函数 | 执行时机 |
|---|---|
before_request | 每个请求处理前执行 |
after_request | 每个请求处理后执行(无异常) |
teardown_request | 每个请求处理后执行(无论是否异常) |
before_first_request | 第一个请求处理前执行(Flask 2.0+已废弃,改用 app.before_first_request_funcs) |
示例:
from flask import Flask, request
app = Flask(__name__)
# 请求处理前:记录请求信息
@app.before_request
def log_request():
print(f'请求路径:{request.path},方法:{request.method}')
# 请求处理后:添加响应头
@app.after_request
def add_header(resp):
resp.headers['X-Processed-By']='Flask'
return resp
# 请求处理完成后(无论是否异常):清理资源
@app.teardown_request
def teardown(exception):
if exception:
print(f'请求异常:{exception}')
print('请求处理完成,清理资源')
@app.route('/')
def index():
return 'Hello, Flask!'
if __name__ == '__main__':
app.run(debug=True)
Flask 的核心功能有限,扩展是其生态的核心,以下是最常用的扩展:
Flask-SQLAlchemy:ORM 框架,简化数据库操作(支持 MySQL/PostgreSQL/SQLite 等);
安装:pip install flask-sqlalchemy
示例:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
# 配置数据库连接(SQLite 示例,文件型数据库)
app.config['SQLALCHEMY_DATABASE_URI']='sqlite:///test.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS']=False
# 关闭修改跟踪(提升性能)
db = SQLAlchemy(app)
# 定义模型(对应数据库表)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
# 主键
username = db.Column(db.String(80), unique=True, nullable=False)
# 唯一、非空
email = db.Column(db.String(120), unique=True, nullable=False)
def __repr__(self):
return f'<User {self.username}>'
# 创建表(需在 Python 交互环境执行)
# with app.app_context():
# db.create_all()
@app.route('/add-user')
def add_user():
# 添加数据
user = User(username='张三', email='[email protected]')
db.session.add(user)
db.session.commit()
return '用户添加成功'
@app.route('/users')
def get_users():
# 查询数据
users = User.query.all()
return f'所有用户:{users}'
if __name__ == '__main__':
app.run(debug=True)
开发环境的 app.run() 仅用于调试,生产环境需使用WSGI 服务器(Gunicorn)+ 反向代理(Nginx):
安装:pip install gunicorn
创建 wsgi.py:
from app import app
if __name__ == '__main__':
app.run()
启动 Gunicorn:
# 启动:workers 是工作进程数(建议=CPU 核心数*2+1),bind 绑定地址和端口
gunicorn -w 4 -b 0.0.0.0:8000 wsgi:app
Nginx 负责接收客户端请求,转发到 Gunicorn,同时处理静态文件、负载均衡等:
server {
listen 80;
server_name your-domain.com; # 你的域名
# 静态文件(Flask 的 static 文件夹)
location /static {
alias /path/to/your/project/static;
}
# 转发动态请求到 Gunicorn
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
app_context)和请求上下文(request_context),request/session属于请求上下文,current_app/g属于应用上下文;
current_app:当前运行的应用实例;g:全局临时存储,仅在当前请求有效,用于在钩子函数和视图函数间传递数据。blinker),用于监听应用事件(如请求开始/结束、模板渲染完成等),实现解耦的事件通知;pytest+flask.test_client() 编写单元测试,模拟请求并验证响应。
微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML 转 Markdown 互为补充。 在线工具,Markdown 转 HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML 转 Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online