在作为我的Flask应用程序的一部分运行Ajax请求时,我遇到了令人困惑的行为。我编写了一个处理程序,接收div单击,然后将包含特定数据的Ajax请求发送到app.py中指定的特定路由。然后将数据插入到数据库中。虽然这种方法在我自己的机器上运行我的Flask应用程序时运行得很好,但是当我将我的应用程序移动到另一个托管服务(毕多芬)时,每次单击div,请求都会被发送两次,数据被两次插入数据库就是证明。
以前也问过类似的问题变体(例如这里和这里 ),但是这些问题都是处理POST请求的,而我的问题是使用GET。此外,这些问题通常涉及与POST请求一起提交的HTML请求,因此产生了附加请求。但是,我的代码没有任何形式。
我的代码示例(简化,但在本质上与我当前的工作相同):
在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中
@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元素时,Ajax请求就会触发两次,时间戳会被两次插入到我的数据库中,"Added timestamp"会被两次附加到.wrapper中。为了解决这个问题,我做了一些事情,包括:
event.stopPropagation()true,并在.ajax的success处理程序中将其重置为false。我在一个条件语句中用这个布尔值包装了$.ajax。这些补丁都不起作用。然而,让我困惑的是,为什么$.ajax在我的机器上运行时只被调用一次,而在主机上运行时却被调用了两次。这和缓存有关吗?我怎样才能解决这个问题?非常感谢!
编辑:
奇怪的是,重复的请求很少发生。有时,只发出一个请求,而另一些时候,请求被重复。但是,我已经在Chrome中检查了Network输出,它只显示单个请求头。
访问日志输出(删除IP):
<IP1> - - [05/Aug/2019:16:35:03 +0000] "GET /submit_time?payload=.... HTTP/1.1" 200 76 "-" "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.1) Gecko/2008070208 Firefox/3.0.1" "<IP>" response-time=0.217
<IP2> - - [05/Aug/2019:16:35:05 +0000] "GET /submit_time?payload=.... HTTP/1.1" 200 71 "http://www.katchup.work/create" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36" "<IP2>" response-time=0.198发布于 2019-08-09 14:10:24
谢谢所有回复的人。最终,我能够用两个不同的解决方案来解决这个问题:
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,因为他的帖子促使我考虑了我最初尝试失败的外部/网络相关原因。
发布于 2019-08-05 17:35:56
根据您的最新更新,我不得不说这不是一个重复的请求。您的日志显示,一个请求来自基于Windows的计算机上的Mozilla,而另一个请求来自Mac上的Chrome,这仅仅是来自两个不同位置的两个不同的请求,它们在时间上非常接近。即使它是来自虚拟机的测试,它也不应该记录多个OSs或浏览器,因为VM会处理所有的翻译,防止出现这样的混乱。
您不包括IP地址,但是如果它们是公共地址(如127.x.x.x、10.x.x.x或192.x.x.x以外的其他地址),那么它们肯定是两个同时使用您的软件的不同用户。
如果您跟踪的是相同的用户,可能只是他们使用您的软件在两个不同的设备(如桌面和移动电话)。如果这是不允许的,那么确保他们的访问反映了这一点。如果它可以通过DNS跟踪到不同的地理位置,您可能会有一个受损的帐户锁定,直到真正的用户能够确认他们的身份。
不管你如何分割它,用新的数据,我不认为它实际上是你的软件,除非你可以通过测试甚至可靠地再现它。花点时间考虑一下,它可能不是一个bug,而是一些其他的东西。软件开发人员习惯于认为每件事都是错误和错误,而这可能是一种善意的或恶意的攻击,而这可能是以前没有考虑过的。
祝你好运,希望我能给你一些考虑的东西!
发布于 2019-08-07 03:39:23
非常不寻常的解决方案,但它应该有效(如果不是,我认为这个问题不能用js解决)。
编辑的:检查ajax请求中发送的ID!(所以请检查服务器端!)这肯定是一个独特的标识,所以,您可以用这个@computercarguy car家伙是否正确进行测试。
let ids = []
function generateId(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)
});
function ajaxHandler(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", "")
}
});https://stackoverflow.com/questions/57334868
复制相似问题