请求钩子
在客户端和服务器交互的过程中,有些准备工作或扫尾工作需要处理,比如:
- 在请求开始时,建立数据库连接;
- 在请求开始时,根据需求进行权限校验;
- 在请求结束时,指定数据的交互格式;
为了让每个视图函数避免编写重复功能的代码,Flask提供了通用设置的功能,即请求钩子。
请求钩子是通过装饰器的形式实现,Flask支持如下四种请求钩子:
- before_first_request
- 在处理第一个请求前执行[项目初始化时的钩子]
- before_request
- 在每次请求前执行
- 如果在某修饰的函数中返回了一个响应,视图函数将不再被调用
- after_request
- 如果没有抛出错误,在每次请求后执行
- 接受一个参数:视图函数作出的响应
- 在此函数中可以对响应值在返回之前做最后一步修改处理
- 需要将参数中的响应在此参数中进行返回
- teardown_request:
- 在每次请求后执行
- 接受一个参数:错误信息,如果有相关错误抛出
- 需要设置flask的配置DEBUG=False,teardown_request才会接受到异常对象。
代码
from flask import Flask,request
# 初始化
app = Flask(import_name=__name__)
# 声明和加载配置
class Config():
DEBUG = True
app.config.from_object(Config)
@app.before_first_request
def before_first_request():
"""
这个钩子会在项目启动后第一次被用户访问时执行
可以编写一些初始化项目的代码,例如,数据库初始化,加载一些可以延后引入的全局配置
"""
print("----before_first_request----")
print("系统初始化的时候,执行这个钩子方法")
print("会在接收到第一个客户端请求时,执行这里的代码")
@app.before_request
def before_request():
"""
这个钩子会在每次客户端访问视图的时候执行
# 可以在请求之前进行用户的身份识别,以及对于本次访问的用户权限等进行判断。..
"""
print("----before_request----")
print("每一次接收到客户端请求时,执行这个钩子方法")
print("一般可以用来判断权限,或者转换路由参数或者预处理客户端请求的数据")
@app.after_request
def after_request(response):
print("----after_request----")
print("在处理请求以后,执行这个钩子方法")
print("一般可以用于记录会员/管理员的操作历史,浏览历史,清理收尾的工作")
response.headers["Content-Type"] = "application/json"
response.headers["Company"] = "python oldboy..."
# 必须返回response参数
return response
@app.teardown_request
def teardown_request(exc):
print("----teardown_request----")
print("在每一次请求以后,执行这个钩子方法")
print("如果有异常错误,则会传递错误异常对象到当前方法的参数中")
# 在项目关闭了DEBUG模式以后,则异常信息就会被传递到exc中,我们可以记录异常信息到日志文件中
print(exc)
# 编写路由视图
@app.route(rule='/')
def index():
print("-----------视图函数执行了---------------")
return "<h1>hello world!</h1>"
if __name__ == '__main__':
# 运行flask
app.run(host="0.0.0.0")
- 在第1次请求时的打印:
----before_first_request----
系统初始化的时候,执行这个钩子方法
会在接收到第一个客户端请求时,执行这里的代码
----before_request----
127.0.0.1 - - [04/Aug/2020 14:40:22] "GET / HTTP/1.1" 200 -
每一次接收到客户端请求时,执行这个钩子方法
一般可以用来判断权限,或者转换路由参数或者预处理客户端请求的数据
-----------视图函数执行了---------------
----after_request----
在处理请求以后,执行这个钩子方法
一般可以用于记录会员/管理员的操作历史,浏览历史,清理收尾的工作
----teardown_request----
在每一次请求以后,执行这个钩子方法
如果有异常错误,则会传递错误异常对象到当前方法的参数中
None
- 在第2次请求时的打印:
----before_request----
每一次接收到客户端请求时,执行这个钩子方法
一般可以用来判断权限,或者转换路由参数或者预处理客户端请求的数据
127.0.0.1 - - [04/Aug/2020 14:40:49] "GET / HTTP/1.1" 200 -
-----------视图函数执行了---------------
----after_request----
在处理请求以后,执行这个钩子方法
一般可以用于记录会员/管理员的操作历史,浏览历史,清理收尾的工作
----teardown_request----
在每一次请求以后,执行这个钩子方法
如果有异常错误,则会传递错误异常对象到当前方法的参数中
None
异常捕获
主动抛出HTTP异常
- abort 方法
- 抛出一个给定状态代码的 HTTPException 或者 指定响应,例如想要用一个页面未找到异常来终止请求,你可以调用 abort(404)
- 参数:
- code – HTTP的错误状态码
# abort(404)
abort(500)
抛出状态码的话,只能抛出 HTTP 协议的错误状态码
abort在工作中基本不会被使用,工作中的异常抛出往往在业务错误的时候使用raise进行抛出错误类型,而不是抛出http异常。
捕获错误
- errorhandler 装饰器
- 注册一个错误处理程序,当程序抛出指定错误状态码的时候,就会调用该装饰器所装饰的方法
- 参数:
- code_or_exception – HTTP的错误状态码或指定异常
- 例如统一处理状态码为500的错误给用户友好的提示:
@app.errorhandler(500)
def internal_server_error(e):
return '服务器搬家了'
- 捕获指定异常类型(0不可以作为除数)
@app.errorhandler(ZeroDivisionError)
def zero_division_error(e):
return '除数不能为0'
代码:
from flask import Flask
# 创建flask应用
app = Flask(__name__)
"""加载配置"""
class Config():
DEBUG = True
app.config.from_object(Config)
"""
flask中内置了app.errorhander提供给我们捕获异常,实现一些在业务发生错误时的自定义处理。
1. 通过http状态码捕获异常信息
2. 通过异常类进行异常捕获
"""
"""1. 捕获http异常[在装饰器中写上要捕获的异常状态码也可以是异常类]"""
@app.errorhandler(404)
def error_404(e):
return "<h1>您访问的页面失联了!</h1>"
# return redirect("/") # 如果失效网页,那么就返回首页
# 异常捕获
@app.route(rule="/11")
def index11():
print('------------视图函数执行-------------')
abort(404) # The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.
# abort(403) # Not Found
return "<h1>hello world!</h1>"
@app.errorhandler(ZeroDivisionError)
def error_zero(exc):
print("0不能是除数")
return "0不能是除数"
@app.route(rule="/zero")
def index1():
1/0
return '111'
"""2. 捕获系统异常或者自定义异常"""
class APIError(Exception):
pass
@app.route("/")
def index():
raise APIError("api接口调用参数有误!")
return "个人中心,视图执行了!!"
@app.errorhandler(APIError)
def error_apierror(e):
return "错误: %s" % e
if __name__ == '__main__':
app.run(host="localhost",port=8080)
context
执行上下文:即语境,语意,在程序中可以理解为在代码执行到某一行时,根据之前代码所做的操作以及下文即将要执行的逻辑,可以决定在当前时刻下可以使用到的变量,或者可以完成的事情。
Flask中上下文对象:相当于一个容器,保存了 Flask 程序运行过程中的一些信息[变量、函数、类与对象等信息]。
Flask中有两种上下文,请求上下文(request context)和应用上下文(application context)。
- application 指的就是当你调用
app = Flask(__name__)
创建的这个对象app
; - request 指的是每次
http
请求发生时,WSGI server
(比如gunicorn)调用Flask.__call__()
之后,在Flask
对象内部创建的Request
对象; - application 表示用于响应WSGI请求的应用本身,request 表示每次http请求;
- application的生命周期大于request,一个application存活期间,可能发生多次http请求,所以,也就会有多个request
请求上下文(request context)
思考:在视图函数中,如何取到当前请求的相关数据?比如:请求地址,请求方式,cookie等等
在 flask 中,可以直接在视图函数中使用 request 这个对象进行获取相关数据,而 request 就是请求上下文的对象,保存了当前本次请求的相关数据,请求上下文对象有:request、session
- request
- 封装了HTTP请求的内容,针对的是http请求。举例:user = request.args.get('user'),获取的是get请求的参数。
- session
- 用来记录请求会话中的信息,针对的是用户信息。举例:session['name'] = user.id,可以记录用户信息。还可以通过session.get('name')获取用户信息。
请求上下文提供的变量/属性/方法/函数/类与对象,只能在视图中或者被视图调用的地方使用
应用上下文(application context)
它的字面意思是 应用上下文,但它不是一直存在的,它只是request context 中的一个对 app 的代理(人),所谓local proxy。它的作用主要是帮助 request 获取当前的应用,它是伴 request 而生,随 request 而灭的。
应用上下文对象有:current_app,g
current_app
应用程序上下文,用于存储应用程序中的变量,可以通过current_app.name打印当前app的名称,也可以在current_app中存储一些变量,例如:
- 应用的启动脚本是哪个文件,启动时指定了哪些参数
- 加载了哪些配置文件,导入了哪些配置
- 连接了哪个数据库
- 有哪些可以调用的工具类、常量
- 当前flask应用在哪个机器上,哪个IP上运行,内存多大
from flask import Flask,request,session,current_app,g
# 初始化
app = Flask(import_name=__name__)
# 声明和加载配置
class Config():
DEBUG = True
app.config.from_object(Config)
# 编写路由视图
@app.route(rule='/')
def index():
# 应用上下文提供给我们使用的变量,也是只能在视图或者被视图调用的地方进行使用,
# 但是应用上下文的所有数据来源于于app,每个视图中的应用上下文基本一样
print(current_app.config) # 获取当前项目的所有配置信息
print(current_app.url_map) # 获取当前项目的所有路由信息
return "<h1>hello world!</h1>"
if __name__ == '__main__':
# 运行flask
app.run(host="0.0.0.0")
g变量(global)
g 作为 flask 程序全局的一个临时变量,充当者中间媒介的作用,我们可以通过它传递一些数据,g 保存的是当前请求的全局变量,不同的请求会有不同的全局变量,通过不同的thread id区别
g.name='abc' # name是举例,实际要保存什么数据到g变量中,可以根据业务而定,你可以任意的数据进去
注意:不同的请求,会有不同的全局变量g
from flask import Flask,request,session,current_app,g
# 初始化
app = Flask(import_name=__name__)
# 声明和加载配置
class Config():
DEBUG = True
app.config.from_object(Config)
@app.before_request
def before_request():
g.name = "root"
def get_two_func():
name = g.name
print("g.name=%s" % name)
def get_one_func():
get_two_func()
# 编写路由视图
@app.route(rule='/')
def index():
# 请求上下文提供的变量/属性/方法/函数/类与对象,只能在视图中或者被视图调用的地方使用
# 请求上下文里面信息来源于每次客户端的请求,所以每个视图中请求上下文的信息都不一样
# print(session)
# 应用上下文提供给我们使用的变量,也是只能在视图或者被视图调用的地方进行使用,
# 但是应用上下文的所有数据来源于于app,每个视图中的应用上下文基本一样
print(current_app.config) # 获取当前项目的所有配置信息
print(current_app.url_map) # 获取当前项目的所有路由信息
get_one_func()
return "<h1>hello world!</h1>"
if __name__ == '__main__':
# 运行flask
app.run(host="0.0.0.0")
两者区别:
- 请求上下文ÿ