🍖Flask请求扩展及中间件
一.请求扩展
1.before_request
- 作用 : before_request 相当于 django 中的 process_request,每一个请求在被处理前都会经过这个方法
- 应用 : 用户登录认证(这样避免了每一个视图函数都加用户登录认证的装饰器)
- 注意 : before_request 的返回值为 None 才会往后走, 否则直接返回你的返回值,如果定义了after_request那么会接着它执行, 最终本次请求响应结束
- 示例
from flask import Flask, request, render_template, session, url_for, redirect, flash, get_flashed_messages
from markupsafe import Markup
app = Flask(__name__)
app.debug = True
app.secret_key = 'the_secret_key'
@app.before_request
def process_request(*args, **kwargs):
# 判断访问的是不是登入路径,是的话返回None继续往后走
if request.path == '/login':
return None
else:
# 不是的话判断是否携带用户信息(判断是否登入状态)
username = session.get('username')
print('username', username)
if username:
return None
else:
# 如果没有,则重定向到登入界面
return redirect('/login')
@app.route('/login')
def login():
username = request.args['username']
print(username)
if username == 'shawn':
session['username'] = username
return redirect('/index')
else:
return render_template('login.html')
@app.route('/index')
def index():
return render_template('index.html')
if __name__ == '__main__':
app.run()
2. after_request
-
作用 : 类比django中间件中的process_response,如果请求没有出现异常的情况下, 会在请求返回return之前执行. 但是如果有多个顺序是从下往上执行.
-
与Django中process_response的区别
Django中当请求返回return后, 会从当前位置结束接着从当前位置response出去
Flask中的after_request请求返回return之后, 后面的response也会一个个走完
@app.after_request # 后执行
def process_response1(response):
print('process_response1')
return response
@app.after_request # 先执行
def process_response2(response):
print('process_response2')
return response
3.before_first_request
- 作用 : 顾名思义, 项目启动第一次请求时触发执行
- 应用 : 项目初始化用来保证以后项目只要不重启就不再继续执行
@app.before_first_request
def before_first():
print("before_first")
4. teardown_request
- 效果 : 不管什么情况, 都会触发, 即便遇到了异常, 并且返回return没有任何效果, 无法控制返回结果
- 应用 : 记录日志
@app.teardown_request
def ter(e):
print("不管什么情况,都会触发,即便遇到了异常")
5.errorhandler
- 作用: 绑定错误的状态码进而可以捕获服务器的错误, 并返回对应的错误页面
@app.errorhandler(404)
def error_404(arg):
return "404页面找不到了..."
6.template_global
- 作用: 全局的标签, 在任意的html页面中就可以直接使用, 不需要在render_template中传递参数以后才能使用
@app.template_global()
def gl(a1, a2):
return a1 + a2
# html 文件中使用
{{ gl(1,2) }}
7.template_filter
- 作用: 全局的过滤器, 在任意的html页面中就可以直接使用, 不需要在render_template中传递参数以后才能使用
@app.template_filter()
def db(a1, a2, a3):
return a1 + a2 + a3
# html 文件中使用,相比较Django的过滤器最多只能传两个参数,这里可以传多个
# 1传给a1,2-->a2,3-->a3
{{ 1|db(2,3) }}
8.示例
- 测试1
from flask import Flask, request, render_template, session, url_for, redirect, flash, get_flashed_messages
from markupsafe import Markup
app = Flask(__name__)
app.debug = True
app.secret_key = 'the_secret_key'
@app.before_request
def before_request(*args,**kwargs):
print("before_request1")
return 'before_request1' # 这里return非None
@app.before_request
def before_request(*args,**kwargs):
print("before_request2") # 前面return非None,这里不走了
@app.after_request
def after_request(response):
print("after_request1")
return response # 后出去
@app.after_request
def after_request(response):
print("after_request2")
return response # 先出去
# 第一次请求的时候触发
@app.before_first_request
def before_first():
print("before_first")
@app.teardown_request
def ter(e):
print("不管什么情况,都会触发,即便遇到了异常")
@app.route('/index')
def index():
return render_template('index.html')
if __name__ == '__main__':
app.run()
"""
输出结果 :
before_first
before_request1
after_request2
after_request1
不管什么情况,都会触发,即便遇到了异常
"""
- 测试2
from flask import Flask, request, render_template, session, url_for, redirect, flash, get_flashed_messages
from markupsafe import Markup
app = Flask(__name__)
app.debug = True
app.secret_key = 'the_secret_key'
@app.errorhandler(404)
def error_404(arg):
return "404页面找不到了..."
@app.template_global()
def gl(a1, a2):
return a1 + a2
@app.template_filter()
def db(a1, a2, a3):
return a1 + a2 + a3
@app.route('/index')
def index():
return render_template('index.html')
if __name__ == '__main__':
app.run()
<!--html-->
<h2>{{gl(10, 20)}}</h2>
<hr>
<h2>{{10|db(10,10)}}</h2>
- 效果
二.中间件
1.自定义中间件
- 严格来讲, flask中并没有中间件, 但是我们查看其源码可以模拟一个中间件
from flask import Flask
app = Flask(__name__)
app.debug = True
class MyMiddleware(object):
def __init__(self, old_wsgi_app):
self.old_wsgi_app = old_wsgi_app
def __call__(self, environ, start_response):
print('开始之前')
ret = self.old_wsgi_app(environ, start_response)
print('结束之后')
return ret
if __name__ == '__main__':
app.wsgi_app = MyMiddleware(app.wsgi_app)
app.run()
1.源码分析
- 我们发现当执行
app.run
方法的时候,最终执行run_simple
,最后执行app( )
, app 是 Flask 类的对象, 对象加括号触发类中的__call__()
方法, 也就是在执行app.__call__
方法
- 所以, 我们找到 Flask 类中的
__call__()
方法, 该方法直接返回了self.wsgi_app(environ, start_response)
- 该返回值执行
wsgi_app()
方法, 接收environ(请求), start_response(响应), 也就是说 flask 中所有的内容调度都是从这里开始的, 这个方法就是程序的入口 - 那么好办了, 我们想要实现中间件就必须在内重写
wsgi_app()
方法, 在该方法前后添加一些自己想要的功能 - 定义一个中间件MyMiddleware, 在内部书写初始化方法
__init__()
, 用来接收原始的app.wsgi
对象并转成自己的对象 - 因为
app+( )
会触发类中__call__( )
方法, 所以在类中书写__call__()
方法, 里面重写wsgi_app()
方法,于是你就可以在wsgi_app()
方法前后进行自己功能的添加
- 最后将自己的
wsgi_app
赋值给app
对象完成替换