您现在的位置是:首页 >其他 >使用Flask高效构建Web应用网站首页其他
使用Flask高效构建Web应用
1、聊聊Flask框架
Flask是Armin ronacher基于Python开发的微型Web框架,诞生于2010年,它依赖于jinja2模板和Werkzeug WSGI服务。Flask的核心简单易于扩展,它不会替你做出太多决策比如使用何种数据库或模板引擎,这些都可以根据自己的需求进行选择和替换。Flask的设计理念是让发人员可以根据自己的需求进行自定义,同时提供足够的灵活性和可扩展性。
2、Falsk常用第三方扩展包
可在Flask官方文档的扩展包页面中找到许多常用的Flask扩展包。
插件 | 功能 |
---|---|
Flask-SQLAlchem | 使用SQLAlchemy ORM |
Flask-Login | 处理用户认证和会话管理 |
Flask-Uploads | 处理文件上传 |
Flask-Admin | 构建管理界面 |
Flask-WTF | 处理Web表单 |
Flask-RESTful | 构建RESTful API |
Flask-Mail | 发送电子邮件 |
Flask-Caching | 缓存数据 |
Flask-Jwt-Extended | 处理JSON Web Tokens |
Flask-SocketIO | 处理WebSocket通信 |
Flask-Migrate | 处理数据库迁移 |
Flask-Assets | 处理静态资源 |
Flask-Cors | 实现跨域 |
Flask-Bcrypt | 实现加密 |
3、Flask钩子函数
钩子函数 | 执行时间 | 特别说明 |
---|---|---|
before_first_request | 项目初始化时的钩子 | |
before_request | 在每次请求前执行 | |
after_reauest | 如果没有抛出错误,在每次请求后执行 | 接收视图函数做出的响应 |
teardown_request | 在每次请求后执行 | 接收错误信息,如果有相关错误抛出 |
4、Flask路由
路由和视图的名称必须全局统一,不能出现重复,否则报错
4.1 flask默认支持的转换器
名称 | 描述 |
---|---|
string | 默认类型,接受不带斜杠的任何文本 |
int | 接受正整数 |
float | 接受正浮点值 |
path | 接收string但也接受斜线 |
uuid | 接受UUID字符串 |
4.2 任意路由参数
# 不限定类型
@app.route('/user/<params>')
def user_info(params):
return 'params %s' % params
# 限定数据类型 中间不能有空格
@app.route('/user/<int:uid>')
def user_info(uid):
return 'uid %d' % uid
4.3 自定义路由转换器
from werkzeug.routing import BaseConverter
class MobileConverter(BaseConverter):
"""check mobile"""
def __init__(self,map,*args):
super().__init__(map)
self.regex = "1[3-9]d{9}"
app.url_map.converters['mobile'] = MobileConverter
@app.route('/user/<mobile:mobile>', methods=['get', 'post'])
def test_view(mobile):
return mobile
5、HTTP请求与响应
flask中使用request获取当前请求的对象
5.1 请求
获取常用的请求信息:
信息 | 描述 | 示例 |
---|---|---|
request.method | 请求的HTTP方法 | GET, POST, PUT, DELETE等等 |
request.url | 请求的完整URL | http://example.com/path?query=string |
request.headers | 请求头部的字典形式 | {‘Content-Type’: ‘application/json’} |
request.args | URL中的查询参数 | {‘key’: ‘value’} |
request.form | 表单数据 | {‘username’: ‘john’, ‘password’: ‘123’} |
request.files | 上传的文件 | {‘file’: } |
request.cookies | 请求中的Cookie | {‘session_id’: ‘abc123’} |
request.remote_addr | 客户端的IP地址 | 127.0.0.1 |
request.user_agent | 发起请求的UA | Mozilla/5.0 … |
request.json | Json格式的请求体数据 | {‘key’: ‘value’} |
request.data | 请求体的原始数据(字节形式) | b’raw data’ |
获取请求示例:
from flask import request, jsonify
from application.apps.index import index_blueprint
@index_blueprint.route("/index")
def index():
data = request.get_json()
# 数据处理...
return jsonify(data='welcome flask!'), 200
5.2 响应
flask默认支持两种数据响应和页面响应
数据响应
from flask import make_response, jsonify
@app.route("/")
def index():
# 默认响应html
return '<h1>hello world!</h1>'
# 响应html
return make_response('<h1>hello world!</h1>')
# 响应json
return jsonify(msg='hello world!')
页面响应
from flask import redirect
@app.route("/")
def handle_request():
# 重定向
return redirect("http://www.example.com")
# 视图方法内部跳转
return redirect(url_for("view funcname"))
# 视图方法内部跳转携带参数
return redirect(url_for("view funcname", params-name=''))
自定义http响应内容
from flask import make_response
@app.route("/")
def handle_request():
response = Response('Custom Response')
response.headers['Content-Type'] = 'text/plain'
response.status_code = 200
return response
6、异常捕获
6.1 主动抛出异常
可以使用abort方法抛出一个给定的状态码的HTTPException或者指定响应,一般用于权限校验等页面上错误展示
# 参数HTTP状态码
abort(500)
6.2 捕获自定义异常
初始化app的时候可以将捕获异常的方法注册,也可以使用装饰器
def exception_method_not_allow(error):
logging.error("An exception occurred: {}".format(str(error)))
return jsonify(msg='method not allowed'), 405
# 第一个参数可以是异常类型或者状态码
app.register_error_handler(405, exception_method_not_allow)
# 捕获自定义异常
@app.errorhandler(ZeroDivisionError)
def zero_division_error(e):
return '除数不能为0'
7、context
执行上下文:即语境,语意,在程序中可以理解为在代码执行到某一行时,根据之前代码所做的操作以及下文即将要执行的逻辑,可以决定在当前时刻下可以使用到的变量,或者可以完成的事情。Flask中上下文对象:相当于一个容器,保存了程序运行过程中的变量、函数、类与对象等信息。Flask中有请求上下文和应用上下文。
- 请求:发生http请求时,调用
Flask.__call__()
之后,在Flask
对象内部创建的Request
对象; - 应用:调用
app = Flask(__name__)
创建的这个对象app
,
7.1 请求上下文
在flask中,可以直接在视图函数中使用request这个对象进行获取相关数据,request就是请求上下文的对象,保存了当前本次请求的相关数据,请求上下文对象有:request、session。
request封装了HTTP请求的内容,针对的是http请求。session用来记录用户会话中的信息。
7.2 应用上下文
应用上下文,它不是一直存在的,只是request context 中操作当前falsk应用对象app的代理(local proxy)。它的作用主要是帮助 request 获取当前的flask应用相关的信息,它是伴request而生,随request 而灭的。
current_app是应用程序上下文,用于存储应用程序中的变量,可以通过current_app.name打印当前app的名称,也可以在current_app中存储一些变量,例如:
- 应用的启动脚本是哪个文件,启动时指定了哪些参数
- 加载了哪些配置文件,导入了哪些配置
- 连接了哪个数据库
- 有哪些可以调用的工具类、常量
- 当前flask应用在哪个机器上,哪个IP上运行,内存多大
from flask import current_app
def index():
print(current_app.config) # 获取当前项目的所有配置信息
print(current_app.url_map) # 获取当前项目的所有路由信息
return "<h1>hello world!</h1>"
7.3 g对象
g对象是flask程序全局的一个临时变量,充当者中间媒介的作用,我们可以通过它传递一些数据,g对象保存的是当前请求的全局变量,不同的请求会有不同的全局变量,通过不同的thread-id区别。
from flask import Flask, g
app = Flask(__name__)
@app.before_request
def before_request():
g.name = "root"
@app.route(rule='/')
def index():
print(g.name) # 获取到全局变量root
return "<h1>hello world!</h1>"
if __name__ == '__main__':
app.run()
8、蓝图 Blueprint
Blueprint是一个存储视图方法的容器,这些操作在这个Blueprint被注册到一个应用之后就可以被调用,Flask可以通过Blueprint来组织URL以及处理请求。Flask使用Blueprint让应用实现模块化,Blueorint具有如下属性:
- 一个项目可以具有多个Blueprint
- 可以将一个Blueprin注册到任何一个未使用的URL下比如"/“、”/api"或者子域名
- 在一个应用中,一个模块可以注册多次
- Blueorint可以单独具有自己的模板、静态文件或者其它的通用操作方法
- 在一个应用初始化时,就应该要注册需要使用的Blueprint
8.1 蓝图使用
示例可以通过:
http://127.0.0.1:5000/api/v1/index
访问到蓝图中定义的视图函数
# 创建一个蓝图模块
# __init__.py
from flask import Blueprint
index_blueprint = Blueprint("index_blueprint", __name__)
from application.apps.index.view import *
# views.py
from flask import jsonify
from application.apps.index import index_blueprint
@index_blueprint.route("/index")
def index():
return jsonify(data='welcome flask!'), 200
# 注册蓝图到应用
app.register_blueprint(index_blueprint, url_prefix='/api/v1')
8.2 运行机制
- 蓝图是保存了一组将来可以在应用app对象上执行的操作,注册路由就是一种操作
- 当在app对象上调用route装饰器注册路由时,这个操作将修改对象的url_map路由表
- 然而,蓝图对象根本没有路由表,当我们在蓝图对象上调用route装饰器注册路由时,它只是在内部的一个延迟操作记录列表defered_functions中添加了一个项
- 当执行了app对象的register_blueprint()方法时,app应用对象将从蓝图对象的defered_functions列表中取出每一项,并以自身作为参数执行该匿名函数,即调用app应用对象的add_url_rule()方法,将蓝图的路由信息全部注册到应用对象app的url_map路由表
8.3 蓝图静态文件
和app应用对象不同,蓝图对象创建时不会默认注册静态目录的路由。需要我们在创建时指定static_folder
参数。下面的示例将蓝图所在目录下的static_home目录设置为静态目录。有了蓝图的静态目录以后,并不会影响原来项目中提供的static总静态文件目录的使用。
# index/__init__.py
from flask import Blueprint
index_blueprint = Blueprint("index_blueprint", __name__, static_folder="static_index")
9、实现异步
如果在安装 Flask 时使用了额外的 async
( 即用 pip install flask[async]
命令安装),那么路由、出错处理器、请求前、请 求后和拆卸函数都可以是协程函数。这样,视图可以使用 async
定义,并使用 await
。
import logging
import asyncio
from flask import jsonify
from application.apps.index import index_blueprint
from application.extensions import redis_cli
@index_blueprint.route("/index")
async def index():
# 模拟耗时
await asyncio.sleep(1)
return jsonify(data='welcome flask!'), 200
9.1 性能
异步函数需要一个事件循环来运行。 Flask作为WSGI应用,使用一个worker来处理一个请求响应周期。当请求进入异步视图时, Flask会在一个线程中启动一个事件循环,在其中运行视图函数,然后返回结果。即使对于异步视图,每个请求仍然会绑定一个worker。好处是您可以在一个视图内运行异步代码,例如多个并发数据库查询,对外部API的HTTP请求,等等。但是,应用程序可以处理的请求并发数量将保持不变。
异步本质上并不比同步快。 在执行并发IO绑定任务时, 异步是有益的。但是对于CPU密集型任务,则未必有用,因此传统的Flask视图仍然适用于大多数用例。 Flask异步支持的引入,带来本地化编写和使用异步代码的可能性。
10、搭建一个合理的项目架构
适合一个中型项目使用,区分模块,区分环境
11、 总结
从 Flask 0.11 开始,Flask已经内置了一个命令行工具,因此不再需要使用Flask-Script,会产生包不兼容的问题。使用flask manage.py方式启动项目应该执行export FLASK_APP=manage.py,指定flask入口文件,另外,flask-sqlalchemy是flask的ORM工具,参考SqlAlchemy使用入门。