背景:我们正在编写注册/付款页面,我们的理念是首先在服务器端编写所有验证和错误检查,然后添加客户端验证作为第二步(un -obstructive jQuery)。
我们想要禁用双击服务器端,因此我们编写了一些锁定的,线程安全的代码来处理同时发布的/竞争条件。当我们试图对此进行测试时,我们意识到我们不能同时发生后期或竞赛条件。
我认为(无论如何在旧浏览器中)双击提交按钮的工作原理如下:
- 用户双击提交按钮。
- 浏览器在第一次点击时发送帖子
- 在第二次点击时,浏览器取消/忽略初始帖子,并发起第二篇帖子(在第一篇帖子返回之前有回复)。
- 浏览器等待返回第二个帖子,忽略初始帖子回复。
我认为从服务器端看起来像这样:服务器获得两个同时发布的请求,执行并响应它们(不知道没有人正在听第一个响应)。
根据我们的测试(FireFox 3.0,IE 8.0),实际情况如下:
- 用户双击提交按钮
- 浏览器发送第一次点击的帖子
- 浏览器排队第二次点击,但等待第一次点击的响应。
- 首次点击时返回响应(忽略响应?)。
- 浏览器会发送第二次点击的帖子。
因此,从服务器端:服务器接收它执行并响应的单个帖子。然后,服务器接收它执行的第二个请求并响应。
我的问题是,这总是以这种方式工作(我会失去理智)吗?或者这是现代浏览器中的一项新功能,可防止将同时发布的内容发送到服务器?
似乎对于服务器端双击预防,我们不必担心同时发布的帖子或竞争条件。只需要担心排队的帖子。
提前感谢您的任何反馈/意见。
亚历
答案 0 :(得分:4)
您需要处理的类似情况(javascript disable-submit-button解决方案未涵盖)是用户单击Submit,服务器处理请求,但在处理用户的Internet连接时下来(也许他们坐火车进入隧道)。
当火车离开隧道时,用户不知道他们的交易是否成功 - 他们按下按钮,但页面上没有任何变化(或者他们可能有“再试一次”页面)。他们自然要做的就是再次点击提交(或“再试一次”按钮)。
处理这种情况的最佳方法是在表单中包含唯一的事务ID(在隐藏字段中)。随机生成此id,并在成功处理事务时,将其存储在已完成事务列表的数据库中。
然后当你收到POST时,检查是否已经看到此事务 - 如果已经看到,则直接跳到状态页面。大致是:
BEGIN TRANSACTION
SELECT *
FROM completedTransactions
WHERE userId = ... AND transactionId = ...
<if we got a result - display results of previous transaction>
<otherwise - process the request as normal>
INSERT INTO completedTransactions (userId, transactionId)
VALUES (....)
END TRANSACTION
这样做的好处是(假设您有一个正确支持交易的数据库 - 并且因为您正在处理付款,我希望您这样做!)您不需要进行任何类型的线程或锁定 - 事情“只是工作”
(虽然要小心 - 如果存在并发问题,某些数据库系统可以随意中止您的事务 - 但使用重试循环很容易处理这种(罕见的)情况......)
关于测试来自浏览器的双击:如果您在两次“提交”点击之间按“停止”按钮,会有什么不同吗?
答案 1 :(得分:1)
这可能是一个愚蠢的回应,但是为什么你不要在点击时使用javascript禁用提交按钮,这样你就不必担心多次点击。我通常在我制作的大多数表格上都这样做,似乎解决了这个问题。
你已经说过你正在使用javascript,这不是问题吗?
答案 2 :(得分:0)
只要请求处于连接或发送阶段,在第一次提交期间单击“提交”将取消请求,在没有服务器“知道”的情况下启动新请求。