Flask-Grundlegende Inhalte-Drei

I、请求钩子

什么是请求钩子?在客户端和服务器交互的过程中,有些准备工作或扫尾工作需要统一处理,为了让每个视图函数避免编写重复功能的代码,flask提供了统一的接口可以添加这些处理函数,即请求钩子。

比如:请求开始时的db_connect、auth认证,或者结束时的置顶数据的交互格式。

先回顾一下flask对请求的处理流程:

接收请求--》创建请求上下文--》请求上下文入栈--》创建该请求的应用上下文--》应用上下文入栈--》处理逻辑--》请求上下文出栈--》应用上下文出栈

看了这个过程,flask放置请求钩子的位置有:处理逻辑之前,处理逻辑之后,应用上下文出栈之前。

那么为什么要设计请求钩子呢?
为了让每个视图函数避免编写重复功能的代码,Flask提供了通用设置等功能,即请求钩子。请求钩子是通过装饰器的形式实现,Flask支持五种请求钩子:
1.before_first_request
在第一个请求前被调用(在处理第一个请求之前执行),可在此方法内做一些初始化操作
2.before_request
在每次请求前执行,如果在某修饰的函数中返回了一个响应(response),视图函数将不再被调用。
3.after_request
在每次请求后(如果没有抛出错误)执行,并且把视图函数所生成的响应传入。可接收一个参数:视图函数做出的相应,在此函数中可以对响应值在返回之前做最后一步修改处理,需要将参数中的相应在此参数中进行返回。在此方法中可对响应做最后一步统一处理。
4.teardown_request
在每次请求后执行;接收一个参数(错误信息),如果有相关错误抛出,需要设置flask的配置DEBUG=Falseteardown_request才会接收到异常对象
5.teardown_appcontext
在应用上下文从栈中弹出之前运行

首先创建一个新的app,demo_gouzi.py并填充

from flask import Flask

app = Flask(__name__)


@app.before_first_request
def before_first_request():
    print('before_first_request')


@app.before_request
def before_request():
    print('before_request')


@app.after_request
def after_request(response):
    print('after_request')
    response.headers['Content-Type'] = "application/json"
    return response


@app.teardown_request
def teardown_request(response):
    print('teardown_request')


@app.route('/')
def index():
    return 'index'


if __name__ == '__main__':
    app.run(debug=False)

我们运行下服务,观察执行结果


执行过程

after_request的执行结果

II、request对象属性

语法 描述
request.scheme 代表请求的方案,http或者https
request.path 请求的路径
request.method 表示请求使用的http方法,GET或者POST请求
request.encoding 表示提交数据的编码方式
request.GET 获取GET请求
request.POST 获取post的请求,比如前端提交的用户密码,可以通过request.POST.get()来获取 另外:如果使用 POST 上传文件的话,文件信息将包含在 FILES 属性中
request.cookies 包含所有的cookie
request.session 一个既可读又可写的类似于字典的对象,表示当前的会话

III、状态保持

因为http是一种无状态协议,不保持某一次请求所产生的信息,如想实现状态保持,在开发中解决方式有:

cookie:数据存储在客户端,节省服务器空间,但是不安全
session:会话,数据存储在服务器端
对于无状态协议的理解,看做以下五条
1、协议对于事务处理没有记忆能力
2、对同一个url请求没有上下文关系
3、每次的请求都是独立的,它的执行情况和结果与前面的请求和之后的请求是无直接关系的,它不会受前面的请求应答情况直接影响,也不会直接影响后面的请求应答情况
4、服务器中没有保存客户端的状态,客户端必须每次带上自己的状态去请求服务器
5、人生若只如初见:比如商品加入购物车,重登后购物车里的东西无了

在Django中我们接触过cookie和session,下面我们看看在Flask中如何使它们

1、cookie
cookie在flask中的使用,我们以如下示例展示:
①设置cookie

# make_response相当于Django中的http_response
from flask import  make_response

@app.route('/cookie')
def set_cookie():
    res = make_response('this is to set cookie')
    res.set_cookie('username', 'laoma')
    return res

启动服务,进入http://127.0.0.1:5000/cookie观察结果

cookie

注:cookie是有时间限制的,超过时间会过期,我们可以自己设计cookie的时间(利用max_age关键字)

# 设计cookie的时间,时间戳单位为:秒
res.set_cookie('username', 'laoma', max_age=3600)

②获取cookie

from flask import request

@app.route('/request')
def get_cookie():
    res = request.cookies.get('username')
    return res

启动服务观察结果

2、session
session会话主要用于存储敏感涉密信息,例如:个人信息、账户余额、验证码等问题,session依赖于cookie,在TensorFlow中,也会使用session尽管和web中的session有所不同,但也可理解为会话方式。
下面我们将使用session

from flask import Flask, redirect, url_for
from flask import session

# 需要设置secret_key
app.secret_key = '9999999'


@app.route('/index1')
def index1():
    session['chase'] = '9999'
    return redirect(url_for('index'))

# ‘0’为设置的默认值,默认值必须设置否则会报500错误
@app.route('/')
def index():
    return session.get('chase', '0')

Session, Cookies以及一些第三方扩展都会用到SECRET_KEY值,这是一个比较重要的配置值,应该尽可能设置为一个很难猜到的值,随机值更佳。随机的问题在于很难判断什么是真随机。一个密钥应该足够随机。你的操作系统可以基于一个密码随机生成器来生成漂亮的随机值,这个值可以用来做密钥;

SECRET_KEY配置变量是通用密钥, 可在 Flask 和多个第三方扩展中使用. 如其名所示, 加密的强度取决于变量值的机密度. 不同的程序要使用不同的密钥, 而且要保证其他人不知道你所用的字符串.其主要作用应该是在各种加密过程中加盐以增加安全性。在实际应用中最好将这个参数存储为系统环境变量。当然我们也可以利用加密哈希,就是让cookie变得“安全”的字段。服务器向我们发送最新的会话数据之前,会结合我们的会话数据、当前时间戳以及服务器的私钥来计算哈希从而加密session,使得会话变得安全。

快速入门中的 “ 会话”部分对应设置哪种服务器端机密提供了很好的建议。加密取决于机密;如果你没有设置要使用的加密服务器端密码,那么每个人都可以破坏你的加密;就像你计算机的密码一样。秘密加上要签名的数据用于创建签名字符串,使用密码哈希算法很难重新创建值;仅当你具有完全相同的机密且原始数据时,你才能重新创建此值,让Flask检测是否未经许可对任何内容进行了更改。由于Flask永远不会将秘密包含在发送给客户端的数据中,因此客户端无法篡改会话数据,并希望产生新的有效签名。

Flask尽量使用该itsdangerous库来完成所有艰苦的工作;会话使用带有自定义JSON序列化程序的itsdangerous.URLSafeTimedSerializer类。

3、上下文
①请求上下文request context
request和session都属于请求上下文对象。request:封装了HTTP请求的内容,针对的是http请求。举例:user = request.args.get('user'),获取的是get请求的参数。
这不再做过多介绍

②应用上下文application context
在发送请求时,我们可以通过上下文发送全局对象,例如app.name

current_app和g都属于应用上下文对象。
1、current_app:表示当前运行程序文件的程序实例。
2、g:(global) 处理请求时,用于临时存储的对象,每次请求都会重设这个变量。比如:我们可以获取一些临时请求的用户信息。

当调用app = Flask(_name_)的时候,创建了程序应用对象app;
request 在每次http请求发生时,WSGI server调用Flask.call();然后在Flask内部创建的request对象;
app的生命周期大于request和g,一个app存活期间,可能发生多次http请求,所以就会有多个request和g。
最终传入视图函数,通过return、redirect或render_template生成response对象,返回给客户端。

区别: 请求上下文:保存了客户端和服务器交互的数据。 应用上下文:在flask程序运行过程中,保存的一些配置信息,比如程序文件名、数据库的连接、用户信息等。

一个操作实例:

from flask import current_app, g

@app.route('/')
def index():
    print(current_app.name)
    print(g.ip)
    return session.get('chase', '0')

IV、上下文隔离原理

Flask主要是借助werkzueg来实现的,其中请求之间隔离的方式借助了库中Local、LocalStack、LocalProxy 三个类,下面将逐一介绍

1、Local:
首先local添加了一个storage字典用来存储添加的属性,当给local对象进行添加属性会自动根据get_ident方法获取当前的线程、进程,键为线程、进程ID号,值为添加的属性,同理当取值时,会自动判断当前的环境来取值, 类似于threading中的local
2、LocalStack:
LocalStack基于local实现了一个‘先进晚出’的栈结构,内部通过为每个线程、进程添加
stack属性来存储数据,存储数据的容器为list,值得注意是每次添加值、取值得时候内部会自动判断当前的线程、进程环境为每个线程、进程床架一个单独的容器,这也是不同请求之间实现隔离的主要原因,同样的current_app 当前的应用上下文也用到了类似的方式来管理,也就是说每个请求都有自己的应用上下文,请求的每次入栈都会判断当前的应用上下文,如果没有创建应用上下文,则创建对应的上下文压入栈也就是_app_ctx_stack,request对应的则是_request_ctx_stack
3、LocalProxy:
作为一种代理模式的实现,我们通过查看源码发现,LocalProxy实现了重写了大量的类的‘魔术’方法,其实localproxy主要就是对对象、方法进行了包装,通过localproxy进行过渡可以直接操作目标对象LocalProxy对_lookup_req_object方法进行了包装,当我们访问request属性的时候,由于LocalProxy重写了setattr getattr等特殊方法,在执行的时候会调用_get_cuurent_object方法且直接执行了self.__local()方法其实也就是包装之前的_lookup_req_object,通过这个方法可以获取全局请求上下文栈顶的请求上下文中的request属性,注意_request_ctx_stack中存储的是请求上下文(Request_Context),我们每次操作的request其实是Request_Context的一个属性

\color{red}{下面给出Flask的上下文流程图}
\color{violet}{上文}

上文

\color{violet}{下文}
下文

此处编辑匆忙,存有大量借鉴,而在此之后作者还会更新该内容
上述内容分别:
源码解析转载于:https://www.cnblogs.com/alplf123/p/10517057.html
流程图转载于:https://www.cnblogs.com/baijinshuo/p/10264326.html


V、初探模板 Jinja2

我们先建立一个flask_templates.py文件,建立应用来体验一下Flask如何调用模板


app = Flask(__name__)


@app.route('/')
def index():
    return render_template('index.html')


if __name__ == '__main__':
    app.run()

index.html的内容为

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
INDEX
</body>
</html>

我们启动一下服务,看看模板是否被调用

我们来介绍一下Jinja2
Jinja2是Python下一个被广泛应用的模版引擎,他的设计思想来源于Django的模板引擎,并扩展了其语法和一系列强大的功能。其中最显著的一个是增加了沙箱执行功能和可选的自动转义功能,这对大多应用的安全性来说是非常重要的。它基于unicode并能在python2.4之后的版本运行,包括python3(来源于百度百科)

其中Jinja2的模板语法与Django的模板内容基本相似

在Jinja2中,存在三种语法:
1、控制结构{% %}
2、变量取值{{ }}
Jinja2模板中使用 {{ }} 语法表示一个变量,它是一种特殊的占位符。当利用Jinja2进行渲染的时候,它会把这些特殊的占位符进行填充/替换,Jinja2支持python中所有的Python数据类型比如列表、字段、对象等。
3、注释 {# #}

@app.route('/')
def index():
    res = {'my_str': 'my', 'my_int': 1, 'my_list': [1, 2, 3], "my_dict": {"on": "day"}}
    return render_template('index.html', my_str='my', my_int=1, my_list=[1, 2, 3], my_dict={"on": "day"})
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>Jinja2模板</h1>
{{ my_str }}<br>
{{ my_int }}<br>
{{ my_list }}<br>
{{ my_dict }}<br>
<hr>
my_int + 10 = {{ my_int + 10 }}<br>
my_int _ my_list[0] = {{ my_int + my_list[0] }}<br>
list[2] = {{ my_list.2 }}<br>
my_dict['on'] = {{ my_dict['on'] }}<br>
my_dict['on'] = {{ my_dict.on }}
</body>
</html>

启动服务观察结果,其语法与python内的语法十分相似。


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342