避免ASP.NET视图中的重复请求的最佳实践

3
我将翻译成如下内容:

我有以下控制器:

[HttpPost]
public ActionResult SomeMethod(string foo, obj bar)
{
  //Some Logic 
}

现在假设从按钮或Ajax(带有一些编辑)调用Action的视图中,我不想接收重复请求。

服务器端最好的处理方法是什么?


更新

您首先需要定义符合双重请求标准的时间间隔-Jonesopolis

假设在这种情况下,双重请求是指第一次和第二次调用之间的时间差小于1秒


1
你首先需要定义符合双重请求标准的时间间隔。 - Jonesopolis
1
你可以将用户、IP地址和用户代理组合的请求时间存储在会话、缓存或其他地方。然后,检查缓存,如果不到1秒钟就拒绝请求(或者在这种情况下执行所需操作)。更有趣的是,是什么促使你在服务器端处理这个问题... - Evk
1
尽管您将“服务器端”放在了那里,但通常在UI上处理要容易得多,例如防抖请求或禁用按钮。虽然不是万无一失的,但这取决于情况,并且通常适用于内部网络应用程序。 - freedomn-m
2
如果你想防止恶意用户垃圾邮件攻击你的服务 - 那就完全是另一回事了。 - Evk
显示剩余14条评论
3个回答

7
坦白地说,你无法完全防止重复提交,至少不是百分之百有效。有一些服务器端的方法可以使用,但没有一个是绝对可靠的。一般的思路是需要识别POST请求。最常见的方法是设置一个带有GUID或类似标识的隐藏输入框。然后当请求到来时,你需要在某个地方记录该GUID,这可以是在会话中、数据库中等等。在处理请求之前,你需要检查你正在使用的任何数据存储中是否存在该GUID。如果存在,则为重复的POST请求,否则可以继续处理。
然而,在此需要强调的是,你必须将其记录在某个地方,而这需要一定的时间。虽然可能只是几毫秒,但这足以让重复请求进入,特别是如果用户双击提交按钮,这通常是双重提交的原因。
Web服务器只是按照请求的顺序响应请求,并且重要的是,它具有多个线程和甚至同时服务请求的多个进程。HTTP是一种无状态协议,所以服务器并不关心客户端是否在之前发出了相同的请求,因为它实际上并不知道客户端之前是否发出了相同的请求。如果两个重复的请求在两个不同的线程上被同时服务,则它们之间就会产生竞争,以查看是否可以在另一个检查是否为重复请求之前设置某些标识来标识它们是重复的。换句话说,大多数情况下,无论你在服务器端尝试什么来阻止重复提交,你都将没有运气,并且两个请求都将通过。
唯一可靠的方法是使用JavaScript在提交时禁用提交按钮。然后,即使用户双击,他们也只能单击一次。当然,如果用户禁用了JavaScript,这对你也没有帮助,但这种情况越来越少见。

正如其他答案建议的那样,我们可以从客户端发送唯一键并在服务器端进行检查,如果我们有基于唯一键或主键的话,它也不会从数据库端插入。 - Damith
那假设服务器实际上正在将某些内容写入数据库,但并非总是如此。如果您要写入数据库,则可以有一个唯一的列,其中存储标识POST请求的GUID或其他内容,然后再次尝试写入相同的GUID将失败。当然,在那一点上,您需要捕获数据库错误并优雅地恢复。但是,这只是乐观并发性,并且有更好的处理方式,例如使用rowversion列。 - Chris Pratt

4
注意。这种方法需要小心。在服务器端控制这个问题会增加大量的复杂性。
首先,你需要识别出多个请求来自同一用户的情况。
我认为在服务器端控制这个问题不是最好的方法。
但是,如果你真的想要那样做...看看这个链接:https://dev59.com/u3VC5IYBdhLWcg3wqzHV#218919 在这个链接中,建议维护一个令牌列表。但是,在你的情况下,只需检查是否收到了相同的令牌超过一次即可。

这是正确的答案。在服务器端管理它很愚蠢。 - Liam
1
Post/Redirect/Get模式非常常见,应该被采用。 - Luke
1
当您使用Token时,您是从服务器端进行操作的。@Liam - Sid
@Luke... 可以使用Post/Redirect/Get模式!但我认为为每个表单生成管理令牌的复杂性最有用,可以防止CSRF攻击。 - Gean Ribeiro
你不能在Post/Redirect/Get模式中使用标准的ValidateAntiForgeryToken吗? - Luke
@Luke... 可能的。我的问题是,需要评估何时复杂性得到回报。 - Gean Ribeiro

0

您至少需要实现双击事件监听器。

  1. UseSubmitBehiviar="false"
  2. OnClientClick="this.disable='true'; this.value="请稍候";"
  3. 检查 ASP.NET 生命周期
  4. 检查请求/重定向

enter image description here

  1. 添加测试代码以查看谁负责

    if (IsPostBack) {

    _CtrlName = thisPage.Request.Params.Get("__EVENTTARGET");
    
    if (_CtrlName != null && _CtrlName == myButton.ID)
    {
    
        //Do your thing
    }
    

    }

  2. 在页面加载时检查 IsPostBack 并正确使用它以防止重复请求。

    protected void Page_Load(object sender, EventArgs e) {

            if (!IsPostBack)
            {
    
    
            }
        } 
    

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接