昨日内容回顾

flask和django对比

flask和django本质是一样的,都是web框架。

但是django自带了一些组件,flask虽然自带的组件比较少,但是它有很多的第三方插件。

那么在什么情况下,使用flask呢?

比如让flask写一个大型项目,它需要很多第三方插件。
那么堆着堆着,就和django一样了!

总结:

如果一个项目需要的插件比较少,可以使用flask。
如果需要的插件比较多,使用django更加方便。

flask知识点

装饰器

在flask中,装饰器用的是比较多的。看下面一段代码

  1. from flask import Flask
  2.  
  3. app = Flask(__name__)
  4.  
  5. @app.route('/index')
  6. def index():
  7. return 'index'
  8.  
  9. if __name__ == '__main__':
  10. app.run()

现在有一个装饰器函数xxx,如果需要在每次请求index页面时,做一些操作。

那么装饰器,应该加在哪里呢?

这样?

  1. @xxx
  2. @app.route('/index')

还是这样呢?

  1. @app.route('/index')
  2. @xxx

答案是,必须在@app.route('/index')下面才行。为什么呢?

因为如果加在@app.route上面,那么执行@xxx之后,那么就直接走视图函数了。已经没有意义了!

而如果在@app.route下面,那么执行到路由后,就会先执行@xxx,再执行视图函数!

装饰器的顺序

看下面一段代码,index视图函数,加了一个装饰器xxxx

  1. from flask import Flask
  2.  
  3. app = Flask(__name__)
  4.  
  5. def xxxx(func):
  6. def inner(*args,**kwargs):
  7. print('before')
  8. return func(*args,**kwargs)
  9.  
  10. return inner
  11.  
  12. @app.route('/index')
  13. @xxxx
  14. def index():
  15. return 'index'
  16.  
  17. if __name__ == '__main__':
  18. app.run()

启动程序,访问首页

  1. http://127.0.0.1:5000/index

查看Pycharm控制台输出:  before

如果再加视图函数home,并应用xxxx装饰器

  1. from flask import Flask
  2.  
  3. app = Flask(__name__)
  4.  
  5. def xxxx(func):
  6. def inner(*args,**kwargs):
  7. print('before')
  8. return func(*args,**kwargs)
  9.  
  10. return inner
  11.  
  12. @app.route('/index')
  13. @xxxx
  14. def index():
  15. return 'index'
  16.  
  17. @app.route('/home')
  18. @xxxx
  19. def home():
  20. return 'home'
  21.  
  22. if __name__ == '__main__':
  23. app.run()

启动之后,会直接报错

  1. AssertionError: View function mapping is overwriting an existing endpoint function: inner

为什么呢?由于代码是从上至下执行的。视图函数执行xxxx装饰器之后,使用__name__方法获取函数名时,名字是inner

那么因此执行到home时,函数名也是inner。那么flask就会抛出异常,inner函数重复了!

如何解决呢?使用functools就可以了!它会保留原函数信息,包括函数名!

  1. from flask import Flask
  2. import functools
  3.  
  4. app = Flask(__name__)
  5.  
  6. def xxxx(func):
  7. @functools.wraps(func)
  8. def inner(*args,**kwargs):
  9. print('before')
  10. return func(*args,**kwargs)
  11.  
  12. return inner
  13.  
  14. @app.route('/index')
  15. @xxxx
  16. def index():
  17. return 'index'
  18.  
  19. @app.route('/home')
  20. @xxxx
  21. def home():
  22. return 'home'
  23.  
  24. if __name__ == '__main__':
  25. app.run()

再次执行,就不会报错了。

因此,以后为了装饰器不出问题,一定要加functools

before_request/after_request

看下面的代码,b1和b2谁会先执行?

  1. # import pymysql
  2. # from DBUtils.PooledDB import PooledDB, SharedDBConnection
  3. # POOL = PooledDB(
  4. # creator=pymysql, # 使用链接数据库的模块
  5. # maxconnections=6, # 连接池允许的最大连接数,0和None表示不限制连接数
  6. # mincached=2, # 初始化时,链接池中至少创建的空闲的链接,0表示不创建
  7. # maxcached=5, # 链接池中最多闲置的链接,0和None不限制
  8. # maxshared=3, # 链接池中最多共享的链接数量,0和None表示全部共享。PS: 无用,因为pymysql和MySQLdb等模块的 threadsafety都为1,所有值无论设置为多少,_maxcached永远为0,所以永远是所有链接都共享。
  9. # blocking=True, # 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错
  10. # maxusage=None, # 一个链接最多被重复使用的次数,None表示无限制
  11. # setsession=[], # 开始会话前执行的命令列表。如:["set datestyle to ...", "set time zone ..."]
  12. # ping=0,
  13. # # ping MySQL服务端,检查是否服务可用。# 如:0 = None = never, 1 = default = whenever it is requested, 2 = when a cursor is created, 4 = when a query is executed, 7 = always
  14. # host='127.0.0.1',
  15. # port=3306,
  16. # user='root',
  17. # password='123',
  18. # database='pooldb',
  19. # charset='utf8'
  20. # )
  21. '''
  22. 1.Flask路由
  23. 1.endpoint="user" # 反向url地址
  24. 2.url_address = url_for("user")
  25. 3.methods = ["GET","POST"] # 允许请求进入视图函数的方式
  26. 4.redirect_to # 在进入视图函数之前重定向
  27. 5./index/<nid> # 动态参数路由 <int:nid> def index(nid)
  28. 6.strict_slashes # 是否严格要求路由地址 /
  29. 7.defaults={"nid":1} # def index(nid)
  30.  
  31. 2.Flask初始化配置(实例化):
  32. 1.template_folder # 指定模板路径
  33. 2.static_url_path # 指定静态文件目录的URL地址
  34. 3.static_folder # 指定静态文件目录路径
  35.  
  36. 3.Flask对象配置
  37. 1.DEBUG #开发模式的调试功能 True False
  38. 2.app.config.from_object(class) # 通过对象的方式导入配置
  39. 3.secret_key # 开启session功能的时候需要添加的配置
  40.  
  41. 4.Blueprint
  42. 1.将功能和主程序分离,注册
  43. 2.bl = Blueprint("dongdong",__name__)
  44. 3.注册 register_blueprint(bl)
  45.  
  46. 5.send_file jsonify
  47. 1.send_file # 打开并返回文件 content-type:文件类型
  48. 2.jsonify # 将一个字符串 转为JSON格式 加入 content-type:application/json 头
  49.  
  50. 6.特殊的装饰器:
  51. 1.before_request # 在请求进入视图函数之前执行的函数(登录认证)
  52. 2.after_request # 在请求响应回浏览器之前执行的函数
  53. 3.before_first_request # 在第一次请求进入视图函数之前执行的函数
  54. 4.errorheader(404) # 当遇到此类错误响应的时候(自定义错误页面)
  55.  
  56. 7.flash
  57. 1.flash("msg","tag") # 闪现存储
  58. 2.get_flashed_messages(category_filter=["tag"]) # 闪现取值
  59. 只要用到了get_flashed_messages就一定清空flash
  60.  
  61. 1.DButils 数据库连接池
  62. 创建连接池同时创建连接
  63. 用到连接时从连接池中抽取一个连接
  64. 释放连接时将连接放回连接池中
  65. 节省与mysql的通讯次数和时长
  66.  
  67. 2.Websocket 通讯协议
  68.  
  69. Web + socket
  70. QQ 即时通讯软件 97
  71.  
  72. 初期轮询:
  73. QQ 联众 软件不断的循环访问服务器问它有没有给我发送的消息
  74. 优点:响应及时
  75. 缺点:浪费CPU资源,浪费带宽
  76.  
  77. 长轮询:
  78. 当客户端发起询问,服务器说你等着1分钟之后,你再来问我
  79. 断开再次发起连接,服务器帮你轮询
  80. 优点:响应及时
  81. 缺点:用户一旦形成规模,服务器消耗是致命的
  82.  
  83. 新的协议 websocket
  84. 规定了一个数据格式
  85. 收发数据
  86. 该收就收
  87. 该发就发
  88.  
  89. 3.群聊
  90.  
  91. 4.私聊
  92.  
  93. '''
  94. # from flask import Flask,request,redirect,session
  95. #
  96. # app = Flask(__name__)
  97. # app.secret_key = "DragonFire"
  98. #
  99. #
  100. # @app.before_request
  101. # def is_login(): # 判断是否登录
  102. # # 白名单设置,判断为登录页面时
  103. # if request.path == "/login":
  104. # # 跳过处理
  105. # return None
  106. # # 判断session是不存在时
  107. # if not session.get("user"):
  108. # # 重定向到登录页面
  109. # return redirect("/login")
  110. #
  111. # @app.after_request
  112. # def foot_log(environ): # 记录访问日志
  113. # print(environ) # 响应信息
  114. # # 判断请求路径不是登录页面
  115. # if request.path != "/login":
  116. # # 打印访问路径
  117. # print("有客人访问了",request.path)
  118. #
  119. # return environ
  120. #
  121. # @app.route("/login",methods=["POST","GET"])
  122. # def login():
  123. # if request.method == "GET":
  124. # return "Login"
  125. #
  126. # user = request.form["username"] # form表单获取
  127. # pwd = request.form["password"] # form表单获取
  128. # # 判断form表示数据和 后台数据库匹配
  129. # # models.UserInfo.objects.filter(username=user,password=pwd).first()
  130. # if user == 'xiao' and pwd == '123':
  131. # # 设置session
  132. # session["user"] = user
  133. # # 跳转首页
  134. # return redirect("/index")
  135. #
  136. #
  137. # @app.route("/index")
  138. # def index():
  139. # return "Index"
  140. #
  141. # @app.route("/home")
  142. # def home():
  143. # return "Home"
  144. #
  145. # if __name__ == '__main__':
  146. # app.run("0.0.0.0", 5000)
  147.  
  148. '''
  149.  
  150. 1.玩具开机提示语
  151. 刚刚开机的时候:
  152. 1.授权问题(MD5授权码)提示语 : 请联系玩具厂商
  153. 2.绑定问题 提示语 : 快给我找一个小主人
  154. 3.成功 提示语:欢迎使用
  155.  
  156. 2.为多个玩具发送点播:
  157. mpop 弹出菜单
  158.  
  159. 3.聊天界面:
  160. <div class="leftd">
  161. <img src="avatar/girl.jpg" class="leftd_h" />
  162. <div class="speech left">点击播放</div>
  163. </div>
  164. <div class="rightd">
  165. <img src="avatar/girl.jpg" class="rightd_h" />
  166. <div class="speech right">点击播放</div>
  167. </div>
  168.  
  169. 按住录音:
  170. hold: 按住事件 开始录音(回调函数)
  171. release: 松开事件 结束录音 执行录音中的回调函数
  172.  
  173. 4.app录音:
  174. var rec = plus.audio.getRcorder()
  175. rec.record(
  176. {filename:"_doc/audio/",format:"amr"},
  177. function(success){ success //录音文件保存路径 },
  178. function(error){}
  179. )
  180.  
  181. rec.stop()
  182.  
  183. 5.app与服务器端文件传输(ws传输):
  184. 1.app使用dataURL方式打开录音文件 : base64 文件
  185. 2.通过某个函数 将 Base64 格式的文件 转为 Blob 用于 websocket传输
  186. 3.将Blob对象使用Ws发送至服务端
  187. 4.服务端保存文件(amr)
  188. 5.将amr 转换为 mp3 使用 ffmpeg -i xxx.amr xxx.mp3
  189.  
  190. 6.简单的对话(app向玩具(web)发起):
  191. app: 1.发起两次 ws.send({to_user:}) 告诉服务端我要发给谁消息
  192. 2. ws.send(blob) app与服务器端文件传输
  193.  
  194. websocket服务:
  195. 0.创建两个变量,用于接收to_user 和 blob对象
  196. 1.收到用户的JSON字符串,to_user
  197. 获取对方的Websocket,用户send
  198. 2.收到用户的Blob对象,语音文件
  199. 保存成amr文件,转换成mp3
  200. 注意保存文件的路径
  201.  
  202. 3.将转换完成的文件发送给 to_user
  203.  
  204. 4.两个变量置空
  205.  
  206. '''
  207.  
  208. from flask import Flask
  209. import functools
  210.  
  211. app = Flask(__name__)
  212.  
  213. @app.before_request
  214. def b1():
  215. print('b1')
  216.  
  217. @app.before_request
  218. def b2():
  219. print('b2')
  220.  
  221. def xxxx(func):
  222. @functools.wraps(func)
  223. def inner(*args,**kwargs):
  224. print('before')
  225. return func(*args,**kwargs)
  226.  
  227. return inner
  228.  
  229. @app.route('/index')
  230. @xxxx
  231. def index():
  232. return 'index'
  233.  
  234. @app.route('/home')
  235. @xxxx
  236. def home():
  237. return 'home'
  238.  
  239. if __name__ == '__main__':
  240. app.run(debug=True)

启动程序,访问index页面

  1. http://127.0.0.1:5000/index

查看Pycharm控制台输出:

  1. b1
  2. b2
  3. before

可以发现,b1先执行。为什么呢?因为代码是从上至下执行的,所以谁先加载,谁就先执行!

关于before_request源码分析,请参考链接:

https://blog.csdn.net/slamx/article/details/50491192

举例:

  1. from flask import Flask
  2.  
  3. app = Flask(__name__)
  4.  
  5. @app.before_request
  6. def b1():
  7. print('b1')
  8.  
  9. @app.after_request
  10. def a1(environ):
  11. print('a1')
  12. return environ
  13.  
  14. @app.route('/index')
  15. def hello_world():
  16. return 'Hello World!'
  17.  
  18. if __name__ == '__main__':
  19. app.run()

访问首页:http://127.0.0.1:5000/index,效果如下:

Pycharm输出:

  1. b1
  2. a1

总结:before_request实际上是将视图函数,append到一个列表中。after_request也是将视图函数append到一个列表中,但是它对列表做了reverse操作!具体,可以看源码。

endpoint

endpoint主要是做反向解析的,使用url_for模块,就可以反向生成url

  1. from flask import Flask,url_for
  2.  
  3. app = Flask(__name__)
  4.  
  5. @app.route('/index',endpoint='n1')
  6. def index():
  7. print(url_for('n1'))
  8. return 'index'
  9.  
  10. if __name__ == '__main__':
  11. app.run(debug=True)

访问url:

  1. http://127.0.0.1:5000/index

执行输出:

  1. /index

flask内置session

flask的session默认存储在哪里呢?在django中,session默认是保存在表里面的。

那么flask的session其实是保存在 用户浏览器的cookie中

它是如何存储的呢?看下面一段代码

  1. from flask import Flask,request,session
  2.  
  3. app = Flask(__name__)
  4. app.secret_key = 'fdsa' # 必须要指定这个参数
  5.  
  6. @app.route('/login')
  7. def login():
  8. #认证过程省略...
  9. # 设置session
  10. session['user_info'] = 'xiao'
  11. return ''
  12.  
  13. if __name__ == '__main__':
  14. app.run(debug=True)

访问登录页面,效果如下:

查看请求, 发现一个Set-Cookie。这个cookie的key就是session,值为一堆字符串。它是已经加密过的!

那么它是如何实现的呢?看这一行代码

  1. session['user_info'] = 'xiao'

它在内存中,维护了一个空间,这个空间是一个字典。由于服务端是单进程,单线程。

所有请求过来时,会排队。这个字典,会放一个key,这个key就是程序的线程id,value存放用户信息。

而value是一个字典,比如:{'user_info':'xiao'}

假设有100个用户,那么有100个值。大概是这样的样子:

  1. {
  2. "线程id": {
  3. "user_info": "xiao"
  4. },
  5. "线程id": {
  6. "user_info": "zhang"
  7. },
  8. ...
  9. }

返回给浏览器时,将内存中的字典做序列化,并做了加密
加完密之后,在cookie中写了一点数据
key是随机的,但是vlaue才是真正的数据

这个时候,flask字典,就清空了。
用户浏览器cookie中就有数据了,但是flask中的数据已经没有了!
这个时候,如果再来一用户,也是执行上面的流程。

总之,作为服务器,我不存储数据。
那么问题来了,flask如何做session验证?

如果之前的用户来了,它会携带cookie。
flask会读取cookie值,如果发现有,进行解密。如果解密成功,那么就是已经登录过了,否则没有登录过。

解密之后,它会将数据放到字典中!
那么读取时,它会直接从内存中读取。

关于flask的源码分析,请参考链接:

https://blog.csdn.net/m0_37519490/article/details/80774069

一、websocket原理

由于时间关系,步骤略...

关于websocket原理,请参考链接:

https://www.cnblogs.com/wupeiqi/p/6558766.html

二、flask之请求上下文

flask上下文管理,主要分为2类:

请求上下文管理

应用上下文管理

由于时间关系,步骤略...

草稿图

关于flask上下文管理,请参考链接:

https://www.cnblogs.com/zhaopanpan/p/9457343.html

https://blog.csdn.net/bestallen/article/details/54429629

关于flask面试题,请参考链接:

https://www.cnblogs.com/caochao-/articles/8963610.html

今日内容总结:

  1. 内容详细:
  2. 1. websocket原理
  3. a. websocket是一个协议。
  4. websocket解决了一个问题:服务端可以向客户端推送消息。
  5.  
  6. http协议规定:
  7. - 请求体请求体
  8. - 一次请求一次响应(无状态短链接)
  9. websocket协议规定:
  10. - 握手
  11. - base64(sha1(key + magic string ))
  12. - 收发数据(加密)
  13. - =127
  14. - =126
  15. - <=125
  16. - 连接创建不断开(持久连接)
  17.  
  18. b. 使用
  19. - flask werkzurg / geventwebsocket
  20. - django: wsgiref / channel
  21. - tornado: 自己写全支持:httpws
  22.  
  23. 2. flask上下文管理
  24.  
  25. 前戏:
  26. a. threading.local
  27.  
  28. # 创建threading.local对象
  29. val = threading.local()
  30.  
  31. def task(arg):
  32. # threading.local对象.xxx = 123
  33. # 内部,获取当前线程ID
  34. # {
  35. # 7800:{'x1':1}
  36. # 7180:{'x1':2}
  37. # }
  38. val.x1 = arg
  39.  
  40. for i in range(10):
  41. t = threading.Thread(target=task,args=(i,))
  42. t.start()
  43.  
  44. # ####### flask中搞了一个升级版的threading.local() #######
  45. # 创建threading.local对象
  46. val = threading.local()
  47.  
  48. def task(arg):
  49. # threading.local对象.xxx = 123
  50. # 内部,获取当前协程ID
  51. # {
  52. # 7800:{'x1':1}
  53. # 7180:{'x1':2}
  54. # }
  55. val.x1 = arg
  56.  
  57. for i in range(10):
  58. t = threading.Thread(target=task,args=(i,))
  59. t.start()
  60. b.
  61. 后进先出的数据结构
  62.  
  63. c. 偏函数
  64. 保留已知参数
  65.  
  66. d. 全局变量,flask程序启动只有一份数据
  67. _request_ctx_stack = LocalStack()
  68. _app_ctx_stack = LocalStack()
  69. current_app = LocalProxy(_find_app)
  70. request = LocalProxy(partial(_lookup_req_object, 'request'))
  71. session = LocalProxy(partial(_lookup_req_object, 'session'))
  72. g = LocalProxy(partial(_lookup_app_object, 'g'))
  73.  
  74. 正文:图
  75.  
  76. 重点总结:
  77. 1. flask路由:装饰器 *****
  78. 2. flasksession,默认写在浏览器cookie中。 ***
  79. 3. websocket协议 *****
  80. 4. flask请求上下文管理 *****
  81.  
  82. 作业:
  83. 请求上下文类关系图

未完待续...

python 全栈开发,Day139(websocket原理,flask之请求上下文)的更多相关文章

  1. Python 全栈开发【第0篇】:目录

    Python 全栈开发[第0篇]:目录   第一阶段:Python 开发入门 Python 全栈开发[第一篇]:计算机原理&Linux系统入门 Python 全栈开发[第二篇]:Python基 ...

  2. Python全栈开发【基础四】

    Python全栈开发[基础四] 本节内容: 匿名函数(lambda) 函数式编程(map,filter,reduce) 文件处理 迭代器 三元表达式 列表解析与生成器表达式 生成器 匿名函数 lamb ...

  3. python 全栈开发之路 day1

    python 全栈开发之路 day1   本节内容 计算机发展介绍 计算机硬件组成 计算机基本原理 计算机 计算机(computer)俗称电脑,是一种用于高速计算的电子计算机器,可以进行数值计算,又可 ...

  4. Python全栈开发

    Python全栈开发 一文让你彻底明白Python装饰器原理,从此面试工作再也不怕了. 一.装饰器 装饰器可以使函数执行前和执行后分别执行其他的附加功能,这种在代码运行期间动态增加功能的方式,称之为“ ...

  5. Python全栈开发记录_第一篇(循环练习及杂碎的知识点)

    Python全栈开发记录只为记录全栈开发学习过程中一些难和重要的知识点,还有问题及课后题目,以供自己和他人共同查看.(该篇代码行数大约:300行) 知识点1:优先级:not>and 短路原则:a ...

  6. python全栈开发目录

    python全栈开发目录 Linux系列 python基础 前端~HTML~CSS~JavaScript~JQuery~Vue web框架们~Django~Flask~Tornado 数据库们~MyS ...

  7. Python全栈开发【面向对象进阶】

    Python全栈开发[面向对象进阶] 本节内容: isinstance(obj,cls)和issubclass(sub,super) 反射 __setattr__,__delattr__,__geta ...

  8. Python全栈开发【面向对象】

    Python全栈开发[面向对象] 本节内容: 三大编程范式 面向对象设计与面向对象编程 类和对象 静态属性.类方法.静态方法 类组合 继承 多态 封装 三大编程范式 三大编程范式: 1.面向过程编程 ...

  9. Python全栈开发【模块】

    Python全栈开发[模块] 本节内容: 模块介绍 time random os sys json & picle shelve XML hashlib ConfigParser loggin ...

随机推荐

  1. 对entry-common.S和call.S的部分理解1

    内核版本: linux-2.6.30.4 文件: linux-2.6.30.4/arch/arm/kernel/entry-common.S linux-2.6.30.4/arch/arm/kerne ...

  2. GoLang基础数据类型--->字典(map)详解

    GoLang基础数据类型--->字典(map)详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任.   可能大家刚刚接触Golang的小伙伴都会跟我一样,这个map是干嘛的,是 ...

  3. Hbase记录-HBase扫描/计数/权限

    HBase扫描   scan 命令用于查看HTable数据.使用 scan 命令可以得到表中的数据.它的语法如下: scan ‘<table name>’ 下面的示例演示了如何使用scan ...

  4. HDU - 4324 Triangle LOVE(拓扑排序)

    https://vjudge.net/problem/HDU-4324 题意 每组数据一个n表示n个人,接下n*n的矩阵表示这些人之间的关系,输入一定满足若A不喜欢B则B一定喜欢A,且不会出现A和B相 ...

  5. ZOJ 4019 Schrödinger's Knapsack

    Schrödinger's Knapsack Time Limit: 1 Second      Memory Limit: 65536 KB DreamGrid has a magical knap ...

  6. Winform窗体设计工具源码

    源代码:QQ群616945527,博客资源

  7. wireshark数据包分析

    最近有不少同事开始学习Wireshark,他们遇到的第一个困难就是理解不了主界面上的提示信息,于是跑来问我.问的人多了,我也总结成一篇文章,希望对大家有所帮助.Wireshark的提示可是其最有价值之 ...

  8. Linux - 日志处理一

    Linux 日志处理 history # 历时命令默认1000条 HISTTIMEFORMAT="%Y-%m-%d %H:%M:%S " # 让history命令显示具体时间 hi ...

  9. pandas 读csv文件 TypeError: Empty 'DataFrame': no numeric data to plot

    简单的代码,利用pandas模块读csv数据文件,这里有两种方式,一种是被新版本pandas遗弃的Series.from_csv:另一种就是pandas.read_csv 先说一下问题这个问题就是在读 ...

  10. gtid_executed和gtid_purged变量是如何初始化的

    一.官方释义 1.1.gtid_executed.gtid_purged https://dev.mysql.com/doc/refman/5.7/en/replication-options-gti ...