Python之Flask 请求钩子与上下文

1. 异常处理

1.1 HTTP 异常主动抛出

  • abort方法
    • 抛出一个指定状态代码的HTTPException或指定响应,例如想用一个页面未找到异常来终止请求,你可以调用abort(404)
  • 参数
    • code - HTTP的错误状态码
# abort(404)
abort(500)

抛出状态码的话,只能抛出HTTP协议的错误状态码

1.2 捕获错误

  • errorhandler 装饰器
    • 注册一个错误处理程序,当程序抛出指定错误状态码的时候,就会调用该装饰器所装饰的方法。
  • 参数
    • code_or_exception HTTP的错误状态码或指定异常

例如:
同一处理状态码为500的错误给用户友好的提示
捕获指定异常(ZeroDivisionError)

# utils.errorhandler.py
from main import app

@app.errorhandler(500)
def internal_server_error(e):
    return '对不起,服务器可能搬家了...'

@app.errorhandler(ZeroDivisionError)
def zero_division_error(e):
    return '除数不能为0'
# main.py
# 注意,不在一个文件中,得导一下,让异常处理的注册装饰器生效
from ustils import errorhandler

@app.route('/test_error')
def test_error():
    # i = 1 /0
    abort(500)
    return 'test error'

2. 请求钩子

在客户端和服务器交互过程中,有些准备工作或扫尾工作需要处理,比如:

  • 在请求开始时,建立数据库连接;
  • 在请求开始时,根据需求进行权限校验;
  • 在请求结束时,指定数据格式交互;

为了让每个视图函数避免编写重复功能的代码,Flask提供了通用设施的功能,即请求钩子。

请求钩子是通过 装饰器 的形式实现的,Flask支持如下四种请求钩子:

  • before_first_request

    • 在处理第一个请求前执行
  • before_request

    • 在每次请求前执行
    • 如果被修饰的函数返回了一个响应,视图函数将不再被调用
  • after_request

    • 如果没有抛出错误,在每次请求后执行
    • 接受一个参数:视图函数做出的响应
    • 在此函数中可以对响应值在返回之前做最后一步修改处理
    • 需要将参数中的响应在此参数中进行返回
  • teardown_request

    • 在每次请求后执行
    • 接受一个参数:错误信息,如果有相关错误抛出来

代码测试:

# ustils.hooks.py
from main import app


@app.before_first_request
def before_first_request():
	#在处理第一个请求前执行
    print('before first request...')


@app.before_request
def before_request():
	#在每次请求前执行
    print('before request')
    #如果直接在这里返回一个响应,那么视图函数将不会被访问
    #return '校验不合格'


@app.after_request
def after_request(resp):
	#在每次请求后执行
    print('after request')
    # 在此函数中可以对响应值在返回之前做最后一步修改处理
    resp.headers['python'] = 'flask'
    #需要将参数中的响应在此参数中进行返回
    return resp


@app.teardown_request
def teardown_request(resp):
	#在每次请求后执行
    print('teardown request')
# main.py
from ustils import hooks

@app.route('/test')
def test_hooks_function():
    print('entry')

    return 'test ok', 200, {'hook': 'ok'}

@app.route('/test2')
def test_hooks_function2():
    print('entry2')
    resp = make_response('test2 ok')
    return  resp

3. 上下文(context)

Flask中有两种上下文,即 请求上下文应用上下文
Flask中上下文对象:相当于一个容器,保存了Flask程序运行过程中的一些信息

3.1 请求上下文(reuqest context)

在 flask 中,可以直接在视图函数中使用 request 这个对象进行获取相关数据,而 request 就是请求上下文的对象,保存了当前本次请求的相关数据,请求上下文对象有:requestsession

  • request
    • 封装了HTTP请求的内容,针对的是http请求。
  • session
    • 用来记录请求会话中的信息,针对的是用户的信息。

3.2 应用上下文(application context)

详情文档请移步
它的字面意思是 应用上下文,但它不是一直存在的,它只是request context 中的一个对 app 的代理(人),所谓local proxy。它的作用主要是帮助 request 获取当前的应用,它是伴 request 而生,随 request 而灭的。

应用上下文对象有:current_appg

3.2.1 current_app

应用程序上下文,用于存储应用程序中的变量,可以通过current_app.name打印当前app的名称,也可以在current_app中存储一些变量,例如:

  • 应用的启动脚本是哪个文件,
  • 启动时指定了哪些参数 加载了哪些配置文件,
  • 导入了哪些配置 连了哪个数据库
  • 有哪些public的工具类、常量
  • 应用跑再哪个机器上,IP多少,内存多大
# 可在app中存入一些必要的信息
app.name='zhangsan`app'

@app.route('/operate')
def operate_app():
    # 如果有多个app,而在实际使用时,并不能确定这是哪一个,就可以使用current_app,将其当做一个具体的app使用即可
    return current_app.name

作用
current_app 就是当前运行的flask app,在代码不方便直接操作flask的app对象时,可以操作current_app就等价于操作flask app对象

手动推送情境
如果您尝试在应用情境之外访问 current_app ,或其他任何使用它的东西, 则会看到以下错误消息:RuntimeError: Working outside of application context.

如果在配置应用时发现错误(例如初始化扩展时),那么可以手动推送上下文。如此你就可以直接访问 app 。如:在 with 块中使用 app_context() , 块中运行的所有内容都可以访问 current_app 。:

def create_app():
    app = Flask(__name__)

    with app.app_context():
        init_db()

    return app

3.2.2 g对象

g 作为 flask 程序全局的一个临时变量,充当中间媒介的作用,我们可以通过它在一次请求调用的多个函数间传递一些数据。每次请求都会重设这个变量。

g 表示“全局”的意思,但是指的是数据在 情境 之中是全局的。 g 中的数据在情境结束后丢失,因此它不是在请求之间存储数据的恰当位置。使用 session 或数据库跨请求存储数据。

示例

  • 构建认证机制
  • 对于特定视图可以提供强制要求用户登录的限制
  • 对于所有视图,无论是否强制要求用户登录,都可以在视图中尝试获取用户认证后的身份信息
from flask import g

@app.before_request
def authentication():

    if request.path != '/login':
        if 'user_info' not  in g:
            username = session.get('username', None)
            # 将用户id或其他用户信息存起来
            g.user_info = {'id': 123, 'username': username, 'fullname': '张三'}



def login_required(func):

    def inner(*args, **kwargs):
        if g.user_info is None:
            # 用户未登录
            return redirect('/login')
        else:
            return func(*args, **kwargs)

    return inner

@app.route('/login', methods =['GET','POST'])
def login():
    if request.method == 'GET':
        return render_template('login.html')
    else:
        username = request.form.get('username')
        pwd = request.form.get('password')
        if 'zhangsan' == username and '111' == pwd:
            session['username'] = username
            return render_template('index.html', user_info = g.user_info)
        else:
            return render_template('login.html', msg={'登录失败'})

@app.route('/other')
@login_required
def other():
    return jsonify(g.user_info)

g 的常见用法是在请求期间管理资源。

g

  • et_X() 创建资源 X (如果它不存在),将其缓存为 g.X 。
  • eardown_X() 关闭或以其他方式解除分配资源(如果存在)。它被注册为 teardown_appcontext() 处理器。

例如,您可以使用以下方案管理数据库连接:

from flask import g

def get_db():
    if 'db' not in g:
        g.db = connect_to_database()

    return g.db

@app.teardown_appcontext
def teardown_db(exception):
    db = g.pop('db', None)

    if db is not None:
        db.close()

3. app_context 与 request_context

在Flask程序未运行的情况下,调试代码时需要使用current_app、g、request这些对象,该如何使用?

app_context

app_context为我们提供了应用上下文环境,允许我们在外部使用应用上下文current_app、g

可以通过with语句进行使用

app = Flask(__name__)

app.name = 'my app'

with app.app_context():
    print(current_app.name)

request_context

request_context为我们提供了请求上下文环境,允许我们在外部使用请求上下文request、session

可以通过with语句进行使用

app = Flask(__name__)

environ = {'wsgi.version':(1,0), 'wsgi.input': '', 'REQUEST_METHOD': 'GET', 'PATH_INFO': '/', 'SERVER_NAME': 'itcast server', 'wsgi.url_scheme': 'http', 'SERVER_PORT': '80'}  # 模拟解析客户端请求之后的wsgi字典数据
with app.request_context(environ):  # 借助with语句使用request_context创建请求上下文
     print(request.path)

你可能感兴趣的:(python,python,rabbitmq,batch)