简介
简单的总结和概括一下Flask框架基础的面试可能会出现的问题,不包含针对业务场景给出的解决方案,主要是方便用于回顾常用的知识点,文字性描述尽量精炼,减少长篇大论,提纲主要是结合cahtgpt给出的建议,对于部分遗漏或者可能暂时未涉及到的部分,没做处理。另外总结过程中感觉出现一些有用的资料地址,会整理到一起。有错误和纰漏,欢迎大家友好讨论!
提纲
Flask框架:
- 基本概念、定位、适用的业务场景以及和其他Python Web框架对比有什么区别?(已完成)
- 路由和视图(已完成)
- 请求和响应(已完成)
- 模板引擎(Jinja2)
- 蓝图(blueprint)(已完成)
- 请求上下文和会话(已完成)
- Flask扩展 (已完成)
- 中间件(已完成)
- 错误处理和日志(已完成)
- 部署(已完成)
- REST API、Restful风格、Flask-Restful扩展(已完成)
- 安全性
- 性能优化(已完成)
- 单元测试
- 项目经验
- 异步(线程池方式?celery异步任务框架)(根据工作岗位需求)
关于Flask
轻量的Python Web框架,提供最基本的工具,允许开发者自由的添加扩展和库。(和Django对比更加轻量,更加自由和灵活)
路由和视图
一般直接通过路由装饰器,添加对应的路由,和定义处理的方法,路径参数
@app.route("/index", methods=['GET'])
def index():
pass
请求和响应
- 通过request对象获取客户端发送的请求信息,请求头,请求表单,请求方法;
- 响应对象,可通过redner_template返回对应模板下的html文件,jsonify返回json对象,字符串直接返回;(高版本Flask对象直接返回字典会自动序列化成json对象)
模板引擎(Jinja2)
(主要业务是前后端分离,暂时不涉及到前端页面的数据渲染)
蓝图(Blueprint)
蓝图可以将应用程序划分为更小、模块化组件的方式;用以组织和拆分路由和视图;提高代码的可维护性。
蓝图创建
from flask import Blueprint
bp = Blueprint("auth", __name__, url_prefix="/auth", template_folder="templates")
@bp.route("/login", methods=["GET"])
def login():
pass
注册到APP
from flask import Flask
from . import auth
app = Flask(__name__)
app.register_blueprint(auth.bp)
蓝图嵌套
parent = Blueprint('parent', __name__, url_prefix='/parent')
child = Blueprint('child', __name__, url_prefix='/child')
parent.register_blueprint(child)
app.register_blueprint(parent)
请求上下文和会话
关于请求上下文和应用上下文有什么区别:
- 请求上下文:表示当前处理的HTTP请求的上下文信息;(请求的方法、URL、头部信息、表单数据)
- 应用上下文:表示应用程序的上下文信息;(应用配置、数据库连接、应用上下文是全局的,对于应用程序来说是唯一的,由Flask管理创建和销毁)
关于会话:
- 会话是用于不同请求之间存储用户数据的机制,允许在不同页面之间的请求保持用户的状态;
- 会话数据存放在客户端的Cookies中,通过签名保证数据的完整性和安全性;
- 在Flask中使用会话,是要先配置一个密钥,通常是通过SECRET_KEY来设置,用以签名Cookie数据;
app.config.from_mapping(
SECRET_KEY='66298503f1cce092aaf7a317c23c2480e615f3183536621f7644663e31541560',
DATABASE=os.path.join(app.instance_path, "flasker.sqlite")
)
快捷生成密钥:
python -c 'import secrets; print(secrets.token_hex())'
关于Flask框架请求的处理过程可参考:
Flask扩展
常用的Flask-SQLALChemy
自动通过已存在的数据库生成模型文件:
pip install flask-sqlacodegen
flask-sqlacodegen --flask --outfile <输出的文件名> <数据库连接 URI>
Mysql连接字符串:
dialect+driver://username:password@host:port/database
参考链接:
flaks-sqlalchemy使用操作
设置Mysql连接字符串
SQLALCHEMY_DATABASE_URI = "dialect+driver://username:password@host:port/database"
app读取配置后,初始化连接
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
db.init_app(app)
模型定义
from flasker import db
class User(db.Model):
__tablename__ = 'user'
id = db.Column(db.String(255), primary_key=True)
name = db.Column(db.String(255, 'utf8mb4_0900_ai_ci'), nullable=False, unique=True)
password = db.Column(db.String(255))
address = db.Column(db.String(255))
操作
# 查询
user = User.query.filter(User.name == username).first()
# 插入
user = User(id=str(uuid.uuid1()), name=username, password=generate_password_hash(password))
db.session.add(user)
db.session.commit()
Flask中间件
身份校验中间件(本质是通过装饰器校验目前登录用户的身份信息,如果没登录自动抛出异常然后处理)
第一种方式:
def login_required(view):
@wraps(view)
def wrap_view(*args, **kwargs):
if g.user is None:
abort(400)
return view(*args, **kwargs)
return wrap_view
@bp.route("/person-info", methods=["GET"])
@login_required
def person_blog_info():
return f"<h1>This is {g.user.name} personal page.</h1>"
第二种方式:
class AuthenticationMiddleware:
def __init__(self, app):
self.app = app
def __call__(self, environ, start_response):
print("start")
response = self.app(environ, start_response)
return response
app.wsgi_app = AuthenticationMiddleware(app.wsgi_app)
错误处理和日志
参考链接:
定义errorhandler错误处理,可指定处理对应状态码的错误。
@app.errorhandler(400)
def handle_exception(error):
app.logger.error("用户尝试访问需要权限的页面。")
return f"<h1 style='color: red;'> 用户未登录,无权访问....</h1>\n<a href='{url_for('auth.login')}'>请重新登录</a>"
日志处理器设置
def init_logger():
if not os.path.isdir("logs"):
os.makedirs("logs")
handler = TimedRotatingFileHandler(
filename="logs/flasker.log",
backupCount=7
)
handler.setLevel(logging.DEBUG)
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
handler.setFormatter(formatter)
app.logger.addHandler(handler)
参考链接:
docs.python.org/3/library/l… dormousehole.readthedocs.io/en/latest/l…
部署
Flask是一个WSGI应用,需要一个WSGI服务器来运行,将传入的HTTP请求转换为标准的WSGI请求,传递到Python应用程序,然后将返回的WSGI响应转换为HTTP响应返回给客户端。
uWSGI服务器部署应用,Nginx作为代理服务器
uwsgi.ini
[uwsgi]
chdir=/opt/app/flaskdemo
module=main:app
home=/opt/app/venv
static-map=/static=/opt/app/flaskdemo/flasker/static
threads=8
http=0.0.0.0:8992
master=true
vacuum=true
thunder-lock=true
uid=root
gid=root
harakiri=30
post-buffering=4096
socket=%(chdir)/uwsgi/uwsgi.sock
stats=%(chdir)/uwsgi/uwsgi.status
pidfile=%(chdir)/uwsgi/uwsgi.pid
daemonize=%(chdir)/uwsgi/uwsgi.log
nginx.conf
server {
listen 80;
server_name 120.77.144.48;
location / {
include uwsgi_params;
uwsgi_pass unix:/opt/app/flaskdemo/uwsgi/uwsgi.sock;
}
}
Restful 以及 flask-restful扩展
RESTful是一种设计风格,用于创建网络程序的API。 RESTful是一种通过URL标识资源,通过HTTP方法执行操作,使用标准的数据表述格式进行通信的方式。
flask-restful扩展使用,定义资源,解析传入参数,定义处理请求方式,绑定对应路由(blueprint)
# coding: utf-8
"""
:date: 2023-11-8
:author: linshukai
:description: About Restful API Demo
"""
from werkzeug.security import check_password_hash, generate_password_hash
from flask_restful import Resource, reqparse, Api
from flask import Blueprint
from faker import Faker
from flasker.model import User
from flasker import db, app
import uuid
bp = Blueprint("rest", __name__, url_prefix="/rest")
rest_api = Api(bp)
class PersonResource(Resource):
def get(self):
get_parser = reqparse.RequestParser()
get_parser.add_argument("name", type=str, location="args", required=True)
args = get_parser.parse_args()
fake = Faker()
return {"code": 200, "data": {"address": fake.address(), "name": args.name}, "msg": "success"}
def post(self):
post_parser = reqparse.RequestParser()
post_parser.add_argument("name", type=str, location="args", required=True)
post_parser.add_argument("address", type=str, location="json", required=True)
post_parser.add_argument("age", type=int, location="json", required=True)
args = post_parser.parse_args()
user = User(id=str(uuid.uuid1()), name=args.name, password=generate_password_hash("admin"),
address=args.address)
try:
db.session.add(user)
db.session.commit()
except Exception as e:
db.session.rollback()
app.logger.error(f"数据库错误:{e}")
raise e
return {"code": 200, "msg": "success"}
rest_api.add_resource(PersonResource, "/person")
参考链接:
flask-restful.readthedocs.io/en/latest/ juejin.cn/post/723895…
性能分析
通过werkzeug的组件进行分析
app.wsgi_app = ProfilerMiddleware(app.wsgi_app, restrictions=(10,))
只显示耗时排序前十的调用耗时的方法
参考链接: