一、flask的生命周期
客户端--->wsgi应用程序->全局钩子--> 路由 --> 视图 --> 路由---> 全局钩子 ---> wsgi应用程序---> 客户端
二、请求
-
-
作用:在视图函数中取出本次客户端的请求数据
-
导入:
from flask import request
-
代码位置:
-
代理类 from flask.app import Request ---> from flask.globals.Request
-
源码类:from flask.wrappers.Request
-
request,常用的属性如下
说明 | 类型 | |
---|---|---|
data | 记录请求体的数据,并转换为字符串 只要是通过其他属性无法识别转换的请求体数据 最终都是保留到data属性中 例如:有些公司开发微信小程序,原生IOS或者安卓,这一类客户端有时候发送过来的数据就不一样是普通的表单,查询字符串或ajax | bytes类型 |
form | 记录请求中的html表单数据 | ImmutableMultiDict |
args | 记录请求中的查询字符串,也可以是query_string | ImmutableMultiDict |
cookies | 记录请求中的cookie信息 | Dict |
headers | 记录请求中的请求头 | ImmutableMultiDict |
method | 记录请求使用的HTTP方法 | GET/POST |
url | 记录请求的URL地址 | string |
files | 记录请求上传的文件列表 | ImmutableMultiDict |
json | 记录ajax请求的json数据 |
2.1、获取请求中的各项数据
1、获取查询字符串,代码:
1 from flask import Flask, request 2 from werkzeug.datastructures import ImmutableMultiDict 3 from werkzeug.wrappers import Request as RequestBase 4 from flask.wrappers import Request 5 # 项目实例应用对象 6 app = Flask(__name__) 7 8 # 加载配置 9 app.config.update({ 10 "DEBUG": True 11 }) 12 13 14 # 在http的常用请求方法中,delete和get是没有请求体的!!! 15 16 17 @app.route(rule="/data", methods=["post", "put", "patch"]) 18 def data(): 19 """获取请求体""" 20 # 获取原生的请求体数据[当request对象的其他属性没法接受请求体数据时,会把数据保留在data中,如果有request对象的属性处理了请求体数据,则data就不再保留] 21 # print(request.data) # 如果客户端上传的是xml文档,html格式,二进制流格式,base64格式,就要使用data来接收 22 23 """ 24 1. 没有上传任何数据: 25 b'' 26 2. 上传json数据 27 b'{\n "username": "xiaoming",\n "age": 16\n}' 28 3. 上传表单数据 29 b'' 30 4. 上传xml数据 31 b'<goods-list>\n <goods price="100">\xe5\x95\x86\xe5\x93\x81\xe6\xa0\x87\xe9\xa2\x981</goods>\n <goods price="200">\xe5\x95\x86\xe5\x93\x81\xe6\xa0\x87\xe9\xa2\x982</goods>\n</goods-list>' 32 """ 33 34 # 接收表单上传的数据 35 # print(request.form) 36 """ 37 ImmutableMultiDict([('name', '小明'), ('age', '19')]) 38 """ 39 40 # 接收ajax上传的json数据 41 # print(request.json) # {"username": "xiaoming", "age": 16} 42 # print(request.is_json) # True 往往用于判断是否是ajax请求 43 44 # 上传文件列表 HTML必须以<form method="post" enctype="multipart/form-data"> # 表单属性才能上传文件 45 # print(request.files) 46 """ 47 ImmutableMultiDict([('avatar', <FileStorage: 'avatar.jpg' ('image/jpeg')>)]) 48 """ 49 50 # 接受上传文件 51 # avatar = request.files["avatar"] 52 # print(avatar) 53 """ 54 <FileStorage: 'avatar.jpg' ('image/jpeg')> 55 from werkzeug.datastructures import FileStorage 56 FileStorage,上传文件处理对象,flask封装的一个上传文件处理对象,可以允许我们直接调用对应的方法进行文件的存储处理, 57 也可以结合其他的ORM模块像djangoORM那样通过模型操作对上传自动存储处理 58 """ 59 # 处理上传文件[一般不会这么做!!!而是采用专业的第三方存储设备来存储,] 60 # from pathlib import Path 61 # save_path = str(Path(__file__).parent / "uploads/avatar.jpeg") 62 # avatar.save(save_path) 63 64 # 获取请求头信息 65 print(request.headers) # 获取全部的而请求头信息 66 print(request.headers.get("Host")) # 127.0.0.1:5000,客户端请求地址,也相当于服务器地址 67 68 # 获取客户端发送过来的自定义请求头 69 print(request.headers.get("company")) # beijing,不存在的键的结果:None,存在则得到就是值, 70 print(request.headers.get("token")) # jwt...xxx 71 72 # 获取客户端的请求头中的相关数据 73 print(request.user_agent) # 用户访问服务器时使用的网络代理,一般就是浏览器标记信息,PostmanRuntime/7.26.10 74 print(request.remote_addr) # 客户端远程地址 75 print(request.server) # 服务端的端点,格式:(IP, 端口) 76 77 # 获取请求方法 78 print(request.method) # POST 79 80 # 本次请求的url地址 81 print(request.url) # http://127.0.0.1:5000/data 82 print(request.root_url) # 根路径 83 print(request.path) # /data 84 85 86 return "获取请求体" 87 88 89 if __name__ == '__main__': 90 app.run()
2、获取请求体,代码:
from flask import Flask, request from werkzeug.datastructures import ImmutableMultiDict from werkzeug.wrappers import Request as RequestBase from flask.wrappers import Request # 项目实例应用对象 app = Flask(__name__) # 加载配置 app.config.update({ "DEBUG": True }) # 在http的常用请求方法中,delete和get是没有请求体的!!! @app.route(rule="/data", methods=["post", "put", "patch"]) def data(): """获取请求体""" # 获取原生的请求体数据[当request对象的其他属性没法接受请求体数据时,会把数据保留在data中,如果有request对象的属性处理了请求体数据,则data就不再保留] # print(request.data) # 如果客户端上传的是xml文档,html格式,二进制流格式,base64格式,就要使用data来接收 """ 1. 没有上传任何数据: b'' 2. 上传json数据 b'{\n "username": "xiaoming",\n "age": 16\n}' 3. 上传表单数据 b'' 4. 上传xml数据 b'<goods-list>\n <goods price="100">\xe5\x95\x86\xe5\x93\x81\xe6\xa0\x87\xe9\xa2\x981</goods>\n <goods price="200">\xe5\x95\x86\xe5\x93\x81\xe6\xa0\x87\xe9\xa2\x982</goods>\n</goods-list>' """ # 接收表单上传的数据 # print(request.form) """ ImmutableMultiDict([('name', '小明'), ('age', '19')]) """ # 接收ajax上传的json数据 # print(request.json) # {"username": "xiaoming", "age": 16} # print(request.is_json) # True 往往用于判断是否是ajax请求 # 上传文件列表 HTML必须以<form method="post" enctype="multipart/form-data"> # 表单属性才能上传文件 # print(request.files) """ ImmutableMultiDict([('avatar', <FileStorage: 'avatar.jpg' ('image/jpeg')>)]) """ # 接受上传文件 # avatar = request.files["avatar"] # print(avatar) """ <FileStorage: 'avatar.jpg' ('image/jpeg')> from werkzeug.datastructures import FileStorage FileStorage,上传文件处理对象,flask封装的一个上传文件处理对象,可以允许我们直接调用对应的方法进行文件的存储处理, 也可以结合其他的ORM模块像djangoORM那样通过模型操作对上传自动存储处理 """ # 处理上传文件[一般不会这么做!!!而是采用专业的第三方存储设备来存储,] # from pathlib import Path # save_path = str(Path(__file__).parent / "uploads/avatar.jpeg") # avatar.save(save_path) # 获取请求头信息 print(request.headers) # 获取全部的而请求头信息 print(request.headers.get("Host")) # 127.0.0.1:5000,客户端请求地址,也相当于服务器地址 # 获取客户端发送过来的自定义请求头 print(request.headers.get("company")) # beijing,不存在的键的结果:None,存在则得到就是值, print(request.headers.get("token")) # jwt...xxx # 获取客户端的请求头中的相关数据 print(request.user_agent) # 用户访问服务器时使用的网络代理,一般就是浏览器标记信息,PostmanRuntime/7.26.10 print(request.remote_addr) # 客户端远程地址 print(request.server) # 服务端的端点,格式:(IP, 端口) # 获取请求方法 print(request.method) # POST # 本次请求的url地址 print(request.url) # http://127.0.0.1:5000/data print(request.root_url) # 根路径 print(request.path) # /data return "获取请求体" if __name__ == '__main__': app.run()
三、响应
flask默认支持2种响应方式:
数据响应: 默认响应html文本,也可以返回 JSON格式,或其他格式
页面响应: 重定向
url_for 视图之间的跳转
响应的时候,flask也支持自定义http响应状态码
3.1、响应html文本
from flask import Flask,make_response, Response app = Flask(__name__) app.config.update({ "DEBUG": True }) @app.route("/") def index(): # 默认返回的就是HTML代码,在flask内部调用视图时,得到的返回值会被flask判断类型, # 如果类型不是response对象,则视图的返回值会被作为response对象的实例参数返回客户端 # return "<h1>hello</h1>", 400, {"company": "python"} # return make_response("<h1>hello</h1>", 400, {"company": "python"}) return Response(f"默认首页", 201, {"company": "python"}) if __name__ == '__main__': app.run()
3.2、返回Json数据
from flask import Flask, jsonify from decimal import Decimal app = Flask(__name__) app.config.update({ "DEBUG": True, "JSONIFY_PRETTYPRINT_REGULAR": False, }) @app.route("/") def index(): # """返回json格式数据,返回json字典""" # data = {"name":"xiaoming","age":16} # return data # """返回json格式数据,返回各种json数据,包括列表""" data = [ {"id": 1, "username": "liulaoshi", "age": 18}, {"id": 2, "username": "liulaoshi", "age": 17}, {"id": 3, "username": "liulaoshi", "age": 16}, {"id": 4, "username": "小明", "age": Decimal(15)}, ] return jsonify(data) if __name__ == '__main__': app.run()
from flask import Flask, redirect # 应用实例对象 app = Flask(__name__) @app.route("/") def index(): """页面跳转""" """ 301: 永久重定向,页面已经没有了,站点没有了,永久转移了。 302:临时重定向,一般验证失败、访问需要权限的页面进行登录跳转时,都是属于临时跳转。 """ # redirect函数就是response对象的页面跳转的封装 # response = redirect("http://www.qq.com", 302) # redirect的原理,最终还是借助Resonse对象来实现: response = "", 302, {"Location": "http://www.163.com"} return response if __name__ == '__main__': # 启动项目的web应用程序 app.run(host="0.0.0.0", port=5000, debug=True)
3.3.2、重定向到自己写的视图函数
可以直接填写自己 url 路径
也可以使用 url_for 生成指定视图函数所对应的 url
from flask import url_fo @app.route("/info") def info(): return "info" @app.route("/user") def user(): url = url_for("info") print(url) return redirect(url)
3.3.3、重定向到带有路径参数的视图函数
在url_for函数中传入参数
from flask import Flask, redirect, url_for # 应用实例对象 app = Flask(__name__) @app.route("/demo/<int:mob>") def mobile(mob): print(mob) return f"mobile={mob}" @app.route("/sms") def sms(): """携带路径参数进行站内跳转""" # url_for("视图方法名", 路由路径参数) url = url_for("mobile", mob=13312345678) print(url) return redirect(url) if __name__ == '__main__': # 启动项目的web应用程序 app.run(host="0.0.0.0", port=5000, debug=True)
4、自定义状态吗和响应头
在flask中,可以很方便的返回自定义的状态码,以实现不符合http协议的状态码,例如:status code 400
from flask import Flask, redirect, url_for, make_response, Response # 应用实例对象 app = Flask(__name__) @app.route("/rep") def rep(): """常用以下写法""" return "ok", 201, {"Company":"python-35"} # """原理""" # response = make_response("ok", 201, {"Company": "python-35"}) # return response # # """原理""" # response = Response("ok") # response.headers["Company"] = "oldboy" # 自定义响应头 # response.status_code = 201 # 自定义响应状态码 # return response if __name__ == '__main__': # 启动项目的web应用程序 app.run(host="0.0.0.0", port=5000, debug=True)
5、http的会话控制
所谓的会话(session),就是客户端浏览器和服务端网站之间一次完整的交互过程.
会话的开始是在用户通过浏览器第一次访问服务端网站开始.
会话的结束时在用户通过关闭浏览器以后,与服务端断开.
所谓的会话控制,就是在客户端浏览器和服务端网站之间,进行多次http请求响应之间,记录、跟踪和识别用户的信息而已。
为什么要有会话控制?因为 http 是一种无状态协议,浏览器请求服务器是无状态的。
无状态:指一次用户请求时,浏览器、服务器无法知道之前这个用户做过什么,对于服务端而言,客户端的每次请求都是一次新的请求。
无状态原因:浏览器与服务器是使用 socket 套接字进行通信的,服务器将请求结果返回给浏览器之后,会关闭当前的 socket 连接,而且客户端也会在处理页面完毕之后销毁页面对象。
有时需要保持下来用户浏览的状态,比如用户是否登录过,浏览过哪些商品等
实现状态保持主要有两种方式:
-
在客户端存储信息使用
Cookie(废弃),token[jwt,oauth]
-
在服务器端存储信息使用
Session
Cookie是由服务器端生成,发送给客户端浏览器,浏览器会将Cookie的key/value保存,下次请求同一网站时就随着请求头自动发送该Cookie给服务器(前提是浏览器设置为启用cookie)。Cookie的key/value可以由服务器端自己定义。
使用场景: 登录状态, 浏览历史, 网站足迹,购物车 [不登录也可以使用购物车]
Cookie是存储在浏览器中的一段纯文本信息,建议不要存储敏感信息如密码,因为电脑上的浏览器可能被其它人使用
Cookie基于域名安全,不同域名的Cookie是不能互相访问的
如访问fuguang.com时向浏览器中写了Cookie信息,使用同一浏览器访问baidu.com时,无法访问到fuguang.com写的Cookie信息,只能获取到baidu.com的Cookie信息。
浏览器的同源策略针对cookie也有限制作用.
当浏览器请求某网站时,浏览器会自动将本网站下所有Cookie信息随着http请求头提交给服务器,所以在request中可以读取Cookie信息
设置cookie
设置cookie需要通过flask的Response响应对象来进行设置,由响应对象会提供了方法set_cookie给我们可以快速设置cookie信息。
@app.route("/set_cookie") def set_cookie(): """设置cookie,通过response传递到客户端进行保存""" response = make_response('默认首页') response.set_cookie('username', 'xiaoming') # session会话期有效,关闭浏览器后当前cookie就会被删除 response.set_cookie('user', 'xiaoming', max_age=30 ) # 指定有效时间,过期以后浏览器删除cookie,max_age=150秒 return response
@app.route("/get_cookie") def get_cookie(): """获取来自客户端的cookie""" print(request.cookies) # ImmutableMultiDict([]) username = request.cookies.get('username') # 没有值则返回None user = request.cookies.get('user') # 没有值则返回None print(f"username={username},user={user}") # username=xiaoming,user=xiaoming return "get cookie"
@app.route("/del_cookie") def del_cookie(): """删除cookie,重新设置cookie的时间,让浏览器自己根据有效期来删除""" response = make_response('del cookie') # 删除操作肯定是在浏览器完成的,所以我们重置下cookie名称的对饮有效时间为0,此时cookie的值已经不重要了。 response.set_cookie('user', '', max_age=0) response.set_cookie('username', '', max_age=0) return response
对于敏感、重要的信息,建议要存储在服务器端,不能存储在浏览器中,如手机号、验证码等信息
在服务器端进行状态保持的方案就是Session
Session依赖于Cookie,session的ID一般默认通过cookie来保存到客户端。名字一般叫:sessionid
flask中的session需要加密,所以使用session之前必须配置SECRET_KEY选项,否则报错.
如果将来希望session的生命周期延长,可以通过修改cookie中的sessionID的有效期来完成配置。
注意:一般框架都是把session数据保存到服务端,但是,flask里面的session是基于token方式存储在客户端的,并没有安装传统的方式保存在服务端的文件中。
@app.route("/set_session") def set_session(): """设置session""" session['username'] = 'xiaoming' session['info'] = { "name": "xiaohong", "age": 16, } return "set_session"
可以通过客户端浏览器中的sessionid观察,其实默认情况下,flask中的session数据会被加密保存到cookie中的。当然,将来,我们可以采用flask-session第三方模块把数据转存到其他的存储设备,例如:redis或者mysql中。
@app.route("/get_session") def get_session(): """获取session""" print(session.get('username')) print(session.get('info')) return "get session"
@app.route("/del_session") def del_session(): """删除session,键如果不存在,则会抛出异常,所以删除之前需要判断键是否存在。""" if "username" in session: session.pop("username") if "info" in session: session.pop("info") return "del_session"
使用过程中,session是依赖于Cookie的,所以当cookie在客户端被删除时,对应的session就无法被使用了。