一文掌握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

【全网最全的的本地部署Code Agent攻略参考】跃阶星辰AI开源Step-3.5-Flash

【全网最全的的本地部署Code Agent攻略参考】跃阶星辰AI开源Step-3.5-Flash

1. 简介 Step 3.5 Flash(访问官网)是我们目前最强大的开源基础模型,专为提供前沿推理与智能体能力而设计,同时具备卓越的效率。基于稀疏混合专家(MoE)架构,它每处理一个token仅激活1960亿参数中的110亿。这种"智能密度"使其推理深度可比肩顶级闭源模型,同时保持实时交互所需的敏捷性。 2. 核心能力 * 高速深度推理:聊天机器人擅长阅读,而智能体必须快速推理。通过三路多token预测(MTP-3)技术,Step 3.5 Flash在典型使用场景中实现100-300 tok/s的生成吞吐量(单流编码任务峰值达350 tok/s),能即时响应复杂的多步推理链条。 * 编码与智能体的强力引擎:Step 3.5 Flash专为智能体任务打造,集成可扩展的强化学习框架驱动持续自我进化。其SWE-bench Verified通过率74.4%,Terminal-Bench 2.0通过率51.

By Ne0inhk

GitHub开源免费PDF编辑器推荐:告别破解,高效编辑PDF

文章目录 * 1. PDF补丁丁:国产良心工具箱 * 简介 * 主要功能 * 开源地址 * 特点 * 2. Stirling-PDF:功能全面的PDF处理工具 * 简介 * 主要功能 * 安装与部署 * Docker Compose示例 * OCR中文模型安装 * 开源地址 * 特点 * 3. pdf-lib:强大的JavaScriptPDF库 * 简介 * 主要功能 * 安装 * 代码示例 * 开源地址 * 特点 * 4. gofpdf:Go语言的PDF库 * 简介 * 主要功能 * 安装 * 代码示例 * 开源地址 * 特点 * 总结与选择建议 还在为寻找免费的PDF编辑工具而烦恼吗?或许你曾使用过某些需要付费或破解的软件,不仅存在安全风险,还可能功能受限。本文将介绍几款GitHub上优秀的开源免费PDF编辑器,让你无需破解也能轻松处理PDF文档。 1. PDF补丁丁:国产良心工具箱 简介 PDF补丁丁是一款由国内开

By Ne0inhk
Git原理与使用(一)

Git原理与使用(一)

文章目录 * 什么是Git * Git:版本控制器 * Git安装 * Git基本操作 * 初始化本地仓库 * 配置Git * 工作区、暂存区、版本库 * 各自含义 * 添加文件 * 查看 .git 目录 当我们在学习或工作中可能会使用到同一种文档的不同版本,我们该如何快速准确的获得各种版本呢 什么是Git Git:版本控制器 为了能够更方便的管理不同版本的文件,便有了版本控制器。所谓的版本控制器,就是一个可以记录工程的每一次改动和版本迭代的⼀个管理系统,同时也方便多人协同作业。目前最主流的版本控制器就是 Git 。Git 可以控制电脑上所有格式的文件,例如 doc、excel、dwg、dgn、rvt等等。对于开发人员来说,Git 最重要的就是可以帮助管理软件开发项目中的源代码文件! 注意: 所有的版本控制器,只能追踪文本文件的改动,比如.txt文件、各种程序代码等,每次改动时都可以知道具体的改动,比如某.txt文件第三行新增linux单词,第五行删除ZEEKLOG单词。

By Ne0inhk
BeyondCompare安装(永久免费使用+全网最详细版)

BeyondCompare安装(永久免费使用+全网最详细版)

一.下载: * 阿里云盘(不限速) https://www.alipan.com/s/WaG1z54BQ2U 官网下载(速度较慢): https://www.scootersoftware.com/download.php 二.安装(无脑下一步即可) 三.永久免费使用: 1. 在搜索栏中输入 regedit ,打开注册表 2. 删除项目:计算机 \HKEY_CURRENT_USER\Software\ScooterSoftware\Beyond Compare 4\CacheId 修改注册表 四.每周自动删掉CacheId: 1.创建删除CacheId脚本,命名为freshBeyondcompare4.bat(注意:这里不要放在有中文路径的文件夹下) ```python # 内容如下:

By Ne0inhk