【问题标题】:Blazor click button only once [closed]Blazor 单击按钮仅一次[关闭]
【发布时间】:2021-09-18 17:38:01
【问题描述】:

我正在开发一个 Blazor WebAssembly 项目。客户可以通过点击“Create Order”按钮来创建订单。

问题是当用户多次点击按钮时,系统会创建多个具有相同数据的订单(相同的产品,相同的订单号, 等)。

那么,防止用户创建多个订单的最佳做法是什么?

【问题讨论】:

标签: c# .net asp.net-core blazor blazor-webassembly


【解决方案1】:

一种简单的方法是在处理订单后禁用该按钮:

Index.razor:

@page "/"

<button 
  @onclick="@this.ProcessOrder" 
  disabled="@this.OrderIsBeingProcessed">

  Click me!
</button>

代码隐藏,Index.razor.cs:

public partial class Index
{
  private bool OrderIsBeingProcessed = false;
  
  private async Task ProcessOrder()
  {
    this.OrderIsBeingProcessed = true;
    
    // TODO: process order
    await Task.Delay(3000);
    
    this.OrderIsBeingProcessed = false;
  }
}

请注意,这只是一个(简单)演示如何禁用按钮,而不是防止数据库中出现重复条目​​的万无一失的方法。

【讨论】:

  • 如果连接失败或其他类型的错误,你怎么知道订单是否真的没有保存?如果重试,您也可以创建一个副本。
  • @Kubus 是供 OP 防范的,这只是一个如何禁用按钮的快速演示。我更新了我的答案以使其清楚。
  • 谢谢!我添加了一个示例,希望是一个容错示例。这样就不应该有重复,客户也不会被多次计费。
【解决方案2】:

那么,防止用户创建多个订单的最佳做法是什么?

最佳做法是在此处重新设计您的数据流。创建订单后,您应该移至另一个页面或清除订单输入表单。

当您清除表单时,下一次单击应该会失败,因为缺少必填字段。

【讨论】:

    【解决方案3】:

    我想你正在使用一个 Http Post 并且它不是幂等的。在这些情况下,唯一的客户 ID 应该可以帮助您。可以在服务器端(在您的数据存储中)检查此 order.ClientOrderId。如果它已经被存储,它就是一个副本。

    您也可以在收到来自服务器的响应时禁用该按钮。

    唯一索引将确保在网络故障等情况下或重试期间不会保存重复的订单发布。

    POCO:

      [Index(nameof(ClientOrderId), IsUnique = true)]
      public record Order
      {
        public int Id { get; init; }
    
        public string ClientOrderId { get; init; }
      }
    

    Blazor 组件:

    <button class="btn btn-info" disabled="@IsSaving" @onclick="@(async () => await SaveAsync())">Save</button>
    
        using System.Net.Http;
    
        public bool IsSaving { get; set; }
    
        [Inject]
        private IHttpClientFactory httpClientFactory { get; init; }
    
        private async Task SaveAsync()
        {
          IsSaving = true;
    
          var order = new Order
          {
            ClientOrderId = Guid.NewGuid().ToString()
          };
    
          try
          {
    
            using var httpClient = httpClientFactory.CreateClient();
    
            await httpClient.PostAsJsonAsync("Orders", order).ConfigureAwait(false);
          }
          catch (Exception exception)
          {
            //add retry logic
          }
          finally
          {
            IsSaving = false;
          }
        }
    

    Web API 控制器 Post 方法

        [HttpPost]
        public async Task<IActionResult> Post(Order order)
        {
          var existingOrder = repository.Entities.FirstOrDefaultAsync(c => c.ClientOrderId == order.ClientOrderId);
    
          if(existingOrder)
            return CreatedAtAction(actionName, new { id = existingOrder.Id }, existingOrder);
    
          repository.Add(order);
    
          await repository.SaveChangesAsync();
    
          var actionName = nameof(Post);
    
          return CreatedAtAction(actionName, new { id = order.Id }, order);
        }
    

    【讨论】:

    • 这种方法也可以应用在 WinUI、WPF 等中,不仅在 Blazor (WASM) 中
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-18
    • 2014-01-26
    • 1970-01-01
    • 2016-10-12
    相关资源
    最近更新 更多