首页 > 解决方案 > SPA - 基于服务器端验证的操作确认

问题描述

我正在寻找一种可重用的解决方案来解决用户执行的某些操作在完成之前可能需要用户进一步确认的问题。

我正在使用 React 前端和 WebAPI 后端开发一个应用程序。

假设用户正在发起资金转账,并假设在发起转账之前我们无法检查他/她的账户客户端中的可用资金。当用户提交请求时(通过 HTTP POST),如果用户的帐户有足够的资金,则请求应该正常完成,而如果资金不足并且用户在透支限制内,则应提示用户使用透支,并且仅当用户确认使用透支请求应该完成。

另一种情况是,假设用户想要删除一个收款人帐户(通过 HTTP DELETE)。当收款人帐户没有定期付款设置时,删除应该正常完成。当存在定期付款设置时,应进一步提示用户(检查定期帐户只能在服务器端完成,并且仅在需要时才会完成(因此提前这样做并且不能向客户端提供数据)。

我确信这是任何应用程序中的常见情况,但无法在互联网上找到解决此问题的合理解决方案。

提前致谢

标签: restarchitecturesingle-page-applicationsoftware-designwebapi

解决方案


这种情况并不常见 - 你对它提出了一个奇怪的要求。在理想的设计中 - 您会要求客户打开透支功能,这可能是对api/settings端点的单独 API 调用。您将其与常规请求有效负载相结合,这会使事情变得稍微复杂一些。

您只需要一个足够详细的响应合同和一个处理这些案例的前端,仅此而已。

我在下面嘲笑了一个。

以下是您转移资金案例的说明。您可以修改其中一些响应合同,使其更具描述性,以便您可以处理更多案例。

  1. 发起从客户端(react-app)到服务器的传输请求。通行证转让详情。请注意,您将其标记为DirectTry.
  2. DirectTry由于账户资金问题而失败/但您确认可以透支。我们将 guid 和交易信息、客户信息添加到字典中,以防万一客户想要验证透支。您可能也应该在这里到期。
  3. 向 react-app 返回一个提示状态。您可以通过从服务器传递到客户端的消息We need you to approve going into overdraft for this transaction/后续状态是什么等来扩展它......我们还传输一个 guid,只是为了确保它实际上是同一个设备,相同的事务请求将启用透支。
  4. 在 react-app 端,你会看到提示状态,启动一个通知组件说点击这里批准你的透支。
  5. 您单击批准,相同的 guid 以不同的属性包传递到服务器端,表明这现在也允许透支。
  6. 交易通过。

public enum State
{
   // everything worked
   Done,

   // error - no retry needed.
   CriticalError,

   // additional prompt
   Prompt,
}

public class ServiceResult
{
   // some state.
   public State State { get; set; }

   ... some other props...
}


/// Part of some api controller
[Route("api/funds")]
[HttpPost]
public async Task<ServiceResult> TransferFunds(FundTransferPayload payload)
{
   // ... do some ops.
   ValidationResult validationResult; = new ValidationResult();
   if (payload.OperationType == OperationType.DirectTry)
   {
       validationResult = new ValidationResult();
   }
   // pass the guid we returned to front-end back to service to make sure
   else if (payload.OperationType == OperationType.PromptProvided)
   {
      validationResult = ValidateOp(
           ValidationDictionary[payload.promptGuid],
           payload.customerId,
           payload.operationType,
           payload.operationId);
   }

   var transferResult = await SomeService.SomeOps(validationResult);
   if (transferResult.Succeeded) {...}
   else if (transferResult.NeedsOverdraft)
   {
      var someGuid = new Guid();
      ValidationDictionary.Add(new Key(someGuid), new Value(payload.customerId, 'overdraft', transferResult.operationId));
      return new ServiceResult() { State=Prompt, ...otherprops... };
   }
   else if (transferResult.Failed) {...}
}

在你的前端是这样的......


axios.post('/api/funds', {
    operationType: 'DirectTry',
    ...some other props...
  })
  .then(function (response) {
    if (response.data.state === 'Prompt') {
      // save the guid or whatever token you're using - you'll call your API again with that.
      // change the UX with the prompt. Upon prompt, fire the request appropriately.
    }
  });


推荐阅读