摘要
主要是介绍的Flask的原理和实现。主要是的在python的开发中常用Flask的框架。本博文对Flask的原理和应用进行详细的学习和介绍。
Flask
Flask本身相当于一个内核,其他几乎所有的功能都要用到扩展(邮件扩展Flask-Mail,用户认证Flask-Login,数据库Flask-SQLAIchemy),都需要用第三方的扩展来实现。比如可以用Flask扩展ORM体验证工具,文件上传、身份验证等。Flask没有默认使用的数据库,你可以选择MySQL,也可以用NoSQL。其WSGI工具箱采用Werkzeug (路由模块),模板引擎则使用Jinja2。这两个也是Flask框架的核心。
框架的对比
1)框架轻重
- 重量级的框架:为方便业务程序的开发,提供了丰富的工具、组件,如Django
- 轻量级的框架︰只提供Web框架的核心功能,自由、灵活、高度定制,如Flask、Tornado
2)与Django对比
- 1django提供了︰django-admin快速创建项目工程目录
- 2manage.py管理项目工程
- 3orm模型(数据库抽象层)
- 4admin后台管理站点缓存机制
- 缓存机制
- 文件存储系统
- 用户认证系统
Flask的扩展
- Flask-SQLalchemy:操作数据库;Flask-script:插入脚本;
- Flask-migrate:管理迁移数据库;
- Flask-Session: Session存储方式指定;Flask-WTF:表单;
- Flask-Mail:邮件;
- Flask-Bable:提供国际化和本地化支持,翻译;Flask-Login:认证用户状态;
- Flask-OpenID:认证;
- Flask-RESTful:开发REST API的工具;
- Flask-Bootstrap:集成前端Twitter Bootstrap框架;Flask-Moment:本地化日期和时间;
- Flask-Admin:简单而可扩展的管理接口的框架
工程的搭建
1安装一个虚拟的python的环境(minconda):Conda——创建新的虚拟环境_庄小焱
from flask import Flask
#定义一个flask第一个参数__name__
app = Flask(__name__)# ->定义为字符串类型的模块名
@装饰器的作用是将映射到试图函数的index
@app.route('/')
def hello_world():
return 'Hello World!'
# flask的应用程序的run方法启动web服务器
if __name__ == '__main__':
app.run()
Flask对象的参数化
Flask对象的初始化参数
Flask 程序实例在创建的时候,需要默认传入当前Flask程序所指定的包(模块),接下来就来详细查看一下Flask应用程序在创建的时候一些需要我们关注的参数:
import_name:
- Flask程序所在的包(模块),传__name__就可以。
- 其可以决定Flask在访问静态文件时查找的路径.
static_url_path
- 静态文件访问路径,可以不传,默认为︰/ + static_folder.
# app = Flask(__name_)#-> Flask(模块名字符串类型)
app = Flask(_name__, static_url_path='/s ')
static_folder
- 静态文件存储的文件夹,可以不传,默认为static
template_folder
- 模板文件存储的文件夹,可以不传,默认为templates
Flask将配置信息保存到了app.config属性中,该属性可以按照字典类型进行操作。
读取:app.config.get(name) app.config[name]
从配置对象中加载
app.config.from_object(配置对象)
from flask import Flask
# 配置对象的方式进行加载 继承复用 代码可以复用 但是敏感信息暴露
class DefaultConfig(object):
"""默认的配置"""
SECRECT_KEY = "agfjagfjfaglfjkdfh;a"
app = Flask(__name__, static_url_path='/s', static_folder="/static")
# 从类中进行加载
app.config.from_object(DefaultConfig)
# 配置文件中加载
# app.config.from_pyfile("./setting.py")
# 从环境变量加载变量 从环境变量指向的配置文件中读取的配置信息会覆盖揄从配置对象中加载的同名参数
app.config.from_envvar('PROJECT_SETTING')
app.config.from_envvar('PROJECT_SETTING', slice=True)
@app.route('/')
def hello_world():
print(app.config['SECRECT_KEY'])
return 'Hello World!'
if __name__ == '__main__':
app.run()
export变量名=变量值#设置
echo $变量名#读取
#例如
export ITCAST=python
echo $ITCAST
app.run()运行参数
app.run(host="0.0.0.0", port=5000,debug = True)
环境变量FLASK_APP指明flask的启动实例
flask run -h 0.0.0.0 -p 8000绑定地址端口
flask run --help获取帮助
生产模式与开发模式的控制
通过FLASK_ENV环境变量指明
export FLASK_ENV=production运行在生产模式,未指明则默认为此方式
export FLASK_ENV=development运行在开发模式
Flask的请求原理
查询路由信息 命令行方式:flask routes(*)
# 获取的flask的所有的路由的路径信息
print(app.url_map)
Map([<Rule '/' (GET, HEAD, OPTIONS) -> hello_world>,
<Rule '/s/<filename>' (GET, HEAD, OPTIONS) -> static>])
# 获取的flask的所有的路由的路径信息
for rule in app.url_map.iter_rules():
print("name={},path={}".format(rule.endpoint, rule.rule))
请求方式
- GET
- OPTIONS:简化版的GET请求用于询问服务器接口信息的
- CORS(跨域的访问):djanggo -cors ->中间都是拦截了options请求的。
比如接口允许的请求方式允许的请求源头域名
vwwvw.meiduo.site ->api.meiduo.site/users/1o
options api.meiduo.site/uses/1
返回response -> allow-origin
'www.meiduo.site'o GET api.meiduo.site/users/1
- HEAD(自带)简化版GET请求 只返回GET请求处理时的响应头头,不返回响应体
Flask的蓝图原理
在Flask中,使用蓝图Blueprint来分模块组织管理。蓝图实际可以理解为是一个存储一组视图方法的容器对象,其具有如下特点:
- 一个应用可以具有多个Bqeprint
- 可以将一个Blueprint注册到任何一个未使用的URL下比如“/user”、“/goods”
- Blueprint可以单独具有自己的模板、静态文件或者其它的通用操作方法,它并不是必须要实现应用的视图和函数的
- 在一个应用初始化时,就应该要注册需要使用的Blueprint
但是一个Blueprint并不是一个完整的应用,它不能独立于应用运行,必须要注册到某一个应用中。
使用蓝图可以分为三个步骤
1.创建一个蓝图对象
user_bp=$lueprint( 'user',__name__)
2.在这个蓝图对象上进行操作,注册路由,指定静态文件夹,注册模版过滤器
@user_bp.route( ' / ')
def user_profile( ):
return 'user_profile'
3.在应用对象上注册这个蓝图对象
app.register_blueprint(user_bp)
Flask 启动流程
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return "hello 懒编程"
if __name__ == '__main__':
app.run()
在代码中,通过 Flask(__name__)
实例化了 Flask 类,对应的__init__() 方法如下
# flask/app.py
classs Flask(_PackageBoundObject):
def __init__(...):
# 对每次请求,创建一个处理通道。
self.config = self.make_config()
self.view_functions = {}
self.error_handler_spec = {}
self.before_request_funcs = {}
self.before_first_request_funcs = []
self.after_request_funcs = {}
self.teardown_request_funcs = {}
self.teardown_appcontext_funcs = []
self.url_value_preprocessors = {}
self.url_default_functions = {}
self.url_map = Map()
self.blueprints = {}
self._blueprint_order = []
self.extensions = {}
__init__() 方法中有大量的注释,注释中解释了这些变量的用途,但仅从变量名就知道,它们用于存储每次请求对应的信息,相当于一个处理通道。Flask 实例化后,接着利用 @app.route('/')
装饰器的方式将 hello () 方法映射成了路由,相关代码如下:
可以发现 route () 方法就是一个简单的装饰器,具体处理逻辑在 addurlrule () 方法中。
从 addurlrule () 方法可以看出, @app.route('/')
的主要作用就是将路由保存到 urlmap 中,将装饰的方法保存到 viewfunctions 中。需要注意的是,每个方法的 endpoint 必须不同,否则会抛出 AssertionError。最后调用了 app.run () 方法运行 Flask 应用,对应代码如下。
run () 方法进一步调用 werkzeug.serving 下的 run_simple () 方法启动 web 服务,其中 self 就是 Flask () 的 application。
逐层深入,runsimple() -> makeserver() -> BaseWSGIServer() -> WSGIRequestHandler
WSGIRequestHandler 类从名称就可以知,它主要用于处理满足 WSGI 协议的请求,该类中的 execute () 方法部分代码如下。
简单而言,app.run () 会启动一个满足 WSGI 协议的 web 服务,它会监听指定的端口,将 HTTP 请求解析为 WSGI 格式的数据,然后将 environ, start_response 传递给 Flask () 实例对象。
类对象作为方法被调用,需要看到__call__() 方法,代码如需。
主要逻辑在 wsgiapp () 方法中,一开始进行了请求上下文的处理 (后面文章单独剖析 Flask 上下文相关源码),随后通过 fulldispatch_request () 方法找到当前请求路由对应的方法,调用该方法,获得返回,如果请求路由不存在,则进行错误处理,返回 500 错误。
fulldispatchrequest () 方法中最关键的逻辑在于 dispatch_request () 方法,该方法会将调用对应路由的处理函数并获得该函数的结果。此外还有 trytriggerbeforefirstrequestfunctions()、preprocessrequest()、finalizerequest () 方法,这些方法会执行我们自定义的各种钩子函数,这些钩子函数会存储在 beforerequestfuncs()、beforefirstrequestfuncs()、afterrequestfuncs () 方法中。最后,需要注意,app.run () 方法仅在开发环境中会被使用,通过上面的分析,已经知道 app.run () 背后就是使用 werkzeug 构建了一个简单的 web 服务,但这个 web 服务并不牢靠,生产环境通常利用 uWSGI,通过配置的形式,指定 WSGI app 所在文件来启动 Flask 应用。