ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

第四章:Flask高级

2022-02-05 20:05:24  阅读:270  来源: 互联网

标签:__ Flask app request 高级 session def user 第四章


第四章:Flask高级

1、蓝图

在一个Flask 应用项目中,如果业务视图过多,可否将以某种方式划分出的业务单元单独维护,将每个单元用到的视图、静态文件、模板文件等独立分开?

例如从业务角度上,可将整个应用划分为用户模块单元、商品模块单元、订单模块单元,如何分别开发这些不同单元,并最终整合到一个项目应用中?

一、蓝图介绍

在Flask中,使用蓝图Blueprint来分模块组织管理。

蓝图实际可以理解为是一个存储一组视图方法的容器对象,其具有如下特点:

  • 一个应用可以具有多个Blueprint
  • 可以将一个Blueprint注册到任何一个未使用的URL下比如 “/user”、“/goods”
  • Blueprint可以单独具有自己的模板、静态文件或者其它的通用操作方法,它并不是必须要实现应用的视图和函数的
  • 在一个应用初始化时,就应该要注册需要使用的Blueprint

但是一个Blueprint并不是一个完整的应用,它不能独立于应用运行,而必须要注册到某一个应用中。

二、使用步骤

使用蓝图可以分为三个步骤

  1. 创建一个蓝图对象
user_bp=Blueprint('user',__name__) 
  1. 在这个蓝图对象上进行操作,注册路由,指定静态文件夹,注册模版过滤器
@user_bp.route('/') 
def user_profile(): 
	return 'user_profile' 
  1. 在应用对象上注册这个蓝图对象
app.register_blueprint(user_bp)

案例:1、单个文件的蓝图。2、独立模块的蓝图

三、蓝图中的静态资源和模板

指定蓝图的url前缀

在应用中注册蓝图时使用 url_prefix 参数指定

app.register_blueprint(user_bp, url_prefix='/user') 
app.register_blueprint(item_bp, url_prefix='/items') 

蓝图中的静态资源

和应用对象不同,蓝图对象创建时不会默认注册静态目录的路由。需要我们在 创建时指定 static_folder 参数。

下面的示例将蓝图所在目录下的static_admin目录设置为静态目录

admin = Blueprint("admin",__name__,static_folder='static_admin') 
app.register_blueprint(admin,url_prefix='/admin') 

现在就可以使用 /admin/static_admin/<filename> 访问 static_admin 目录下的静态文件了。

也可通过 static_url_path 改变访问路径

admin = Blueprint("admin",__name__,static_folder='static_admin',static_url_path='/lib') 
app.register_blueprint(admin,url_prefix='/admin') 

蓝图中的模板

蓝图对象默认的模板目录为系统的模版目录,可以在创建蓝图对象时使用 template_folder 关键字参数设置模板目录

admin = Blueprint('admin',__name__,template_folder='my_templates')

2、Cookie和Session

一、Cookie介绍

Cookie是一段不超过4KB的小型文本数据,保存在客户端浏览器中,由一个名称(Name)、一个值(Value)和其它几个用于控制Cookie有效期、安全性、使用范围的可选属性组成。其中 :

(1) Name/Value:设置Cookie的名称及相对应的值,对于认证Cookie,Value值包括Web服务器所提供的访问令牌。

(2) Expires属性:设置Cookie的生存期。有两种存储类型的Cookie:会话性与持久性。Expires属性缺省时,为会话性Cookie,仅保存在客户端内存中,并在用户关闭浏览器时失效;持久性Cookie会保存在用户的硬盘中,直至生存期到或用户直接在网页中单击“注销”等按钮结束会话时才会失效 。

(3) Path属性:定义了Web站点上可以访问该Cookie的目录 。

(4) Domain属性:指定了可以访问该 Cookie 的 Web 站点或域。

二、Flask中操作Cookie

设置

from flask import Flask, make_response 
app = Flask(__name__) 
@app.route('/cookie') 
def set_cookie(): 
	resp = make_response('set cookie ok') 
	resp.set_cookie('username', 'root') 
	return resp 

设置有效期

@app.route('/cookie') 
def set_cookie(): 
	response = make_response('hello world') 
	response.set_cookie('username', 'root', max_age=3600) 
	return response 

读取

from flask import request 
@app.route('/get_cookie') 
def get_cookie(): 
	resp = request.cookies.get('username') 
	return resp 

删除

from flask import request 
@app.route('/delete_cookie') 
def delete_cookie(): 
	response = make_response('hello world') 
	response.delete_cookie('username') 
	return response

三、Session介绍

Session:与cookie功能效果相同。Session与Cookie的区别在于Session是记录在服务端的,而Cookie是记录在客户端的。

当访问服务器某个网页的时候,会在服务器端的内存里开辟一块内存,这块内存就叫做session,而这个内存是跟浏览器关联在一起的。这个浏览器指的是浏览器窗口,或者是浏览器的子窗口,意思就是,只允许当前这个session对应的浏览器访问,就算是在同一个机器上新启的浏览器也是无法访问的。而另外一个浏览器也需要记录session的话,就会再启一个属于自己的session。

问题:如何知道浏览器和这个服务器中的session是一一对应的呢?又如何保证不会去访问其它的session呢?

**解答:**就是当访问一个页面的时候给浏览器创建一个独一无二的号码,也给同时创建的session赋予同样的号码。 这样就可以在打开同一个网站的第二个页面时获取到第一个页面中session保留下来的对应信息(理解:当访问第二个页面时将号码同时传递到第二个页面。找到对应的session。)。这个号码也叫sessionID(签名),session的ID号码或者签名是独一无二的。

session的两种传递方式:第一种通过cookies实现。第二种通过URL重写来实现

当客户端进行第一次请求时,客户端的HTTP request(cookie为空)到服务端,服务端创建session,视图函数根据form表单填写session,请求结束时,session内容填写入response的cookie中并返回给客户端,客户端的cookie中便保存了用户的数据。

当同一客户端再次请求时, 客户端的HTTP request中cookie已经携带数据,此时cookies不为空, 获取cookie的有效时长,如果cookie依然有效,通过与写入时同样的签名算法将cookie中的值解密出来,若cookie已经失效,则返回空。 再根据解密出来的内容判断服务器中是否存在对应的值。

注意:在Flask中Session的签名算法是:HMAC 和 SHA1算法

四、Session操作步骤

需要先设置SECRET_KEY

class DefaultConfig(object): 
	SECRET_KEY = 'please-generate-a-random-secret_key' 
app.config.from_object(DefaultConfig) 
或者直接设置 
app.secret_key='please-generate-a-random-secret_key' 

设置

from flask import session 
@app.route('/set_session') 
def set_session(): 
	session['username'] = 'root' 
	return 'set session ok' 

读取

@app.route('/get_session') 
def get_session(): 
	username = session.get('username') 
	return 'get session username {}'.format(username)

3、请求钩子

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

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

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

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

  • before_fifirst_request
    • 在处理第一个请求前执行
  • before_request
    • 在每次请求前执行
    • 如果在某修饰的函数中返回了一个响应,视图函数将不再被调用
  • after_request
    • 如果没有抛出错误,在每次请求后执行
    • 接受一个参数:视图函数作出的响应
    • 在此函数中可以对响应值在返回之前做最后一步修改处理
    • 需要将参数中的响应在此参数中进行返回
  • teardown_request:
    • 在每次请求后执行
    • 接受一个参数:错误信息,如果有相关错误抛出
from flask import Flask 
from flask import abort 

app = Flask(__name__) 

# 在第一次请求之前调用,可以在此方法内部做一些初始化操作 
@app.before_first_request 
def before_first_request(): 
	print("before_first_request") 
# 在每一次请求之前调用,这时候已经有请求了,可能在这个方法里面做请求的校验 
# 如果请求的校验不成功,可以直接在此方法中进行响应,直接return之后那么就不会执行视图函数 
@app.before_request 
def before_request(): 
	print("before_request") 
	# if 请求不符合条件: 
	# return "laowang" 
# 在执行完视图函数之后会调用,并且会把视图函数所生成的响应传入,可以在此方法中对响应做最后一步统一的处理 
@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=True) 

在第1次请求时的打印:

before_first_request 
before_request 
after_request 
teardown_request 

在第2次请求时的打印:

before_request 
after_request 
teardown_request

4、Flask上下文

上下文:即语境,语意,在程序中可以理解为在代码执行到某一时刻时,根据之前代码所做的操作以及下文即将要执行的逻辑,可以决定在当前时刻下可以使用到的变量,或者可以完成的事情。

Flask中有两种上下文,请求上下文和应用上下文

Flask中上下文对象:相当于一个容器,保存了 Flask 程序运行过程中的一些信息。

一、请求上下文

思考:在视图函数中,如何取到当前请求的相关数据?比如:请求地址,请求方式,cookie等等

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

  • request
    • 封装了HTTP请求的内容,针对的是http请求。举例:user = request.args.get(‘user’),获取的是get请求的参数。
  • session
    • 用来记录请求会话中的信息,针对的是用户信息。举例:session[‘name’] = user.id,可以记录用户信息。还可以通过session.get(‘name’)获取用户信息。

二、应用上下文

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

应用上下文对象有:current_app,g

current_app

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

  • 应用的启动脚本是哪个文件,启动时指定了哪些参数
  • 加载了哪些配置文件,导入了哪些配置
  • 连了哪个数据库
  • 有哪些public的工具类、常量
  • 应用跑再哪个机器上,IP多少,内存多大
from flask import Flask, current_app 
app1 = Flask(__name__) 
app2 = Flask(__name__) 
# 以redis客户端对象为例 
# 用字符串表示创建的redis客户端 
# 为了方便在各个视图中使用,将创建的redis客户端对象保存到flask app中, 
# 后续可以在视图中使用current_app.redis_cli获取 

app1.redis_cli = 'app1 redis client' 
app2.redis_cli = 'app2 redis client' 

@app1.route('/route11') 
def route11(): 
	return current_app.redis_cli 

@app1.route('/route12') 
def route12(): 
	return current_app.redis_cli 

@app2.route('/route21') 
def route21(): 
	return current_app.redis_cli 

@app2.route('/route22') 
def route22(): 
	return current_app.redis_cli 

注意:

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

三、G对象

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

示例

from flask import Flask, g 
app = Flask(__name__) 
def db_query(): 
	user_id = g.user_id 
	user_name = g.user_name 
	print('user_id={} user_name={}'.format(user_id, user_name)) 

@app.route('/') 
def get_user_profile(): 
	g.user_id = 123 
	g.user_name = 'laoliu' 
	db_query() 
	return 'hello world' 

g对象与请求钩子的综合案例

需求

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

实现

from flask import Flask, abort, g 

app = Flask(__name__) 

@app.before_request 
def authentication(): 
	"""
	利用before_request请求钩子,在进入所有视图前先尝试判断用户身份 
	:return: 
	""" 
	# TODO 此处利用鉴权机制(如cookie、session、jwt等)鉴别用户身份信息 
	# if 已登录用户,用户有身份信息 
	g.user_id = 123 
	# else 未登录用户,用户无身份信息 
	# g.user_id = None 

def login_required(func): 
	def wrapper(*args, **kwargs): 
		if g.user_id is not None: 
			return func(*args, **kwargs) 
		else:
			abort(401) 

	return wrapper 

@app.route('/') 
def index(): 
	return 'home page user_id={}'.format(g.user_id) 

@app.route('/profile') 
@login_required 
def get_user_profile(): 
	return 'user profile page user_id={}'.format(g.user_id) 

5、异常处理

一、HTTP异常主动抛出

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

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

二、捕获错误

  • errorhandler 装饰器
    • 注册一个错误处理程序,当程序抛出指定错误状态码的时候,就会调用该装饰器所装饰的方法
  • 参数:
    • code_or_exception – HTTP的错误状态码或指定异常
  • 例如统一处理状态码为500的错误给用户友好的提示:
@app.errorhandler(500) 
def internal_server_error(e): 
	return '服务器搬家了' 
  • 捕获指定异常
@app.errorhandler(ZeroDivisionError) 
def zero_division_error(e): 
	return '除数不能为0'

标签:__,Flask,app,request,高级,session,def,user,第四章
来源: https://blog.csdn.net/qq_42385761/article/details/122792446

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有