摘要
本文部分内容来源于网络,个人收集整理,请勿传播
Flask是一个基于Python开发并且依赖jinja2模板和Werkzeug WSGI服务的一个微型框架,对于Werkzeug本质是Socket服务端,其用于接收http请求并对请求进行预处理,然后触发Flask框架,开发人员基于Flask框架提供的功能对请求进行相应的处理,并返回给用户,如果要返回给用户复杂的内容时,需要借助jinja2模板来实现对模板的处理,即:将模板和数据进行渲染,将渲染后的字符串返回给用户浏览器。
“微”(micro)并不表示你需要把整个Web应用塞进单个Python文件(虽然确实可以),也不意味着 Flask 在功能上有所欠缺。微框架中的”微”意味着Flask旨在保持核心简单而易于扩展。Flask不会替你做出太多决策——比如使用何种数据库。而那些Flask所选择的——比如使用何种模板引擎——则很容易替换。除此之外的一切都由可由你掌握。如此,Flask可以与您珠联璧合。
默认情况下,Flask不包含数据库抽象层、表单验证,或是其它任何已有多种库可以胜任的功能。然而,Flask 支持用扩展来给应用添加这些功能,如同是 Flask 本身实现的一样。众多的扩展提供了数据库集成、表单验证、上传处理、各种各样的开放认证技术等功能。Flask 也许是“微小”的,但它已准备好在需求繁杂的生产环境中投入使用。
- 配置文件
- 路由系统
- 模板语言
- 请求&响应
- session&cookie
- 闪现
- 蓝图
- 请求扩展(django中间件)
- 中间件
常见的web框架
- Django:有很多组件
- ORM
- Form
- ModelForm
- 缓存
- Session
- 中间件
- 信号
- Flask:短小精悍,内部没有太多组件,第三方组件丰富
- Tornado:异步非阻塞(node.js)
Web框架本质
众所周知,对于所有的Web应用,本质上其实就是一个socket服务端,用户的浏览器其实就是一个socket客户端。
1 | #!/usr/bin/env python |
上述通过socket来实现了其本质,而对于真实开发中的python web程序来说,一般会分为两部分:服务器程序和应用程序。服务器程序负责对socket服务器进行封装,并在请求到来时,对请求的各种数据进行整理。应用程序则负责具体的逻辑处理。为了方便应用程序的开发,就出现了众多的Web框架,例如:Django、Flask、web.py 等。不同的框架有不同的开发方式,但是无论如何,开发出的应用程序都要和服务器程序配合,才能为用户提供服务。这样,服务器程序就需要为不同的框架提供不同的支持。这样混乱的局面无论对于服务器还是框架,都是不好的。对服务器来说,需要支持各种不同框架,对框架来说,只有支持它的服务器才能被开发出的应用使用。这时候,标准化就变得尤为重要。我们可以设立一个标准,只要服务器程序支持这个标准,框架也支持这个标准,那么他们就可以配合使用。一旦标准确定,双方各自实现。这样,服务器可以支持更多支持标准的框架,框架也可以使用更多支持标准的服务器。
WSGI(Web Server Gateway Interface)是一种规范,它定义了使用python编写的web app与web server之间接口格式,实现web app与web server间的解耦。
python标准库提供的独立WSGI服务器称为wsgiref。
1 | from wsgiref.simple_server import make_server |
werkzeug
1 | from werkzeug.wrappers import Request, Response |
Flask
基本使用
1 | from flask import Flask |
配置文件
1 | flask中的配置文件是一个flask.config.Config对象(继承字典),默认配置为: |
路由系统
- @app.route(‘/user/
‘) - @app.route(‘/post/int:post_id‘)
- @app.route(‘/post/float:post_id‘)
- @app.route(‘/post/path:path‘)
- @app.route(‘/login’, methods=[‘GET’, ‘POST’])
- @app.route(‘/index/int:nid‘, methods=[‘GET’, ‘POST’],endpoint=’index’)
- url_for反向解析url
常用路由系统有以上五种,所有的路由系统都是基于一下对应关系来处理:
1 | DEFAULT_CONVERTERS = { |
注册路由原理
1 | def auth(func): |
@app.route和app.add_url_rule参数:
- rule, URL规则
- view_func, 视图函数名称
- defaults=None, 默认值,当URL中无参数,函数需要参数时,使用defaults={‘k’:’v’}为函数提供参数
- endpoint=None, 名称,用于反向生成URL,即: url_for(‘名称’)
- methods=None, 允许的请求方式,如:[“GET”,”POST”]
- strict_slashes=None, 对URL最后的 / 符号是否严格要求,
1 |
|
- redirect_to=None, 重定向到指定地址
1 |
|
- subdomain=None, 子域名访问
1 | from flask import Flask, views, url_for |
自定制正则路由匹配
1 | from flask import Flask, views, url_for |
模板
模板的使用
Flask使用的是Jinja2模板,所以其语法和Django无差别
自定义模板方法
Flask中自定义模板方法的方式和Bottle相似,创建一个函数并通过参数的形式传入render_template,如
html
1 | <!DOCTYPE html> |
run.py
1 | #!/usr/bin/env python |
其他
1 | <!DOCTYPE html> |
注意:Markup等价django的mark_safe
请求和响应
1 | from flask import Flask |
Session
除请求对象之外,还有一个session对象.它允许你在不同请求间存储特定用户的信息,它是在Cookies的基础上实现的,并且对Cookies进行密钥签名要使用会话,你需要设置一个密钥.
- 设置 session[‘username’] = ‘xxx’
删除 session.pop(‘username’, None)
设置cookie时,如何设定关闭浏览器则cookie失效
- response.set_cookie(‘k’,’v’,exipre=None)
基本使用
1 | from flask import Flask, session, redirect, url_for, escape, request |
自定义Session
1 | pip3 install Flask-Session |
第三方session
1 | #!/usr/bin/env python |
蓝图
蓝图用于为应用提供目录划分:
- 构造程序目录
- 可以自己造,来回导入
- 蓝图
- 批量url前缀
- 自定模板路径、静态文件路径
- 请求扩展
- 针对app,所有蓝图生效
- 针对某个蓝图做:用户验证
- 小型应用程序:示例
- 大型应用程序:示例
- 蓝图对象名称和函数名称不要重复
其他:
- 蓝图URL前缀:xxx = Blueprint(‘account’, name,url_prefix=’/xxx’)
- 蓝图模板目录优先路径:xxx = Blueprint(‘account’, name,template_folder=’tpl’)
- 蓝图子域名:xxx = Blueprint(‘account’, name,subdomain=’admin’)
- 前提需要给配置SERVER_NAME: app.config[‘SERVER_NAME’] = ‘wupeiqi.com:5000’
- 访问时:admin.wupeiqi.com:5000/login.html
message
message是一个基于Session实现的用于保存数据的集合,其特点是:使用一次就删除。
1 | from flask import Flask, flash, redirect, render_template, request, get_flashed_messages |
应用:对临时数据的操作,比如错误信息
1 | from flask import flash |
中间件
1 | from flask import Flask, flash, redirect, render_template, request |
请求扩展
- @app.after_request 类似django的process_request 常用
- 可以用来写登录验证
- 用request.path设置白名单
- @app.after_request 类似django的process_response 常用
- after_request请求拦截后after_request还会执行
- @app.errorhandler(code) 定制错误信息
- @app.template_global() @app.template_filter() 定制模板中用的方法
- @app.before_first_request 第一次请求执行的函数
1 | #!/usr/bin/env python |
上下文管理
- ThreadLocal
- 源码(request)
- 单进程单线程,基于全局变量
- 单进程多线程,threading.local对象
- 单进程单线程(多个协程),threading.local对象做不到
1 | import threading |
others
1 |
|
上下文
- threading.Local对每个线程保存自己的值,而flask定义了一个Local对象支持协程、线程将数据存储到单独的空间
- 首先将请求相关的数据environ封装到了RequestContext对象(Request、Session)
- ctx再通过LocalStack对象将RequestContext对象放到Local中(每个线程、协程独立空间存储)
- 视图函数就可以调用request、session
- 调用这些数据(request、session)的时候会执行Localproxy中对应的方法
- localproxy又调用了一个函数(_lookup_req_object)
- 再通过LocalStack去Local里面把RequestContext拿出来(通过top拿列表钟最后一个)
- 然后再去RequestContext中获取request或者session
- 最后通过调用LocalStack的pop方法将数据在Local中移除
请求上下文
- 执行app.call方法
- 执行app.wsgi_app方法
- ctx = self.request_context(environ)
- 将请求相关的environ封装到了RequestContext对象中
- 再将对象封装到Local中(每个线程、协程独立空间存储)
- ctx.app 当前app名称
- ctx.request Request对象(封装请求相关的数据)
- ctx.session 空
- ctx.push()
- 将ctx通过LocalStack添加到Local中
- _request_ctx_stack.local = {‘唯一id’:{‘stack’:[ctx,]}
- ctx.session 有值了
- 执行视图函数(如打印request)
- request是LocalProxy对象
- 执行LocalProxy相应的对象(str)
- LocalProxy中的partial(_lookup_req_object,’request’)自动传递request参数
- 目标是去local中获取ctx,然后在ctx里面获取request
- str(LocalProxy._get_current_object)
- 调用偏函数
- ctx.request
- 移除请求数据
应用上下文
- from flask import request,session,g,current_app
- ctx.push中
- app_ctx 创建了一个AppContext(self)对象
- app_ctx.app = 当前app对象
- app_ctx.g = 对象(用于保存一个周期中要存储的值)
- 每个请求周期都会创建一个用于在请求周期中传递值的一个容器
- app_ctx.push到一个LocalStack()中
- _app_ctx_stack.local = {‘唯一id’:{‘stack’:[app_ctx,]}
- 多线程是如何体现的
- flask的local保存数据时,使用列表创建出来的栈,为什么用栈
- 如果是web运行环境,栈中永远只保存一条数据
- 写脚本获取app信息的时候可能存在app上下文嵌套关系
- web访问多app应用时,上下文管理是如何实现的
- 使用离线脚本执行flask的时候就可能出现多app堆栈(单元测试)
多app应用
1 | from werkzeug.wsgi import DispatcherMiddleware |
离线脚本
1 | from flask import Flask,current_app,_app_ctx_stack |
信号
- before_first_request
- 触发request_started信号
- before_request
- 如果有模板渲染
- 触发before_render_template信号
- render
- 触发template_rendered信号
- after_request
- session.save_session
- 触发request_finished信号
- 如果出现异常
- 触发got_request_exception信号
- 触发request_tearing_down信号
Flask框架中的信号基于blinker,其主要就是让开发者可是在flask请求过程中定制一些用户行为。
1 | pip3 install blinker |
内置信号
1 | request_started = _signals.signal('request-started') |
1 |
|
自定义信号
1 | from flask import Flask, current_app, flash, render_template |