Flask-1-04-Request
在开始学习代码之前先介绍一个工具叫做Postman :
对就长上面那样,它是一款非常适合开发人员使用的调试工具,可以根据你发起的请求携带一些参数,起码我现在是这么理解的,各种你要发送给服务端的参数
那么就进入今天的正题:
获取请求从参数:
首先我们需要从框架中导入,Flask为我们准备的request,它就是Flask中表示当前请求的request对象。request对象中保存了一次Http请求的信息
首先我们通过Postman为我们发起一条请求信息,在这之前,我们需要先定义一个视图,让它找到请求的路径
# coding:utf-8 from flask import Flask, request app = Flask(__name__) @app.route('/index', methods=['GET', 'POST']) def index(): return 'test ok' if __name__ == '__main__': app.run(host='0.0.0.0', debug=True)
不重要的注意:为了让文档看起来不那么冗长,我选择性的减少了空行
通过上面一系列的操作大致应该了解如果发送一次请求,接下来的有些操作,有可能是文字形容不太清楚的。所以仔细的看一下截图的每一个带有选框和你下载Postman不一样的地方。
Request的常用属性
-
form
一个包含解析过的从 POST 或 PUT 请求发送的表单对象的 MultiDict 。请注意上传的文件不会在这里,而是在 files 属性中。
-
args
一个包含解析过的查询字符串( URL 中问号后的部分)内容的 MultiDict 。
-
values
一个包含 form 和 args 全部内容的 CombinedMultiDict 。
-
cookies
一个包含请求中传送的所有 cookie 内容的 dict 。
-
stream
如果表单提交的数据没有以已知的 mimetype 编码,为性能考虑,数据会不经修改存储在这个流中。大多数情况下,使用可以把数据提供为字符串的 data 是更好的方法。流只返回一次数据。
-
headers
进入请求的标头存为一个类似字典的对象。
-
data
如果进入的请求数据是 Flask 不能处理的 mimetype ,数据将作为字符串存于此。
-
files
一个包含 POST 和 PUT 请求中上传的文件的 MultiDict 。每个文件存储为FileStorage 对象。其基本的行为类似你在 Python 中见到的标准文件对象,差异在于这个对象有一个 save() 方法可以把文件存储到文件系统上。
-
environ
底层的 WSGI 环境。
-
method
当前请求的 HTTP 方法 (POST , GET 等等)
首先我们通过Postman发送一个from表单请求,先写好后台接收的视图,然后再发起请求
# coding:utf-8 # request中包含了前端发送过来的所有请求数据 from flask import Flask, request app = Flask(__name__) @app.route('/index', methods=['GET', 'POST']) def index(): # 接收客户端请求过来的数据 # 通过requset.form可以直接提取请求体中的表单格式的数据, 是一个类似字典的对象 name = request.form.get('username') pwd = request.form.get('password') return 'username = %s, password = %s' % (name, pwd) if __name__ == '__main__': app.run(host='0.0.0.0', debug=True)
一个不重要的注意:请把注意点放在Flask框架上,这个Postman不是重点,主要是后端通过request接收前端发送过来的数据才是重点,理清request才是最主要的。你也可以通过页面发送到视图,你任何可以发起请求的方式都是可以的不单单只有Postman
通过代码可以看出,我们通过request.form来接受前端发送过来的表单数据,接受的是类似字典的一个对象,既然是字典我们也可以通过这种方式来赋值给变量 request.form['username'],但是并不推荐后端来这么做。因为使用get()方法获取,就算找不到最多也就是返回一个None,不会对你的程序造成什么不必要的错误,但是如果这里你通过request.form['username']这种方式获取得话,在Web程序当中会直接返回一个400的状态码,会给你爆出一个错误,影响你的页面的流畅,如果你属于交付之后的状态,那么你的甲方一定会来找你麻烦,为了避免不必要的麻烦,请善待你的程序,避免不必要的Bug,那么接下来就来展示一下这些问题,代码会非常的像,请仔细观看
from flask import Flask, request app = Flask(__name__) @app.route('/index', methods=['GET', 'POST']) def index(): name = request.form['username'] # 注意这里为更改代码 # name = request.form.get('username') pwd = request.form.get('password') return 'username = %s, password = %s' % (name, pwd) if __name__ == '__main__': app.run(host='0.0.0.0', debug=True)
接下来在看一下如果通过request.form.get('username')这种方式接受前端发送过来的请求参数出现找不到的情况会是友好的吗?
from flask import Flask, request app = Flask(__name__) @app.route('/index', methods=['GET', 'POST']) def index(): # name = request.form['username'] name = request.form.get('username') # 这次是通过get()方法接受数据 pwd = request.form.get('password') return 'username = %s, password = %s' % (name, pwd) if __name__ == '__main__': app.run(host='0.0.0.0', debug=True)
如果表单中传过来同名的键,那么接受结果会如何呢?我在请求中传入了两个username分别是不同的值
通过返回的结果可以看出,返回给我们的是第一个找到的username的值,这里如果真的都想获取到,可以通过getlist()方法来接受请求的同名的键
from flask import Flask, request app = Flask(__name__) @app.route('/index', methods=['GET', 'POST']) def index(): # 通过get方法只能拿到多个同名参数的第一个 name = request.form.get('username') pwd = request.form.get('password') # 通过getlist() 可以获取多个 name_list = request.form.getlist('username') return 'username = %s, password = %s, username_list = %s' % (name, pwd, name_list) if __name__ == '__main__': app.run(host='0.0.0.0', debug=True)
注意:肯定会有小伙伴,会看见在字符串前面有一个u,这里简单的扩展一下,因为在Python2中,字符串类型默认是两种,一种是str,还有一种Unicode,这里展现的就是Unicode字符串,在Python3中已取消。是一个好吃的鸡肋。不用太在意,如果以后需要操作它,会在演示如何去使用
如果是请求体的数据不是表单格式的(如json格式),可以通过request.data获取
from flask import Flask, request app = Flask(__name__) @app.route('/index', methods=['GET', 'POST']) def index(): # 如果是请求体的数据不是表单格式的(如json格式),可以通过request.data获取 json_data = request.data return 'json_data = %s' % json_data if __name__ == '__main__': app.run(host='0.0.0.0', debug=True)
POST请求方式也是可以在地址栏里传递参数的,格式:192.168.3.20:5000/index?age=30&city=harbin
from flask import Flask, request app = Flask(__name__) @app.route('/index', methods=['GET', 'POST']) def index(): # args是用来提取url中的参数(查询字符串) age = request.args.get('age') city = request.args.get('city') return 'age : %s, city : %s' % (age, city) if __name__ == '__main__': app.run(host='0.0.0.0', debug=True)
一个不重要的注意:还有很多接受方法会在必要的时候引入
上传文件
用Flask处理文件上传非常的简单,只需要通过一个save()方法,就可以把文件保存到服务器的文件系统中,当然这也是一个非常危险的操作,之后会有介绍,已上传的文件会存储在内存或文件系统中一个临时的位置,你可以通过请求对象的files属性来访问他们,每一个上传的文件都会存储在这个字典中,它跟Python中的file对象基本一致。你也可以通过Python的方法来处理文件写入到本地的方法来完成这个操作
这里我们依然使用Postman来完成这个请求,但是如果你使用的是html页面,一定要确保在HTML表单中设置enctype="multipart/form-data" 属性,不然你的浏览器根本不会发送这个文件,我们还是先来完成后台的代码:
from flask import Flask, request app = Flask(__name__) @app.route('/', methods=['POST']) def upload(): # 使用files属性来访问保存在内存中或者文件系统中一个临时的位置的这个上传文件 file_obj = request.files.get('pic') # 使用save方法来保存刚刚获取的上传文件 file_obj.save('/home/python/图片/upload.jpg') return '文件上传完毕' if __name__ == '__main__': app.run(host='0.0.0.0', debug=True)
给你们展示一下上传的图片,屌丝气质暴露无遗,哈哈哈
可以看出在Flask中上传一个文件真的很简单,接下来,我们就讨论为什么直接使用save()方法会很危险,而且我在官方提供的文档中找到一个非常好的解决方案,可以研究一下,实在看不懂也是无所谓的。文件上传的基本概念实现上非常简单,流程是:
-
<form> 标签中有定义一个 enctype=multipart/form-data ,并且在里面包含一个 <input type=file> 标签。
-
服务端应用通过请求对象上的 files 字典访问文件。
-
使用文件的 save() 方法将文件永久地保存在文件系统上的某处。
这样就完成了,但是你永远都无法保证在浏览器访问你网站的那个人,不想搞些事情,比如XSS问题的HTML文件(跨站脚本攻击【XSS】),所以我们要限制上传文件的后缀,来尽量保证不要传入一些脚本文件或者别的什么东西。
# coding:utf-8 # request中包含了前端发送过来的所有请求数据 from flask import Flask, request from werkzeug import secure_filename import os # 存储上传文件的路径 UPLOAD_FOLDER = '/home/python/图片/' # 允许的文件类型 ALLOWED_EXTENSIONS = set(['txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif']) app = Flask(__name__) # 将路径添加到配置中 app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER # 用来验证用户上传的文件是否是允许的 def allowed_file(filename): # 判断文件中是否有. and 用.只截取1次文件名得到的一个列表,选择列表第二个值看是否属于被允许的文件类型 return '.' in filename and filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS @app.route('/', methods=['POST']) def upload(): # 使用files属性来访问保存在内存中或者文件系统中一个临时的位置的这个上传文件 file_obj = request.files.get('pic') # 判断用户是否上传了文件 和 文件是否是允许的类型 if file_obj and allowed_file(file_obj.filename): filename = secure_filename(file_obj.filename) file_obj.save(os.path.join(UPLOAD_FOLDER, filename)) return '文件上传完毕' else: return '文件上传失败' if __name__ == '__main__': app.run(host='0.0.0.0', debug=True)
通过Postman上传一个带有图片的请求,这里我们重新上传了一个别的文件
这里看出上传文件是没有问题的,现在我们来测试一下如果上传一个不被允许的文件类型,是否会帮我们阻拦
这里就可以阻拦我们不想看到的文件被上传,如果有人想问,文件里包含很多.会不会被阻拦,答案显然是不会,只要是在允许的类型里,都是可以上传的,因为rsplit()是从字符的最后面开始截取的,而且设置了只截取一次 rsplit('.', 1) 【注释 .代表用.截取,1代表截取1次】