前言

好久没写了, 主要是太忙了, 本篇记一下今天解决的一个问题吧, 耗了我大半天的时间才解决

问题

今天在调试代码时, 发现了一个诡异的问题, 我之前写了一个接口, 作用是接收上传的文件, 因为这个接口需要一定的权限控制, 所以我写了3个装饰器在上面, 这个项目是用的 flask, 代码类似于

@app.route('/upload', methods=['POST'])
@login_requireds
@verify_requireds
@upload_requireds
def upload_file():
pass

每个装饰器代码类似于

def verify_requireds(func):
# 阻止未审核的账户进行操作
@functools.wraps(func)
def inner(*args, **kwargs):
pass
if user_dict.get("verify") != 1:
response_msg = {"status": 300, "msg": "Sorry, Your · account is not audited", "msg_zh": "该账户未审核"}
return jsonify(response_msg)
return func(*args, **kwargs)
return inner

而在测试当中, 发现前端的请求一直是 504 错误, 而后端的log显示每次都正常返回了数据, 类似



后端的log



然后就开始了漫长的捉虫

首先通过postman测试发现postman并没有问题, 猜测是不是跨域问题, 我们使用了 flask-cors 来进行跨域设置, 我们是这样设置的

from flask_cors import CORS

CORS(app, support_credentials=True)

为了印证该猜想, 我们开启了 flask-cors 的 debug 模式,

logging.getLogger('flask_cors').level = logging.DEBUG

看到了options 请求时, debug 打印

DEBUG Request to '/upload' matches CORS resource '/*'. Using options: {'origins': ['.*'], 'methods': 'DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT', 'allow_headers': ['.*'], 'expose_headers': None, 'supports_credentials': False, 'max_age': None, 'send_wildcard': False, 'automatic_options': True, 'vary_header': True, 'resources': '/*', 'intercept_exceptions': True, 'always_send': True, 'support_credentials': True}

所以初步排除了是跨域导致的问题

后来猜测是不是前端的代码问题, 后来前端 debug 调试发现是浏览器就返回了 504 错误, 就算是BUG也很难去通过前端去解决了

后来无意中发现, 其实并不是每次都返回 504, 当文件足够小时(1k左右), 请求都是正常的, 而文件大时, 则有一定几率会返回正常, 发现了这个现象时, 我们开始考虑是不是后端会 “夯住” 或者前端的连接并没有断开而是一直保持呢? 但是确实postman一切ok, 所以是不是请求有些异常, 而框架并没有很好的处理, postman兼容性很好, 忽略了这个错误呢?

我们使用软件进行了抓包, 发现了问题

抓包如图, 我们发现, 其实在前端发出请求后, 后端确实返回了200并且已经被读取, 但是后续又向前端发送了 RST 的数据包

搜了一下 RST 的作用, 其实就是代表服务端告诉客户端, 我要断开链接了, 你的还没发送的数据包直接丢掉吧, 可能就是这样的操作让前端框架以为后端断开链接了, 报了 504/502 错误

其实正常情况下, 触发 RST 的情况也只有去访问一个不存在的端口或服务时才会有, 所以前端框架这样统一判断也情有可原, 而且 RST 的滥用也可能引发安全问题 https://baike.baidu.com/item/RST%E6%94%BB%E5%87%BB

再结合之前发现的, 很小的文件几乎每次都正常, 我们大致找到了原因

因为我们现有的逻辑, 在装饰器执行过程中, 还没有获取 file 文件, 如果装饰器直接拦截, 此时可能 file 还没有完全接收完毕, 此时 flask 会发送 RST 告诉客户端抛弃发送, 而前端就会报错, 而小文件发送特别快, 不存在数据未发送完成的情况, 所以小文件是 ok 的

所以解决办法就非常简单了, 在装饰器前再加一个装饰器, 这个装饰器的作用是获取 file 文件, 顺便做一下如果没有 file 文件返回一个错误, 确保在服务端返回之前已经完全接收到了 file 文件即可.

我们增加一个 file 装饰器

def file_requireds(func):
# 对文件进行校验
@functools.wraps(func)
def inner(*args, **kwargs):
files = request.files.get('file')
if not files:
return jsonify({"status": 300, "msg": "not find file", "msg_zh": "没有文件"})
return func(*args, **kwargs)
return inner

加入到接口的装饰器大军中

@app.route('/upload', methods=['POST'])
@file_requireds
@login_requireds
@verify_requireds
@upload_requireds
def upload_file():

再测试就完全可以了, 抓包也一切正常了

此例警醒我以后接受文件相关的接口一定要将文件全部获取到再进行操作, 或者是前端考虑解决办法

记一次flask上传文件返回200前端却504的问题的更多相关文章

  1. FLask上传文件

    目录 Flask上传文件 改进上传 上传进度条 一个更简便的方案 Flask上传文件 文件上传的基本原理实际上很简单,基 本上是: 一个带有 enctype=multipart/form-data 的 ...

  2. flask上传文件到指定路径

    flask上传文件到指定路径 项目结构如下: 首先是:视图函数uload_file.py,代码如下: #!/usr/bin/env python # -*- coding: utf-8 -*- fro ...

  3. 使用input:file控件在微信内置浏览器上传文件返回未显示选择的文件

    使用input:file控件在微信内置浏览器上传文件返回未显示选择的文件 原来的写法: <input type="file" accept="image/x-png ...

  4. Nginx上传文件返回413的解决

    通过http上传文件时返回403 Request Entity Too Large错误时,原因是默认设置的允许上传文件太小,默认是2M,如果上传文件大小大于2M时,那么就会返回413的错误,修改ngi ...

  5. jquery ajaxform上传文件返回不提示信息的问题

    在使用jquery的ajaxform插件进行ajax提交表单并且上传文件的时候,返回类型datatype :json但是后台通过写出一个json对象后,在执行完以后没有进入success函数,而是直接 ...

  6. flask 上传文件

    flask upload 近日在学习python,接触到了flask框架,刚好客户有个需求,需要在网页上传一个python 代码的zip包,然后使用docker 容器运行这个zip里面的程序,输出结果 ...

  7. 记一次FTP上传文件总是超时的解决过程

    好久没写博,还是重拾记录一下吧. 背景:买了一个阿里云的云虚拟机用来搭建网站(起初不了解云虚拟主机和云服务器的区别,以为都是有SSH功能的,后来发现不是这样样子啊,云虚拟机就是FTP上传网页+MySQ ...

  8. 记一次yii2 上传文件

    1 view渲染 <form action="../src/website/import/report-flow" method="post" encty ...

  9. flask上传文件时request.files为空的解决办法

    在做上传文件的时候遇到request.files是空 原因在于html中的表单form没有指明 enctype="multipart/form-data" <form met ...

随机推荐

  1. 剑指offer二刷——数组专题——数组中重复的数字

    题目描述 在一个长度为n的数组里的所有数字都在0到n-1的范围内. 数组中某些数字是重复的,但不知道有几个数字是重复的.也不知道每个数字重复几次.请找出数组中任意一个重复的数字. 例如,如果输入长度为 ...

  2. Panda 交易所热点关注:股权交易中心+区块链试点将开始

    近期,Panda 交易所注意到,中国证监会已同意北京.上海等5家区域性股权市场参与区块链建设试点工作.Panda 交易所获悉的具体情况是,北京股权交易中心曾联合其他单位共同推出区域性股权市场中介机构征 ...

  3. 关于Android手机CPU不同架构的问题

    1.共有7种架构 armeabiv-v7a: 第7代及以上的 ARM 处理器.2011年15月以后的生产的大部分Android设备都使用它. arm64-v8a: 第8代.64位ARM处理器,很少设备 ...

  4. 网络编程-python实现-socket(1.1.1)

    @ 目录 1.不同电脑进程之间如何通信 2.什么是socket 3.创建socket 1.不同电脑进程之间如何通信 利用ip地址 协议 端口 标识网络的进程,网络中的进程通信就可以利用这个标志与其他进 ...

  5. Hibernate实现对数据的CRUD

    今天主要去看公司的老框架, CRUD用的较多,所以总结一下步骤,以免忘记的时候温习 回顾 JDBC 工作过程: 加载驱动 建立连接 定义sql,发生sql语句 执行sql语句获得执行结果 处理返回结果 ...

  6. zabbix学习(一)——LNMP环境搭建及zabbix安装

    第一部分:LNMP环境搭建 一.环境说明: OS:   centos7.6_x64nginx:nginx-1.16.0php:   php-7.1.11mysql:mysql-5.6.44 zabbi ...

  7. matplotlib学习日记(十)-划分画布的主要函数

    (1)函数subplot()绘制网格区域中的几何形状相同的子区布局 import matplotlib.pyplot as plt import numpy as np '''函数subplot的介绍 ...

  8. EF Core 三 、 骚操作 (导航属性,内存查询...)

    EF Core 高阶操作 本文之前,大家已经阅读了前面的系列文档,对其有了大概的了解 我们来看下EF Core中的一些常见高阶操作,来丰富我们业务实现,从而拥有更多的实现选择 1.EF 内存查找 wh ...

  9. android 使用 Lottie

    1.添加依赖 dependencies { implementation 'com.airbnb.android:lottie:2.5.5'//lottie } 2.1layout实现 <?xm ...

  10. Python基础(上篇)

    本篇文章主要内容:变量.注释.运算符.关键字.数据类型. 在入手变量之前我们先来看看经典的编程语句 → hello world 在python3中输出到控制台的函数是print() print(&qu ...