Flask09_post请求2(json、文件、csrf)

  • Post author:
  • Post category:其他


# encoding=utf-8
"""
1、处理和响应json数据
(1)POST方法传送的数据,是用&符号分割的键值对格式:key=value
(2)可以用JSON格式(小巧和易用)、XML格式(重量、规范繁琐)表示
(3)request.headers可以获取到post请求发送数据的请求头,请求头中Content-Type的值是application/json
(4)传送的数据是JSON格式,可以通过request.json方法查看
(5)会自动将json数据转换成python字典或列表,可以使用type函数查看:type(request.json)
(6)request.json数据可以看做字典,支持所有字典的方法
(7)request.json.get(需求键)/request.json[需求键]方法
    A、可以在字典参数中重大奥对应的数据键时,可以通过指定键获取到数据(类似字典取值)
    B、无法在字典参数中找到对应的数据键时,request会返回内置的None,Flask服务器会报500错误
        >>先判断得到的数据是否为None,再进行处理
        >>通过request.json.get(需求键,默认值)的方式为其设置默认值,当无法获取到键时,直接使用默认值代替
(8)request.json.getlist(需求键):post请求数据存放于组合数据类型中有多个值时,返回到一个列表中
(9)通过flask.Response响应JSON时,要通过json.dumps把响应体字典改成JSON格式,响应头的Content-Type通过mimetype参数设置为application/json
(10)通过flask.jsonify响应JSON时,可以直接将响应体字典改成JSON格式,响应头的Content-Type自动变换成application/json
(11)需要服务器的HTTP响应头更好可定制性,可以修改add()函数:resp响应对象.headers.add('键key','值value')
2、上传文件
(1)上传文件
    A、请求方法:一般也是用POST方法
    B、后端接收:需要request.files接收数据
    C、存储方式:在保存图片的同时,将路径保存到数据库
(2)在项目中创建目录static/uploads/imgs
    A、windows中直接手动创建即可;linux中执行命令mkdir 项目名称/static/uploads/imgs
    B、实例参数设置,文件上传目录
        folder = 'static/uploads/imgs/'
        app.config['UPLOAD_FOLDER'] = folder
    C、设置上传文件大小(16M),超过会抛出异常
        app.config['MAX_CONTENT_LENGTH'] = 16 * 1000 * 1000
    D、设置限定文件格式(使用集合,自动去重)
        suffix = {'png', 'jpg', 'jpeg', 'gif'}
        app.config['ALLOWED_EXTENSIONS'] = suffix
C、通过url/upload使用POST上传,上传的图片存放在服务器端的static/uploads/imgs目录下
(3)werkzeug库可以判断文件名是否安全:from werkzeug.utils import secure_filename
    A、假设上传的图片只允许png、jpg、jpeg、gif四种格式,设置支持的文件格式(使用集合,自动去重)
        suffix = {'png', 'jpg', 'jpeg', 'gif'}
        app.config['ALLOWED_EXTENSIONS'] = suffix
    B、函数内部post方法获取到传递的文件对象:file = request.files['image']
    C、自定义规则,判断文件对象是允许的格式-判断文件对象是否有后缀以及后缀是否在后缀集合中:
        def allowed(name):
            # 判断文件有后缀名
            if '.' in name:
                # 判断后缀名在允许格式内
                if name.split('.')[-1] in suffix:
                    return True
                else:
                    return False
            else:
                return False
    D、调用自定义格式规则,判断文件合法(文件对象存在,文件是允许的格式)
        if file and allowed(file.filename):
            # 获取文件名,secure_filename检查客户端上传的文件名,确保安全
            name = secure_filename(file.filename)
            # 将文件保存到static/uploads/imgs目录,文件名同上传时使用的文件名
            file.save(os.path.join(app.root_path, folder, name))
            # 返回提示成功信息
            return '文件{name}上传成功~'
        else:
            return '文件上传失败~'
(4)app.config中的config是字典的子类,可以用来设置自有的配置信息,也可以设置自己的配置信息
    A、设置文件上传目录:app.config['UPLOAD_FOLDER'] = 'static/uploads/imgs/'
    B、设置支持的文件格式:app.config['ALLOWED_EXTENSIONS'] = {'png', 'jpg', 'jpeg', 'gif'}
    C、设置限制上传的文件大小:app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 #16MB
(5)file是上传文件对应的对象
    A、file.save(path)用来将file保存在服务器的文件系统中,参数最好是绝对路径,否则会报错
    B、app.root_path获取所在文件的目录在文件系统中的绝对路径
    C、os.path.join()用来将使用合适的路径分隔符将路径组合起来
    D、在处理上传文件时候,需要使用try:...except:...,将当前目录下的文件将上传到服务器
    E、可以在static/uploads/imgs中看到上传的文件,可以获取上传文件的内容:content = file.stream.read()
        @app.route('/upload', methods=['GET', 'POST'])
        def upload():
            if request.method == 'POST':
                f = request.files['file']
                print(request.files)
                # secure_filename检查客户端上传的文件名,确保安全,注意文件名称不要全中文!!!
                f.save(os.path.join(folder, secure_filename(f.filename)))
                return render_template('upload.html')
            else:
                return render_template('index.html')
        <h3>上传文件</h3>
        <form action="/upload/" method="POST" enctype="multipart/form-data">
            <input type="file" name="file" >
            <input type="submit" value="提交">
        </form>
(6)下载文件方式:from flask import send_from_directory
    A、send_from_directory(url, filename, as_attachment=True)
    B、将url指定的目录下的指定文件发送到客户端
    C、url:跳转的路由地址('./')
    D、filename:下载的文件名
    E、as_attachment=True:文件作为附件下载
        @app.route('/download/<filename>', methods=['GET', 'POST'])
        def download(filename):
            return send_from_directory('./upload', filename, as_attachment=True)
        <h3>下载文件</h3>
        <a href="/download/sample.txt">下载sample.txt</a>
3、csrf跨站请求伪造攻击
(1)概念:使用还在生效的cookie对指定的网站进行攻击
(2)原理:利用用户的身份向受信网站发起违法请求,获取利益
    >> 访问B网站通过之后,会下发一个cookie给用户浏览器
    >> 在cookie还在生效时,访问C网站,C网站可能会获取到下发给用户的cookie
    >> C网站可以使用用户的cookie访问B网站
(3)解决方法:在用户合法请求网页,每次刷新下发一个校验值,提交请求会携带校验值,证明请求的来源
    >> 用户请求Flask,由Flask下发一个随机的校验值(字符串),再次请求携带校验值,flask通过校验值校验用户,校验值每次随着页面的刷新在变化,校验值被称为csrf_token
    >> C网站可以获取到用户的cookie及失效的csrf_token,访问B网站会失败
(3)Flask-wtf可以进行csrf_token的防护,csrf一般不会对get请求进行防护
(4)使用方法:wtf有编写好的csrf防御机制,只需要开启即可
    A、导入CSRF保护:from flask_wtf import CSRFProtect
    B、实例化: csrf=CSRFProtect()
    C、绑定app:csrf.init_app(app)
(5)前端需要使用csrf_token
    A、报错:Bad Request..The CSEF token is missing
    B、在post的form表单中,添加隐藏input
    <input type="hidden" value="{{ csrf_token() }}" name="csrf_token">
(6)避免csrf_token验证
    A、csrf一旦开启,所有的post请求都会开启csrf保护
    B、避免csrf:只需在视图中的路由上添加@csrf.exempt装饰器
"""
import os
from flask import Flask
from flask import request
from flask import jsonify
from flask import render_template
from werkzeug.utils import secure_filename
from flask_wtf import CSRFProtect


basic = "F:/MyProject"

# 创建web服务器(http)实例:内置方法__name__是预定义变量,被设置为使用本模块,html存放的路径,静态文件的路径
app = Flask(__name__)

# 实例化csrf
csrf=CSRFProtect()
# csrf绑定web实例
csrf.init_app(app)

# 支持post请求:路由上添加methods方法(允许方式均可写入列表中)
@app.route('/demo/json', methods=['GET', 'POST'])
def json_demo():
    # request.headers:获取post请求的HTTP请求头Content-Type发生改变
    print(request.headers)
    # 查看post请求向服务器传递的参数对象类型
    print(type(request.json))
    # 获取post请求的参数数据
    print(request.json)
    # 获取post请求的某个参数数据-支持字典的一切方法
    print(request.json['name'])
    # post请求的字典参数中不存在时,进行判断处理
    user = request.form.get('user')
    if user is None:
        print("参数数据user不存在~")
    else:
        print(f"参数数据user为:{user}")
    # post请求的字典参数中不存在时,进行默认值设置
    print(request.form.get('nick', '未知'))
    # post请求的字典参数中存在组合类型的多个数据时
    print(request.form.getlist('hobby'))
    # 设置响应数据字典
    result = {
        'mode': '测试',
        'status': '成功',
        'code': 200
    }
    # 响应JSON时,要把响应体改成JSON格式,响应头的Content-Type也要设置为application/json
    # resp = Response(json.dumps(result), mimetype='application/json')
    # 可以使用flask内置的jsonify工具函数将字典直接转换成json响应格式
    resp = jsonify(result)
    # 需要服务器的HTTP响应头更好可定制性,可以修改add()函数
    resp.headers.add('Server', 'Python/Flask')
    return resp


# 设置文件上传目录
folder = 'static/uploads/imgs/'
app.config['UPLOAD_FOLDER'] = folder

# 设置支持的文件格式(集合类型)
suffix = {'png', 'jpg', 'jpeg', 'gif'}
app.config['ALLOWED_EXTENSIONS'] = suffix
# 判断文件名是否是我们支持的格式
def allowed(name):
    # 判断文件有后缀名
    if '.' in name:
        # 通过secure_filename获取到文件的后缀名,判断后缀名在允许格式内
        if secure_filename(name) in suffix:
            return True
        else:
            return False
    else:
        return False

# 支持post请求:路由上添加methods方法(允许方式均可写入列表中)
@app.route('/demo/file', methods=['GET', 'POST'])
def file_demo():
    file = request.files['image']
    if file and allowed(file.filename):
        # 获取文件的文件名
        name = file.filename
        # 获取存储文件的路径
        filepath = os.path.join(basic, folder, name)
        # 获取存储文件目录的路径
        dirpath = os.path.dirname(filepath)
        # 判断存储目录是否存在,不存在则创建
        if not os.path.exists(dirpath):
            os.makedirs(dirpath)
        # 将文件保存到static/uploads/imgs目录,文件名同上传时使用的文件名
        print(app.root_path, folder, name)
        file.save(os.path.join(basic, folder, name))
        # 返回提示成功信息
        return f'文件{name}上传成功~'
    else:
        return '文件上传失败~'


# 设置csrf表单校验(开启csrf即可)
@app.route("/csrf/demo",methods=["GET","POST"])
def scrf_demo():
    return render_template("csrf表单校验.html",**locals())

# 使用@csrf.exempt可以在开启csrf的情况下,此视图去除csrf校验
@csrf.exempt
@app.route("/csrf/exempt",methods=["GET","POST"])
def csrf_exempt():
    return render_template("csrf表单校验.html",**locals())


# 启动项目服务器--可以通过参数更改,通常网站的页面需要放到网站服务器上,用户无法双击打开
if __name__ == '__main__':
    def run(self):
        app.run(
            # host:主机地址
            host="localhost",
            # port:访问端口
            port=80,
            # debug:调试模式,测试环境True,生产环境Fasle
            debug=True,
            # use_reloader:自动重启
            use_reloader=True
        )



版权声明:本文为weixin_43867837原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。