Ajax GET请求已发送两次

时间:2019-08-03 01:33:51

标签: javascript jquery html ajax flask

作为Flask应用程序的一部分运行Ajax请求时,我遇到了令人困惑的行为。我已经编写了一个处理程序,接收到div单击,然后将包含某些数据的Ajax请求发送到我的app.py中指定的特定路由。然后将数据插入数据库。尽管在自己的计算机上运行Flask应用程序时,这种方法效果很好,但将应用程序移至另一个托管服务(Pythonanywhere)时,每次我单击div时,请求都会被发送两次,因为数据两次插入数据库证明了这一点。

之前,有人问过这个问题的类似变体(例如herehere),但是当我使用{{1 }}。此外,这些问题通常涉及与POST请求一起提交的HTML GET,因此是附加请求。但是,我的代码没有任何形式。

我的代码示例(简化了,但与我目前的工作基本相同):

form中:

POST

frontend.html中:

<div class='wrapper'>
   <div class='submit_stamp' data-timestamp='2019-8-2'>Submit</div>
</div>

frontend.js中:

$('.wrapper').on('click', '.submit_stamp', function(){
   $.ajax({
     url: "/submit_time",
     type: "get",
     data: {time: $(this).data('timestamp')},
     success: function(response) {
       $('.wrapper').append(response.html);
     },

   });
});

这样,每当我单击app.py元素时,Ajax请求都会触发两次,时间戳两次插入到我的数据库中,并且@app.route('/submit_time') def submit_time(): db_manager.submit_stamp(flask.request.args.get('time')) return flask.jsonify({'html':'<p>Added timestamp</p>'}) 被两次附加到submit_stamp之后。我为解决此问题所做的一些事情包括:

  1. 在处理程序中添加"Added timestamp"

  2. 使用布尔标志系统,在单击后立即将变量设置为.wrapper,并在event.stopPropagation()的{​​{1}}处理程序中将其重置为true 。我用这个布尔值将false包裹在条件语句中。

这些补丁均无效。但是,令我感到困惑的是,为什么success在我的计算机上运行时被调用一次,而在托管服务上运行时被调用两次。它与缓存有关吗?我该如何解决这个问题?非常感谢你!

编辑:

奇怪的是,重复请求很少发生。有时,仅发出一个请求,而其他时间,则重复请求。但是,我已经检查了Chrome中的Network XHR输出,它仅显示单个请求标头。

访问日志输出(删除IP):

.ajax

5 个答案:

答案 0 :(得分:4)

对于您的最新更新,我不得不说这不是重复的请求。在您的日志中,一个请求来自一台基于Windows的计算机上的Mozilla,另一个请求来自于Mac上的Chrome,这是两个不同位置发出的两个不同请求,它们恰好在时间上彼此接近。即使是在虚拟机上进行的测试,也不应记录多个操作系统或浏览器,因为VM会处理所有翻译,避免出现这种混乱情况。

您不包括IP地址,但是如果它们是公共地址(例如127.xxx,10.xxx或192.xxx之外的其他地址),则它们肯定是恰好正在使用您的软件的两个不同用户同时。

如果您要跟踪它是同一用户,则可能只是他们在2种不同的设备(例如台式机和移动电话)上使用您的软件。如果不允许这样做,请确保其访问权限反映了这一点。如果可以通过DNS将其跟踪到不同的地理位置,则您可能有一个被入侵的帐户可以锁定,直到真实用户确认其身份为止。

无论如何,用新数据对其进行切片,除非您可以通过可靠的测试来重现它,否则我认为它实际上不是您的软件。花时间考虑一下,它可能不是错误,而是其他。软件开发人员习惯于认为一切都是错误及其错误,而这可能是以前可能没有考虑过的良性或恶意攻击。

祝你好运,希望我给了你一些思考!

答案 1 :(得分:2)

非常不寻常的解决方案,但它应该可以工作(如果不能,我认为用js无法解决问题。)

已编辑:在ajax请求中检查已发送的ID! (因此,请在服务器端检查!)这肯定是唯一的ID,因此,您可以使用此@computercarguy是否正确进行测试。

var ids = [];
var generateId = function(elem)
{
    let r = Math.random().toString(36).substring(7);
    while ($.inArray(r, ids) !== -1)
    {
        r = Math.random().toString(36).substring(7);
    }

    ids.push(r);
    elem.attr("id", r);
};

$(document).ready(function()
{
    $(".wrapper").find(".submit_stamp").each(function()
    {
        generateId($(this));
    });

    console.log(ids);
});

var ajaxHandler = function(stampElem, usedId)
{    
    let testData = new FormData();
    testData.append("time", stampElem.data('timestamp'));
    testData.append("ID", usedId);

    $.ajax({
        url: "/submit_time",
        type: "get",
        data: testData,
        success: function(response)
        {
            $('.wrapper').append(response.html);
            generateId(stampElem);
            if (stampElem.attr("id").length)
            {
                console.log("new id:"+stampElem.attr("id"));
            }
        },
    });

};

$(".wrapper").on("click", ".submit_stamp", function(ev)
{
    ev.preventDefault();
    ev.stopImmediatePropagation();

    if ($(this).attr("id").length)
    {
        let id = $(this).attr("id");
        $("#"+id).one("click",
            $.proxy(
                ajaxHandler, null, $(this), id
            )
        );
        $(this).attr("id", "");
    }
});

答案 2 :(得分:2)

感谢所有回复的人。最终,我能够通过两种不同的解决方案来解决此问题:

1)首先,我能够通过检查后端的IP来阻止违规请求:

@app.route('/submit_time')
def submit_time():
   _ip = flask.request.environ.get('HTTP_X_REAL_IP', flask.request.remote_addr)
   if _ip == '128.177.108.218':
     return flask.jsonify({'route':'UNDEFINED-RESULT'.lower()})
   return flask.jsonify({"html":'<p>Added timestamp</p>'})

以上内容实际上是一种临时破解,因为无法保证目标IP保持不变。

2)但是,我发现在HTTPS上运行也删除了重复的请求。最初,我是从Pythonanywhere仪表板加载应用程序的,结果是http://www.testsite.com。但是,一旦我安装了正确的SSL证书,刷新了页面并再次运行请求,我发现产生了所需的结果。

我向@computercarguy授予了赏金,因为他的帖子促使我思考最初尝试失败的与外部/网络相关的原因。

答案 3 :(得分:-1)

首先,我将使用以下语法作为个人偏好

$('.wrapper').click(function (event) {
    event.stopImmediatePropagation();
    $.ajax({
        url: "/submit_time",
        type: "get",
        data: {
            time: $(this).data('timestamp')
        },
        success: function (response) {
            $('.wrapper').append(response.html);
        },
    });
});

也正如我说的,您需要确保在引用两个并发请求时,它们确实来自同一IP +客户端,否则您可能会混淆来自不同地方的并行请求,从而重复这样的请求

答案 4 :(得分:-2)

您的js文件几乎没有变化。

$('.wrapper').on('click', '.submit_stamp', function(event){
   event.preventDefault();
   event.stopImmediatePropagation();
   $.ajax({
     url: "/submit_time",
     type: "get",
     data: {time: $(this).data('timestamp')},
     success: function(response) {
       $('.wrapper').append(response.html);
     },

   });
});