一文掌握Flask:从基础使用到高级应用

一文掌握Flask:从基础使用到高级应用
在这里插入图片描述

文章目录

在这里插入图片描述

1 Flask框架概述

1.1 什么是Flask?

Flask是一个轻量级的Python Web框架,它基于Werkzeug WSGI工具箱和Jinja2模板引擎构建。Flask被设计为可扩展灵活的框架,它不强制要求特定的项目结构或依赖组件,这使得开发者可以根据项目需求灵活选择组件和架构。Flask的"微框架"并不意味着功能欠缺,而是指其核心保持简单且易于扩展,允许开发者自由选择数据库、认证方式等其他组件。

Flask的核心哲学是为开发者提供基础构建块,同时给予他们足够的自由度来构建符合特定需求的应用程序。与Django等"全能型"框架不同,Flask遵循"按需配置"的原则,这使得它成为构建从小型简单应用到大型复杂系统的理想选择。

1.2 Flask的核心特性

Flask具有以下几个显著特性:

  • 轻量级且简单:Flask代码库小巧,API简洁易懂,学习曲线平缓,让开发者可以快速上手并构建应用。
  • 灵活性高:不强制使用特定的项目结构或库,开发者可以根据需要选择适合的组件和架构。
  • 扩展性强:拥有丰富的扩展生态系统,可以轻松添加数据库集成、表单验证、认证等功能。
  • Jinja2模板引擎:内置强大的模板引擎,支持模板继承、自动HTML转义等特性。
  • 开发服务器和调试器:内置开发服务器和交互式调试器,便于开发和调试。
  • RESTful请求分发:支持RESTful风格的请求处理,便于构建Web API。
  • 单元测试支持:提供良好的测试客户端和支持,便于编写和运行单元测试。
  • Cookies支持:支持客户端会话管理,包括安全的cookie支持。

1.3 Flask与Django的对比

特性FlaskDjango
设计哲学微框架,简单核心,高度可扩展全能型框架,内置大量功能
学习曲线平缓,易于入门较陡峭,需要学习更多概念
灵活性高,可以自由选择组件较低,遵循"约定优于配置"
数据库支持需要通过扩展添加内置ORM和支持多种数据库
管理后台需要通过扩展添加内置功能强大的管理后台
模板引擎Jinja2自研模板系统
适用场景小型到大型应用,API服务内容管理系统,大型Web应用

1.4 Flask的适用场景

Flask适用于多种Web开发场景:

  • RESTful API开发:Flask的轻量级特性和灵活性使其成为构建Web API的理想选择。
  • 微服务架构:Flapp的简单性使其适合作为微服务架构中的单个服务组件。
  • 原型开发:快速构建概念验证或产品原型。
  • 小型Web应用:构建不需要Django全部功能的小型应用。
  • 大型应用:通过适当的架构设计和扩展,Flask也能支持大型复杂应用。
  • 嵌入式Web服务:为设备或应用程序提供嵌入式Web界面。

2 Flask基本使用

2.1 安装与环境配置

要使用Flask,首先需要安装Python和pip(Python包管理器)。推荐使用Python 3.6或更高版本。

2.1.1 创建虚拟环境

使用虚拟环境是Python开发的最佳实践,它可以隔离项目依赖,避免包冲突。创建虚拟环境有多种方式:

# 使用venv模块(Python 3.3+) python -m venv my_flask_env # 使用virtualenv(需要先安装) pip install virtualenv virtualenv my_flask_env # 使用virtualenvwrapper(更高级的虚拟环境管理) pip install virtualenvwrapper mkvirtualenv my_flask_env 

激活虚拟环境的方法取决于操作系统:

# Windows my_flask_env\Scripts\activate # Linux/Macsource my_flask_env/bin/activate 
2.1.2 安装Flask

激活虚拟环境后,使用pip安装Flask:

pip install flask 

安装Flask时会自动安装以下依赖包:

  • Werkzeug:WSGI工具箱,处理Web服务器网关接口规范
  • Jinja2:模板引擎,用于渲染HTML页面
  • itsdangerous:安全地签名数据,用于保护cookie内容
  • click:命令行界面创建工具
2.1.3 验证安装

创建一个简单的Flask应用来验证安装是否成功:

# app.pyfrom flask import Flask app = Flask(__name__)@app.route('/')defhello():return'Hello, Flask!'if __name__ =='__main__': app.run()

运行应用:

python app.py 

在浏览器中访问http://localhost:5000,应该能看到"Hello, Flask!"消息。

2.2 基本程序结构

一个基本的Flask应用包含以下组件:

2.2.1 初始化应用实例

每个Flask应用都必须创建一个Flask实例:

from flask import Flask app = Flask(__name__)

__name__参数帮助Flask确定应用的位置,以便查找资源文件。

2.2.2 定义路由和视图函数

路由将URL映射到Python函数(视图函数),处理请求并返回响应:

@app.route('/')defindex():return'Home Page'@app.route('/user/<username>')defshow_user(username):returnf'User: {username}'

路由可以包含变量部分,用<variable_name>表示,这些变量会作为参数传递给视图函数。

2.2.3 启动开发服务器

Flask包含一个内置开发服务器,适合开发和测试:

if __name__ =='__main__': app.run(debug=True, host='0.0.0.0', port=5000)

参数说明:

  • debug=True:启用调试模式,代码更改后自动重载,并提供详细错误页面
  • host='0.0.0.0':使服务器对外可访问
  • port=5000:指定端口号(默认5000)

2.3 核心组件详解

2.3.1 路由系统

Flask的路由系统非常灵活,支持多种路由定义方式:

# 基本路由@app.route('/hello')defhello():return'Hello, World!'# 带变量的路由@app.route('/user/<username>')defshow_user(username):returnf'User: {username}'# 指定变量类型@app.route('/post/<int:post_id>')defshow_post(post_id):returnf'Post: {post_id}'# 多HTTP方法路由@app.route('/login', methods=['GET','POST'])deflogin():if request.method =='POST':return do_login()else:return show_login_form()

支持的路由变量类型:

  • string:默认类型,接受任何不包含斜杠的文本
  • int:接受正整数
  • float:接受正浮点数
  • path:类似string,但接受斜杠
  • uuid:接受UUID字符串
2.3.2 请求对象

Flask的request对象包含所有 incoming 请求数据:

from flask import request @app.route('/login', methods=['POST'])deflogin():# 获取表单数据 username = request.form['username'] password = request.form['password']# 获取查询参数 page = request.args.get('page',1,type=int)# 获取JSON数据if request.is_json: data = request.get_json()return'Login processed'
2.3.3 响应对象

视图函数可以返回多种类型的响应:

from flask import make_response, jsonify @app.route('/')defindex():# 返回字符串return'Hello, World!'@app.route('/json')defjson_data():# 返回JSON数据return jsonify({'name':'John','age':30})@app.route('/custom')defcustom_response():# 创建自定义响应 response = make_response('Custom Response') response.headers['X-Custom-Header']='Value' response.status_code =201return response @app.route('/redirect')defredirect_example():# 重定向return redirect('/new-location')@app.route('/render')defrender_example():# 渲染模板return render_template('index.html', name='John')
2.3.4 模板渲染

Flask使用Jinja2模板引擎渲染动态内容。模板通常存放在项目目录下的templates文件夹中:

from flask import render_template @app.route('/hello/<name>')defhello(name):return render_template('hello.html', name=name)

模板文件示例(templates/hello.html):

<!DOCTYPEhtml><html><head><title>Hello Page</title><!-- 三种导入静态文件的方式 --><!-- <link rel="stylesheet" href="../static/index.css"> --><!-- <link rel="stylesheet" href="/static/index.css"> --><linkrel="stylesheet"href="{{ url_for('static', filename='index.css') }}"></head><body><h1>Hello, {{ name }}!</h1></body></html>

Jinja2模板支持变量替换、控制结构和模板继承等高级特性。

2.3.5 静态文件处理

静态文件(CSS、JavaScript、图片)通常存放在static文件夹中,可以使用url_for函数生成URL:

url_for('static', filename='style.css')

在模板中使用静态文件:

<linkrel="stylesheet"href="{{ url_for('static', filename='css/style.css') }}"><scriptsrc="{{ url_for('static', filename='js/app.js') }}"></script><imgsrc="{{ url_for('static', filename='images/logo.png') }}"alt="Logo">

2.4 扩展Flask功能

Flask的核心功能可以通过扩展来增强。常用的扩展包括:

  • Flask-SQLAlchemy:数据库ORM
  • Flask-WTF:表单处理
  • Flask-Login:用户认证
  • Flask-RESTful:构建RESTful API
  • Flask-Mail:电子邮件支持
  • Flask-Migrate:数据库迁移

安装和使用扩展的通常模式:

pip install flask-extension-name 
from flask_extension_name import ExtensionClass extension = ExtensionClass(app)# 或者使用初始化模式

3 Flask项目配置

3.1 配置基础

Flask应用配置通过app.config对象实现,它是一个类似字典的对象,可以用来存储各种配置变量。

3.1.1 直接设置配置值
app = Flask(__name__) app.config['DEBUG']=True app.config['SECRET_KEY']='your-secret-key'

或者使用属性语法:

app.debug =True
3.1.2 常用内置配置变量

Flask提供了许多内置配置变量:

配置变量说明默认值
DEBUG启用/禁用调试模式False
TESTING启用/禁用测试模式False
SECRET_KEY加密签名所需的密钥None
JSON_AS_ASCII禁用ASCII编码JSON响应True
JSON_SORT_KEYS排序JSON键True
SESSION_COOKIE_NAME会话cookie名称‘session’
SESSION_COOKIE_HTTPONLY禁止JS访问会话cookieTrue
SESSION_COOKIE_SECURE仅HTTPS传输会话cookieFalse
PERMANENT_SESSION_LIFETIME永久会话生存期timedelta(days=31)

3.2 配置方法

Flask支持多种配置加载方式,适合不同场景。

3.2.1 从Python文件加载

创建配置文件(如config.py):

# config.py DEBUG =True SECRET_KEY ='your-secret-key' DATABASE_URI ='sqlite:///app.db'

在应用中加载配置:

app.config.from_pyfile('config.py')
3.2.2 从对象加载

创建配置类:

classConfig: DEBUG =False TESTING =False SECRET_KEY ='development-key' DATABASE_URI ='sqlite:///app.db'classProductionConfig(Config): DATABASE_URI ='postgresql://user:pass@localhost/prod_db'classDevelopmentConfig(Config): DEBUG =True DATABASE_URI ='sqlite:///dev.db'classTestingConfig(Config): TESTING =True DATABASE_URI ='sqlite:///test.db'

在应用中加载配置:

app.config.from_object('configmodule.ProductionConfig')
3.2.3 从环境变量加载

首先设置环境变量:

exportAPP_SETTINGS="config.py"exportSECRET_KEY="your-secret-key"

在应用中加载配置:

app.config.from_envvar('APP_SETTINGS')# 从文件 app.config['SECRET_KEY']= os.environ.get('SECRET_KEY')# 单个值
3.2.4 多重配置加载策略

在实际项目中,通常组合使用多种配置方法:

app = Flask(__name__)# 首先加载默认配置 app.config.from_object('app.config.DefaultConfig')# 然后根据环境加载特定配置 env = os.environ.get('FLASK_ENV','development')if env =='production': app.config.from_object('app.config.ProductionConfig')elif env =='testing': app.config.from_object('app.config.TestingConfig')else: app.config.from_object('app.config.DevelopmentConfig')# 最后从环境变量文件覆盖配置(可选) app.config.from_envvar('APP_SETTINGS', silent=True)

3.3 大型项目配置

对于大型项目,需要更复杂的配置结构和组织方式。

3.3.1 项目结构

一个典型的大型Flask项目结构如下:

flask_project/ ├── apps/ # 存放各个蓝图模块 │ ├── app01/ │ │ ├── __init__.py # 存放蓝图的配置 │ │ ├── views.py # 视图函数 │ │ └── models.py # 数据模型 │ └── app02/ │ ├── __init__.py │ ├── views.py │ └── models.py ├── conf/ # 存放项目的配置文件 │ └── application.yaml ├── middleware/ # 存放中间件的配置 │ └── request_middleware.py ├── sdk/ # 存放一些第三方包 │ ├── logs/ # 日志相关 │ ├── http/ # http请求相关 │ └── es/ # es相关 ├── utils/ # 存放项目中公共的工具类 ├── static/ # 静态文件 │ ├── css/ │ ├── js/ │ └── images/ ├── templates/ # 模板文件 ├── extensions.py # 存放SQLAlchemy等扩展的配置 ├── scheduler.py # 存放定时任务的配置 ├── app.py # flask应用的初始化文件,也是入口文件 ├── settings.py # 存放整个flask项目的配置 └── requirements.txt # 项目依赖 
3.3.2 应用工厂模式

应用工厂模式允许创建多个应用实例,便于测试和不同环境部署:

# app.pyfrom flask import Flask from apps.app01 import app01 from apps.extensions import db defcreate_app(config=None):""" App工厂函数 """ app = Flask(__name__)# 加载配置if config isNone: app.config.from_object('settings')else: app.config.from_object(config)# 初始化扩展 db.init_app(app)# 添加蓝图 app.register_blueprint(app01)# 注册中间件 register_middleware(app)return app # 创建app实例 app = create_app()if __name__ =='__main__': app.run(host='0.0.0.0', port=8080, debug=True)
3.3.3 蓝图(Blueprint)使用

蓝图允许将应用模块化,适合大型项目结构:

# apps/app01/__init__.pyfrom flask import Blueprint from flask_restful import Api # 创建蓝图实例 app01 = Blueprint('app01', __name__, url_prefix='/api/v1/app01', template_folder='templates', static_folder='static', static_url_path='assets')# 初始化API(如果使用Flask-RESTful) api = Api(app01)# 导入视图from.import views 
# apps/app01/views.pyfrom.import app01 @app01.route('/endpoint')defendpoint():return'App01 Endpoint'

在主应用中注册蓝图:

# app.pyfrom apps.app01 import app01 as app01_blueprint from apps.app02 import app02 as app02_blueprint app.register_blueprint(app01_blueprint) app.register_blueprint(app02_blueprint)

3.4 扩展配置

3.4.1 数据库配置

使用Flask-SQLAlchemy配置数据库:

# extensions.pyfrom flask_sqlalchemy import SQLAlchemy db = SQLAlchemy()
# settings.pyimport os from pathlib import Path BASE_DIR = Path(__file__).resolve().parent # 数据库配置 SQLALCHEMY_DATABASE_URI ='sqlite:///'+ os.path.join(BASE_DIR,'app.db') SQLALCHEMY_TRACK_MODIFICATIONS =False SQLALCHEMY_ECHO =False# 是否输出SQL语句# MySQL配置示例# user, password, port, host, database, charset = config_parser.get_mysql_set()# SQLALCHEMY_DATABASE_URI = f"mysql+pymysql://{user}:{password}@{host}:{port}/{database}?charset={charset}"

初始化数据库:

# app.pyfrom extensions import db defcreate_app(): app = Flask(__name__)# 加载配置... db.init_app(app)return app 
3.4.2 日志配置

配置应用日志系统:

# settings.pyimport logging.config LOGGING ={'version':1,'disable_existing_loggers':False,'formatters':{'file_formatter':{'format':'[%(asctime)s][%(threadName)s:%(thread)d][%(filename)s[line:%(lineno)d][%(levelname)s] - %(message)s'},'simple':{'format':'[%(asctime)s][%(levelname)s] - %(message)s'},},'handlers':{'console':{'class':'logging.StreamHandler','formatter':'simple','level':'DEBUG'},'file':{'class':'logging.handlers.RotatingFileHandler','formatter':'file_formatter','filename':'app.log','maxBytes':1024*1024*10,# 10MB'backupCount':5},},'loggers':{'flask_logger':{'handlers':['console','file'],'level':'INFO',# 设置日志级别,可以根据需要调整'propagate':False,},'sqlalchemy':{'handlers':['console'],'level':'INFO','propagate':False}},}# 加载日志配置 logging.config.dictConfig(LOGGING)

3.5 部署配置

3.5.1 使用Gunicorn部署

创建Gunicorn配置文件:

# gunicorn_config.pyimport multiprocessing bind ="0.0.0.0:5000" workers = multiprocessing.cpu_count()*2+1 worker_class ="gevent" worker_connections =1000 timeout =30 keepalive =2

启动命令:

gunicorn -c gunicorn_config.py app:app 
3.5.2 使用Nginx + uWSGI部署

uWSGI配置:

; uwsgi.ini [uwsgi] ; 配合nginx使用 socket = 127.0.0.1:5005 ; 项目路径 chdir = /path/to/your/project ; wsgi文件 module = app:app ; 指定工作进程 processes = 4 ; 主进程 master = true ; 每个工作进程有2个线程 threads = 2 ; 保存主进程的进程号 pidfile = /tmp/project_uwsgi.pid 

Nginx配置:

# /etc/nginx/conf.d/yourapp.conf server { listen 80; server_name yourdomain.com; access_log /var/log/nginx/yourapp_access.log main; location / { include uwsgi_params; uwsgi_pass 127.0.0.1:5005; proxy_read_timeout 150; client_max_body_size 20M; } location /static { alias /path/to/your/project/static; expires 30d; } } 
3.5.3 使用Supervisor管理进程

Supervisor配置:

; /etc/supervisor/conf.d/yourapp.conf [program:yourapp_uwsgi] command=/path/to/venv/bin/uwsgi /path/to/uwsgi.ini numprocs=1 directory=/path/to/your/project priority=999 autostart=true autorestart=true stopasgroup=true killasgroup=true startsecs=10 startretries=10 exitcodes=0,2 stopsignal=QUIT user=www-data log_stdout=true log_stderr=true redirect_stderr=true stdout_logfile=/var/log/supervisor/yourapp_uwsgi.log logfile_maxbytes=10MB logfile_backups=10 environment= MODE="PRODUCTION", FLASK_ENV="production" 

4 接口鉴权

4.1 认证与授权基础

在Web应用中,**认证(Authentication)是验证用户身份的过程,而授权(Authorization)**是确定已认证用户有权访问哪些资源的过程。Flask提供了灵活的机制来实现这两种功能。

4.1.1 Session-based认证

Session认证是一种基于服务器存储的认证机制:

  1. 用户登录成功后,服务器为其生成一个唯一的session_id,并将其存储在客户端的Cookie中
  2. 服务器在服务端(内存、数据库或Redis)存储session数据
  3. 每次请求,客户端携带包含session_id的Cookie
  4. 服务器通过验证session_id来识别用户身份
4.1.2 Token-based认证

Token认证是一种基于客户端的认证机制,通常使用JSON Web Token(JWT):

  1. 用户登录后,服务器生成一个Token返回给客户端
  2. 客户端在后续请求中携带该Token(通常在Authorization头中)
  3. 服务器通过解析和验证Token确定用户身份
  4. 服务器无需存储会话状态,Token包含所有必要信息

4.2 Session认证实现

4.2.1 基本Session配置
from flask import Flask, session import os app = Flask(__name__) app.secret_key = os.urandom(24)# 或者使用固定密钥 app.config['SESSION_TYPE']='filesystem'# 或者 'redis', 'memcached'等 app.config['PERMANENT_SESSION_LIFETIME']= timedelta(hours=1)
4.2.2 登录和会话管理
from flask import session, request, jsonify, redirect, url_for from werkzeug.security import generate_password_hash, check_password_hash # 模拟用户数据 USER_DATA ={"test_user": generate_password_hash("password123")}@app.route('/login', methods=['POST'])deflogin(): data = request.json username = data.get('username') password = data.get('password')if username in USER_DATA and check_password_hash(USER_DATA[username], password):# 创建会话 session['username']= username session['logged_in']=True# 设置会话为永久性 session.permanent =Truereturn jsonify({"message":"Login successful"}),200return jsonify({"message":"Invalid credentials"}),[email protected]('/protected', methods=['GET'])defprotected():if'logged_in'in session and session['logged_in']: username = session['username']return jsonify({"message":f"Welcome {username}!"}),200return jsonify({"message":"Unauthorized"}),[email protected]('/logout', methods=['POST'])deflogout():# 清除会话 session.pop('username',None) session.pop('logged_in',None)return jsonify({"message":"Logout successful"}),200
4.2.3 使用Redis存储Session

对于生产环境,建议使用Redis等外部存储保存Session:

from flask import Flask from flask_session import Session import redis app = Flask(__name__) app.config['SECRET_KEY']='your-secret-key' app.config['SESSION_TYPE']='redis' app.config['SESSION_REDIS']= redis.from_url('redis://localhost:6379')# 初始化Session扩展 Session(app)

4.3 Token认证(JWT)实现

4.3.1 JWT基础概念

JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在各方之间安全地传输信息。JWT由三部分组成:

  1. Header:包含令牌类型和算法
  2. Payload:包含声明(claims)
  3. Signature:用于验证令牌完整性
4.3.2 安装和配置JWT扩展
pip install flask-jwt-extended 
from flask import Flask from flask_jwt_extended import JWTManager app = Flask(__name__)# 配置JWT app.config['JWT_SECRET_KEY']='your-jwt-secret-key'# 生产环境应使用更安全的密钥 app.config['JWT_ACCESS_TOKEN_EXPIRES']= timedelta(hours=1) app.config['JWT_REFRESH_TOKEN_EXPIRES']= timedelta(days=30) app.config['JWT_TOKEN_LOCATION']=['headers']# 可以从headers, cookies, query_string等位置获取token app.config['JWT_HEADER_NAME']='Authorization' app.config['JWT_HEADER_TYPE']='Bearer'# 初始化JWT管理器 jwt = JWTManager(app)
4.3.3 实现JWT认证
from flask import request, jsonify from flask_jwt_extended import( create_access_token, create_refresh_token, jwt_required, get_jwt_identity, get_jwt )from werkzeug.security import generate_password_hash, check_password_hash # 模拟用户数据 USER_DATA ={"test_user": generate_password_hash("password123")}@app.route('/login', methods=['POST'])deflogin(): data = request.json username = data.get('username') password = data.get('password')if username in USER_DATA and check_password_hash(USER_DATA[username], password):# 创建访问令牌和刷新令牌 access_token = create_access_token(identity=username) refresh_token = create_refresh_token(identity=username)return jsonify({"access_token": access_token,"refresh_token": refresh_token,"message":"Login successful"}),200return jsonify({"message":"Invalid credentials"}),[email protected]('/protected', methods=['GET'])@jwt_required()# 需要有效的JWT访问令牌defprotected(): current_user = get_jwt_identity()return jsonify({"message":f"Welcome {current_user}!"}),[email protected]('/refresh', methods=['POST'])@jwt_required(refresh=True)# 需要有效的刷新令牌defrefresh(): current_user = get_jwt_identity() new_access_token = create_access_token(identity=current_user)return jsonify({"access_token": new_access_token}),[email protected]('/logout', methods=['POST'])@jwt_required()deflogout():# JWT通常是无状态的,客户端通过丢弃令牌实现"登出"# 如果需要服务端失效令牌,可以使用令牌黑名单return jsonify({"message":"Logout successful"}),200
4.3.4 高级JWT特性
# 添加自定义声明到[email protected]_identity_loaderdefuser_identity_lookup(user):return user.username @jwt.additional_claims_loaderdefadd_claims_to_access_token(identity):# 可以根据用户身份添加自定义声明if identity =="admin":return{"is_admin":True}return{"is_admin":False}# 保护管理员路由@app.route('/admin', methods=['GET'])@jwt_required()defadmin(): claims = get_jwt()ifnot claims.get('is_admin',False):return jsonify({"message":"Admin required"}),403return jsonify({"message":"Welcome Admin!"}),200# 处理令牌失效(黑名单) blacklisted_tokens =set()@jwt.token_in_blocklist_loaderdefcheck_if_token_revoked(jwt_header, jwt_payload): jti = jwt_payload["jti"]return jti in blacklisted_tokens @app.route('/revoke', methods=['POST'])@jwt_required()defrevoke_token(): jti = get_jwt()["jti"] blacklisted_tokens.add(jti)return jsonify({"message":"Token revoked"}),200

4.4 基于角色的访问控制(RBAC)

RBAC(Role-Based Access Control)通过角色管理用户权限。

4.4.1 实现角色系统
# models.pyfrom extensions import db from flask import current_app from werkzeug.security import generate_password_hash, check_password_hash # 角色权限关联表 role_permissions = db.Table('role_permissions', db.Column('role_id', db.Integer, db.ForeignKey('role.id'), primary_key=True), db.Column('permission_id', db.Integer, db.ForeignKey('permission.id'), primary_key=True))# 用户角色关联表 user_roles = db.Table('user_roles', db.Column('user_id', db.Integer, db.ForeignKey('user.id'), primary_key=True), db.Column('role_id', db.Integer, db.ForeignKey('role.id'), primary_key=True))classUser(db.Model):id= db.Column(db.Integer, primary_key=True) username = db.Column(db.String(80), unique=True, nullable=False) password_hash = db.Column(db.String(120), nullable=False) is_active = db.Column(db.Boolean, default=True)# 关系 roles = db.relationship('Role', secondary=user_roles, backref=db.backref('users', lazy=True))defset_password(self, password): self.password_hash = generate_password_hash(password)defcheck_password(self, password):return check_password_hash(self.password_hash, password)defhas_permission(self, permission_name):for role in self.roles:for permission in role.permissions:if permission.name == permission_name:returnTruereturnFalseclassRole(db.Model):id= db.Column(db.Integer, primary_key=True) name = db.Column(db.String(80), unique=True, nullable=False) description = db.Column(db.String(255))# 关系 permissions = db.relationship('Permission', secondary=role_permissions, backref=db.backref('roles', lazy=True))classPermission(db.Model):id= db.Column(db.Integer, primary_key=True) name = db.Column(db.String(80), unique=True, nullable=False) description = db.Column(db.String(255))
4.4.2 基于角色的权限装饰器
from functools import wraps from flask import jsonify from flask_jwt_extended import verify_jwt_in_request, get_jwt_identity defpermission_required(permission_name):defdecorator(f):@wraps(f)defdecorated_function(*args,**kwargs):# 验证JWT verify_jwt_in_request()# 获取当前用户身份 username = get_jwt_identity() user = User.query.filter_by(username=username).first()ifnot user ornot user.has_permission(permission_name):return jsonify({"message":"Insufficient permissions"}),403return f(*args,**kwargs)return decorated_function return decorator # 使用示例@app.route('/admin/dashboard')@jwt_required()@permission_required('admin_dashboard')defadmin_dashboard():return jsonify({"message":"Welcome to admin dashboard"}),200
4.4.3 组织级别的权限控制

对于多租户应用,需要实现组织级别的权限控制:

deforganization_permission_required(permission_name):defdecorator(f):@wraps(f)defdecorated_function(*args,**kwargs):# 验证JWT verify_jwt_in_request()# 获取当前用户身份和组织ID username = get_jwt_identity() organization_id = kwargs.get('organization_id') user = User.query.filter_by(username=username).first()# 检查用户是否属于该组织并有相应权限ifnot user ornot user.has_organization_permission(organization_id, permission_name):return jsonify({"message":"Insufficient organization permissions"}),403return f(*args,**kwargs)return decorated_function return decorator # 使用示例@app.route('/organizations/<organization_id>/settings')@jwt_required()@organization_permission_required('manage_organization')deforganization_settings(organization_id):return jsonify({"message":f"Organization {organization_id} settings"}),200

4.5 OAuth2和第三方认证

4.5.1 使用Authlib实现OAuth2
from authlib.integrations.flask_client import OAuth oauth = OAuth(app)# 配置Google OAuth2 google = oauth.register( name='google', client_id='your-google-client-id', client_secret='your-google-client-secret', access_token_url='https://accounts.google.com/o/oauth2/token', access_token_params=None, authorize_url='https://accounts.google.com/o/oauth2/auth', authorize_params=None, api_base_url='https://www.googleapis.com/oauth2/v1/', client_kwargs={'scope':'email profile'},)@app.route('/login/google')deflogin_google(): redirect_uri = url_for('authorize_google', _external=True)return google.authorize_redirect(redirect_uri)@app.route('/login/google/authorize')defauthorize_google(): token = google.authorize_access_token() resp = google.get('userinfo') user_info = resp.json()# 在这里处理用户信息,创建或登录用户# ...return jsonify(user_info)
4.5.2 使用Logto进行API保护

Logto是一个开源的CIAM(客户身份和访问管理)解决方案:

from flask import Flask, jsonify, request from logto import LogtoClient, LogtoConfig app = Flask(__name__)# Logto配置 logto_config = LogtoConfig( endpoint='https://your-tenant.logto.app', app_id='your-app-id', app_secret='your-app-secret', resource='https://api.yourapp.com'# 你的API资源标识) logto_client = LogtoClient(logto_config)@app.route('/protected')asyncdefprotected():# 从请求头中获取访问令牌 auth_header = request.headers.get('Authorization')ifnot auth_header ornot auth_header.startswith('Bearer '):return jsonify({"error":"Unauthorized"}),401 access_token = auth_header[7:]# 去掉"Bearer "前缀try:# 验证访问令牌 claims =await logto_client.verify_access_token(access_token)# 检查权限ifnot claims.get('scopes',[]).contains('read:data'):return jsonify({"error":"Insufficient permissions"}),403return jsonify({"message":"Access granted","user": claims.get('sub')})except Exception as e:return jsonify({"error":str(e)}),401

5 自定义中间件

5.1 中间件概念与原理

中间件是WSGI应用的重要组成部分,它可以在请求被处理前和响应被发送前执行特定代码,用于实现跨切面关注点如认证、日志、错误处理等。

5.1.1 Flask中间件工作原理

在Flask中,中间件主要通过两种方式实现:

  1. 装饰器模式:使用@app.before_request@app.after_request等装饰器
  2. WSGI中间件:包装Flask应用的WSGI应用

Flask的请求处理流程如下:

  1. 请求到达WSGI服务器
  2. 经过可能的WSGI中间件
  3. 到达Flask应用
  4. 执行before_request钩子
  5. 执行视图函数
  6. 执行after_request钩子
  7. 响应经过WSGI中间件
  8. 返回给客户端

5.2 内置请求钩子

Flask提供了多种装饰器来实现中间件功能。

5.2.1 before_request
@app.before_requestdefbefore_request():# 在每个请求之前执行print(f"Request received: {request.method}{request.path}")# 可以进行身份验证ifnot session.get('user_id')and request.endpoint notin['login','static']:return redirect(url_for('login'))# 设置全局变量 g.request_start_time = time.time()
5.2.2 after_request
@app.after_requestdefafter_request(response):# 在每个请求之后执行print(f"Response sent: {response.status_code}")# 添加自定义响应头 response.headers['X-Frame-Options']='SAMEORIGIN' response.headers['X-Content-Type-Options']='nosniff'# 记录请求处理时间ifhasattr(g,'request_start_time'): processing_time = time.time()- g.request_start_time response.headers['X-Processing-Time']=str(processing_time)return response 
5.2.3 teardown_request
@app.teardown_requestdefteardown_request(exception=None):# 在请求结束时执行,即使发生异常也会执行# 常用于资源清理ifhasattr(g,'db_connection'): g.db_connection.close()
5.2.4 错误处理
@app.errorhandler(404)defnot_found_error(error):return render_template('404.html'),[email protected](500)definternal_error(error): db.session.rollback()# 发生错误时回滚数据库会话return render_template('500.html'),500# 全局异常处理defhandle_ex(error):return jsonify({"code":-1,"msg":f"{error}"})# 注册全局异常处理 app.register_error_handler(Exception, handle_ex)

5.3 自定义WSGI中间件

除了使用Flask的装饰器,还可以创建WSGI中间件。

5.3.1 基本WSGI中间件结构
classMiddleware:def__init__(self, app): self.app = app def__call__(self, environ, start_response):# 请求前处理print('请求前的操作')# 调用应用 response = self.app(environ, start_response)# 响应后处理print('请求之后操作')return response # 应用中间件if __name__ =='__main__': app.wsgi_app = Middleware(app.wsgi_app) app.run()
5.3.2 认证中间件示例
classAuthenticationMiddleware:def__init__(self, app): self.app = app def__call__(self, environ, start_response):# 从environ中获取请求信息 path = environ.get('PATH_INFO','') method = environ.get('REQUEST_METHOD','')# 跳过登录和静态资源if path =='/login'or path.startswith('/static/'):return self.app(environ, start_response)# 检查认证ifnot self.is_authenticated(environ):# 未认证,返回401错误 start_response('401 Unauthorized',[('Content-Type','text/html')])return[b'<h1>Unauthorized</h1>']# 已认证,继续处理请求return self.app(environ, start_response)defis_authenticated(self, environ):# 检查cookie或Authorization头 cookies = environ.get('HTTP_COOKIE','') auth_header = environ.get('HTTP_AUTHORIZATION','')# 这里实现具体的认证逻辑# ...returnTrue# 或False# 应用中间件 app.wsgi_app = AuthenticationMiddleware(app.wsgi_app)

5.4 实用中间件实现

5.4.1 日志记录中间件
import logging import time from flask import request, g classRequestLoggingMiddleware:def__init__(self, app): self.app = app self.logger = logging.getLogger('request_logger') self.logger.setLevel(logging.INFO)# 创建文件处理器 file_handler = logging.FileHandler('app.log') formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') file_handler.setFormatter(formatter) self.logger.addHandler(file_handler)def__call__(self, environ, start_response): start_time = time.time()defcustom_start_response(status, headers, exc_info=None):# 计算处理时间 processing_time = time.time()- start_time # 记录日志 self.logger.info(f"{request.remote_addr} - {request.method}{request.path} - "f"{status} - {processing_time:.2f}s - {request.user_agent}")return start_response(status, headers, exc_info)return self.app(environ, custom_start_response)# 或者使用装饰器方式@app.before_requestdeflog_request_info(): g.start_time = time.time() app.logger.info(f"Request: {request.method}{request.path} - IP: {request.remote_addr}")@app.after_requestdeflog_response_info(response):ifhasattr(g,'start_time'): processing_time = time.time()- g.start_time app.logger.info(f"Response: {response.status} - Time: {processing_time:.2f}s")return response 
5.4.2 跨域中间件(CORS)
classCORSMiddleware:def__init__(self, app, origins=None, methods=None, headers=None): self.app = app self.origins = origins or['*'] self.methods = methods or['GET','POST','PUT','DELETE','OPTIONS'] self.headers = headers or['Content-Type','Authorization']def__call__(self, environ, start_response):if environ['REQUEST_METHOD']=='OPTIONS':# 处理预检请求 start_response('200 OK',[('Access-Control-Allow-Origin',', '.join(self.origins)),('Access-Control-Allow-Methods',', '.join(self.methods)),('Access-Control-Allow-Headers',', '.join(self.headers)),('Access-Control-Max-Age','86400'),# 24小时])return[b'']defcustom_start_response(status, headers, exc_info=None):# 添加CORS头 headers.append(('Access-Control-Allow-Origin',', '.join(self.origins))) headers.append(('Access-Control-Allow-Credentials','true'))return start_response(status, headers, exc_info)return self.app(environ, custom_start_response)# 应用CORS中间件 app.wsgi_app = CORSMiddleware(app.wsgi_app)
5.4.3 速率限制中间件
from collections import defaultdict import time classRateLimitingMiddleware:def__init__(self, app, max_requests=100, time_window=60): self.app = app self.max_requests = max_requests self.time_window = time_window self.requests = defaultdict(list)def__call__(self, environ, start_response): client_ip = environ.get('REMOTE_ADDR') current_time = time.time()# 清理过期的请求记录if client_ip in self.requests: self.requests[client_ip]=[ t for t in self.requests[client_ip]if current_time - t < self.time_window ]# 检查是否超过限制iflen(self.requests[client_ip])>= self.max_requests: start_response('429 Too Many Requests',[('Content-Type','text/plain')])return[b'Rate limit exceeded. Please try again later.']# 记录当前请求 self.requests[client_ip].append(current_time)return self.app(environ, start_response)# 应用速率限制中间件 app.wsgi_app = RateLimitingMiddleware(app.wsgi_app, max_requests=100, time_window=60)

5.5 中间件最佳实践

5.5.1 中间件执行顺序

在Flask中,中间件的执行顺序很重要:

# 执行顺序: 1 → 2 → 视图函数 → 4 → [email protected]_request# 1defbefore1():print('before1')@app.before_request# 2defbefore2():print('before2')@app.after_request# 3defafter1(response):print('after1')return response @app.after_request# 4defafter2(response):print('after2')return response 

注意before_request钩子按注册顺序执行,after_request钩子按反向顺序执行。如果某个before_request钩子返回了响应,后续的before_request钩子和视图函数将不会执行,但所有已注册的after_request钩子仍会执行。

5.5.2 中间件组织与管理

对于大型项目,应该将中间件组织在单独的模块中:

# middleware/__init__.pydefinit_middleware(app):# 注册请求扩展 app.before_request(before_request) app.after_request(after_request)# 注册错误处理 app.register_error_handler(404, not_found_error) app.register_error_handler(500, internal_error)# 应用WSGI中间件 app.wsgi_app = AuthenticationMiddleware(app.wsgi_app) app.wsgi_app = CORSMiddleware(app.wsgi_app) app.wsgi_app = RequestLoggingMiddleware(app.wsgi_app)# middleware/request_middleware.pydefbefore_request():"""请求前的逻辑""" g.start_time = time.time() g.user_id = session.get('user_id') g.ip_address = request.remote_addr defafter_request(response):"""请求后的逻辑"""ifhasattr(g,'start_time'): processing_time = time.time()- g.start_time response.headers['X-Processing-Time']=f'{processing_time:.3f}s'return response # middleware/error_middleware.pydefnot_found_error(error):return render_template('errors/404.html'),404definternal_error(error): db.session.rollback()return render_template('errors/500.html'),500# 在应用中初始化中间件from middleware import init_middleware app = Flask(__name__) init_middleware(app)
5.5.3 性能考虑

使用中间件时需要注意性能影响:

  1. 避免阻塞操作:中间件中的操作应该尽可能高效,避免阻塞I/O操作
  2. 缓存昂贵操作:对于昂贵的操作,考虑使用缓存
  3. 选择性应用:不是所有中间件都需要应用于所有请求,可以根据路径或其他条件选择性应用
  4. 异步处理:对于耗时的操作,考虑使用异步处理或消息队列
# 选择性应用中间件@app.before_requestdefselective_middleware():if request.path.startswith('/api/'):# 只对API路由执行某些操作pass

6 总结

本文全面介绍了Flask框架的基本使用、项目配置、接口鉴权和自定义中间件的实现。通过深入理解这些概念和技术,您可以构建出健壮、安全且可扩展的Flask应用程序。

6.1 关键要点回顾

  1. Flask基础:Flask是一个轻量级但功能强大的Web框架,适合从简单应用到复杂系统的各种场景。
  2. 项目配置:Flask提供灵活的配置系统,支持多种配置源和环境特定的配置。
  3. 接口鉴权:实现了Session-based和Token-based(JWT)两种认证方式,以及基于角色的访问控制(RBAC)。
  4. 中间件:通过装饰器和WSGI中间件两种方式实现自定义中间件,处理跨切面关注点。

6.2 最佳实践建议

  1. 安全性:始终使用HTTPS,妥善管理密钥和密码,验证和清理所有用户输入。
  2. 性能:使用缓存,优化数据库查询,异步处理耗时任务。
  3. 可维护性:遵循模块化设计原则,使用蓝图组织大型项目,编写清晰的文档和测试。
  4. 监控:记录适当的日志,监控应用性能和错误。

6.3 进一步学习方向

要深入学习Flask和Web开发,可以考虑以下方向:

  1. 数据库集成:深入学习SQLAlchemy和数据库设计
  2. 异步编程:学习Flask的异步支持和相关技术
  3. 微服务架构:了解如何使用Flask构建微服务
  4. 测试:掌握Flask应用的测试策略和工具
  5. 部署和DevOps:学习容器化部署和CI/CD流程

Flask是一个强大而灵活的工具,随着经验的积累,您将能够更好地利用其功能来构建符合各种需求的Web应用程序。

在这里插入图片描述

Read more

利用Chatbox AI工具 | 一分钟打造小红书爆款

利用Chatbox AI工具 | 一分钟打造小红书爆款

文章目录 * 一、Chatbox AI工具 * 1.2 Chatbox AI特点 * 1.2.1 支持多种主流和新模型 * 1.2.2 实时联网搜索与查询 * 1.2.3 文档处理和图片聊天 * 1.2.4 AI图像生成 * 1.2.5 AI 生成的图标与可视化见解 * 1.2.7 隐私数据进行本地存储 * 1.3 Chatbox API 与第三方 API 的区别 * 二、Chatbox工具助力打造小红书高质量社交媒体 * 2.1 对标优质账号 * 2.2 点击"小红书文案生成器&

By Ne0inhk
AI员工——OpenCode、OpenClaw+Ollama的安装与配置

AI员工——OpenCode、OpenClaw+Ollama的安装与配置

人工智能(AI)相关的知识内容解析https://coffeemilk.blog.ZEEKLOG.net/article/details/158647749?spm=1001.2014.3001.5502 一、OpenCode的介绍与安装配置  1.1、OpenCode介绍 OpenCode的介绍序号Opencode介绍说明1opencode是什么OpenCode是一款开源AI编码代理工具,可在终端(TUI)、桌面应用和 IDE扩展中使用,支持多种大语言模型、上下文感知,主打隐私优先。2opencode的定位 《1》不是IDE插件,而是独立智能体(Agent),可理解上下文,规划任务、执行代码修改并验证结果。 《2》不是大语言模型本身,而是模型调度层,支持75+的大语言模型提供商(如:Claude、GPT、Gemini、本地的Llama、Qwen等)。 《3》采用MIT协议开源,社区活跃。

By Ne0inhk
【2026 最新】零基础也能用!Cursor 全面上手指南:AI 编程神器从安装到实战 带图详细展示(Windows 版)

【2026 最新】零基础也能用!Cursor 全面上手指南:AI 编程神器从安装到实战 带图详细展示(Windows 版)

一、什么是AI编程工具? 可以分为两类: 1.1 狭义的AI编程工具 面向程序员的,主要用于提升写代码的效率 * Cursor、Trae这样的AI编程IDE工具,就是专门为程序员提供的一个AI助手 * 功能有:生成代码、解释代码含义、优化代码结构、查找代码错误、回答编程问题 * 工具的主要作用是辅助编程,写代码变得很轻松 1.2 广义的AI编程工具 没有编程基础的人也能用的,可以自己创造应用 * 比如Coze、Dify这类智能体搭建平台 * 提供的功能有:可视化界面搭建、预设功能模块、简单逻辑配置、一键部署功能 * 平台提供搭积木的功能,用户组合积木就能实现各种应用程序 * 这类工具就降低了编程门槛,普通人都能用上 二、下载安装 Cursor 2.1 开始下载 下载官网:Cursor · Download 中文官网:Cursor: The best way to

By Ne0inhk

怎么把设计稿转为代码?推荐5个主流的AI代码生成工具

作为设计师的你,是否为设计稿无损转化为前端代码而烦恼?面对紧急的产品迭代需求,设计师的创意如何精准落地?在数字化产品快速发展的今天,可以说设计与开发的协作效率,很大程度上决定产品的上线速度。传统的“设计标注-前端手写”模式,不仅耗时耗力,还常常因为沟通偏差导致实现效果与设计创意相去甚远。 AI浪潮席卷而来,我们不禁要问:有没有一种更高效的方式,能够将设计稿一键生成代码,让创意与实现无缝对接? 今天这篇文章将为你介绍设计稿转代码的演变过程,并盘点5款国内外优秀的设计稿生成代码工具,包括Pixso、Figma、Anima、LocofyAI、BuilderIO,助力产设研团队效率翻倍!   设计稿转代码怎么转? 在了解具体的工具之前,我们可以先来看一下“设计稿转代码”的演变过程。过去,设计稿转代码高度依赖人力,设计师完成UI设计后,需要借助插件,在设计稿上进行尺寸、颜色、字号、间距等信息的标注。前端工程师则像需要对着标注图,逐个元素、逐行样式地用代码进行复现。 这种传统模式的痛点显而易见,沟通成本高、开发周期长、还原度难以保证。为了解决这些问题,最初的效率提升来自于设计软件

By Ne0inhk