大火,这是辛格尔顿的滥用吗? (共享iframe值)

发布于 2025-02-02 02:55:22 字数 4319 浏览 3 评论 0原文

首先,让我解释为什么我认为我需要一个单身人士。我将几个托管的结帐支付处理器集成到我的Blazor Server应用程序中。他们所有人的工作如下;

  • index.razor具有显示付款处理器URL的iFrame。
  • 当客户完成付款时,iframe重定向到我的应用程序指定的URL,paymentcomplete.razor
    • paymentcomplete.razor使用 scoped服务 hostedCheckoutservice将事件提高到index.razor包含付款响应。

这是问题出现的地方paymentcomplete.razor托管在 iframe 中,因此用单独的范围处理。 hostedCheckoutservicepaymentcomplete.razor内部提出的任何属性更改或事件, be index.razor。这使得(几乎?)无法从iframe内部合并到index.razor的范围。

显然解决此问题的是将hostedcheckoutservice注册为 singleton 。现在的问题是,当一个客户从paymentscomplete.razor中提出事件时,所有所有客户都必须处理它。

为了解决这个问题,我创建了一个indexBase,其中一个名为eventTargetID的唯一属性。当iFrame付款完成后,返回到peayscomplete.razor将在查询字符串中包含eventTargetID

indexBase.cs

<iframe style="width:100%;height:50vh" src="@HostedCheckoutFrameSrc " frameborder="0" ></iframe> 


public class IndexBase : ComponentBase, IDisposable
{
    [Inject] NavigationManager NavigationManager { get; set; }
    [Inject] HostedCheckoutService HostedCheckoutService { get; set; }
    [Inject] PaymentApi PaymentApi { get; set; }

    public string HostedCheckoutFrameSrc { get; set; }
    public string EventTargetId { get; set; } = Guid.NewGuid().ToString();

    protected override void OnInitialized()
    {
        HostedCheckoutService.OnPaymentComplete += PaymentComplete;
    }

    public void Dispose()
    {
        HostedCheckoutService.OnPaymentComplete -= PaymentComplete;
    }

    private void PaymentComplete(string eventTargetId, string paymentJson)
    {
        // Hosted checkout iframe has returned a successfull payment.
        // Do something, send order, notification, ect.
    }

    public async Task InitializePayment()
    {
        string returnUrl = NavigationManager.BaseUri + $"/PaymentComplete?eventTargetId={EventTargetId}";
        InitializePaymentResponse response = await PaymentApi.CreatePaymentRequest(returnUrl);
        // Set iframe src property to third party payment providers url.
        // When customer completes third party payment url, the iframe redirects to PaymentComplete.razor (returnUrl).
        HostedCheckoutFrameSrc = PaymentApi.baseUrl + response.PaymentId;
    }
}

paymentcomplete.razor(从第三方URL重定向,托管在iframe内部)

此页面将抓住eventtargetId从查询字符串中升级并升高我们的Singleton服务的活动。

[Inject] NavigationManager NavigationManager { get; set; }
[Inject] PostFormService PostFormService { get; set; }
[Inject] HostedCheckoutService HostedCheckoutService { get; set; }

protected override async Task OnInitializedAsync()
{
    // We face double render problem, but Form values will be null on secord render anyways.
    if (PostFormService.Form != null)
    {
        NavigationManager.TryGetQueryString<string>("eventTargetId", out string eventTargetId);
        string paymentJson = PostFormService.Form?["PaymentResponse"];
        HostedCheckoutService.PaymentCompleted(eventTargetId, paymentJson);
    }
}

在我的singleton hostedcheckoutservice中,我使用eventtargetID过滤所有订户。

public class HostedCheckoutService 
{
    public event Action<string, string> OnPaymentComplete;

    public void PaymentCompleted(string eventTargetId, string paymentJson)
    {
        // Instead of raising the event for every instance attached to this action
        // only raise the event for the specified target.
        var instance = OnPaymentComplete.GetInvocationList()
                            .Where(d => d.Target is IndexBase && ((IndexBase)d.Target).EventTargetId == eventTargetId)
                            .FirstOrDefault();
        instance?.DynamicInvoke(eventTargetId, paymentJson);
    }
}

最后,问题!这似乎是对Singleton事件的不可接受的使用,还是有人使用更好的方法?即使每个客户都无法处理事件,呼叫getInvocationList()仍然包含一个列表每个订阅的班级。

注意:每个事件订户实际上并不是完整的indexBase类。这将是一个简单的付款组件(我简化了此示例)。

First of all, let me explain why I think I need a singleton. I'm integrating several Hosted Checkout payment processors into my Blazor Server application. All of them work as follows;

  • Index.razor has an Iframe that displays a payment processors url.
  • When the customer completes the payment the iframe redirects back to a url specified by my application, PaymentComplete.razor.
    • PaymentComplete.razor uses a scoped service HostedCheckoutService to raise an event to Index.razor containing the payment response.

This is where the problem comes in. PaymentComplete.razor is hosted inside an iframe therefore is treated with a separate scope. Any property changes or events raised by HostedCheckoutService from within PaymentComplete.razor wont be Index.razor. This makes it (nearly?) impossible to merge information from within the iframe to the scope of Index.razor.

What obviously solves this issue is registering HostedCheckoutService as a singleton. Now the problem is that when one client raises an event from PaymentComplete.razor, all clients will have to handle it.

To solve this I created an IndexBase with a unique property named EventTargetId. When the iframe payment is completed, the return url to PaymentComplete.razor will contain the EventTargetId in the query string.

IndexBase.cs

<iframe style="width:100%;height:50vh" src="@HostedCheckoutFrameSrc " frameborder="0" ></iframe> 


public class IndexBase : ComponentBase, IDisposable
{
    [Inject] NavigationManager NavigationManager { get; set; }
    [Inject] HostedCheckoutService HostedCheckoutService { get; set; }
    [Inject] PaymentApi PaymentApi { get; set; }

    public string HostedCheckoutFrameSrc { get; set; }
    public string EventTargetId { get; set; } = Guid.NewGuid().ToString();

    protected override void OnInitialized()
    {
        HostedCheckoutService.OnPaymentComplete += PaymentComplete;
    }

    public void Dispose()
    {
        HostedCheckoutService.OnPaymentComplete -= PaymentComplete;
    }

    private void PaymentComplete(string eventTargetId, string paymentJson)
    {
        // Hosted checkout iframe has returned a successfull payment.
        // Do something, send order, notification, ect.
    }

    public async Task InitializePayment()
    {
        string returnUrl = NavigationManager.BaseUri + 
quot;/PaymentComplete?eventTargetId={EventTargetId}";
        InitializePaymentResponse response = await PaymentApi.CreatePaymentRequest(returnUrl);
        // Set iframe src property to third party payment providers url.
        // When customer completes third party payment url, the iframe redirects to PaymentComplete.razor (returnUrl).
        HostedCheckoutFrameSrc = PaymentApi.baseUrl + response.PaymentId;
    }
}

PaymentComplete.razor (redirected from third party url, hosted inside iframe)

This page will grab the EventTargetId from the query string and raise an event on our singleton service.

[Inject] NavigationManager NavigationManager { get; set; }
[Inject] PostFormService PostFormService { get; set; }
[Inject] HostedCheckoutService HostedCheckoutService { get; set; }

protected override async Task OnInitializedAsync()
{
    // We face double render problem, but Form values will be null on secord render anyways.
    if (PostFormService.Form != null)
    {
        NavigationManager.TryGetQueryString<string>("eventTargetId", out string eventTargetId);
        string paymentJson = PostFormService.Form?["PaymentResponse"];
        HostedCheckoutService.PaymentCompleted(eventTargetId, paymentJson);
    }
}

In my singleton HostedCheckoutService, I filter out any subscribers using the EventTargetId.

public class HostedCheckoutService 
{
    public event Action<string, string> OnPaymentComplete;

    public void PaymentCompleted(string eventTargetId, string paymentJson)
    {
        // Instead of raising the event for every instance attached to this action
        // only raise the event for the specified target.
        var instance = OnPaymentComplete.GetInvocationList()
                            .Where(d => d.Target is IndexBase && ((IndexBase)d.Target).EventTargetId == eventTargetId)
                            .FirstOrDefault();
        instance?.DynamicInvoke(eventTargetId, paymentJson);
    }
}

Finally, the question! Does this seem like an unacceptable use of singleton events or does anyone have a better approach? Even though every client wont be handling the event, a call to GetInvocationList() would still contain a list of every subscribed class.

Note: Each event subscriber would not actually be a full IndexBase class. It would be a simple payment component (I simplified this example).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

北方的巷 2025-02-09 02:55:22

我主要关注的是扩展事件中的所有注册方法。

由于我不知道还有什么hostedcheckoutservice做了什么,让Singleton peays paymentstransactionservice包含一个简单的针对操作的GUID集合 - 并且可能需要注册时间以操作超时系统。索引调用寄存器 payment> paymentstransactionservice注册它的GUID及其动作。 - 显然,当处置时,reregister方法。 paymentcomplete调用transactionComplete payment> Payment Transactionservice的方法。它检查其列表并执行注册操作,如果没有一个列表 - 并记录一个错误。您可以使用每个paymentcomplete调用还可以启动一个管理例程,以检查超时并删除过期的注册。

My main concern would be scaling on calling all the registered methods on the event.

As I don't know what else HostedCheckoutService does, what about having a singleton PaymentTransactionService that contains a simple collection of Guids against Actions - and probably registration times to operate a timeout system . Index calls a Register method on PaymentTransactionService to register it's Guid and it's Action. - and obviously a ReRegister method when it Disposes. PaymentComplete calls a TransactionComplete method on PaymentTransactionService. It checks it's list and executes the registered action if there is one - and logs an error if there isn't. You can use each PaymentComplete call to also kick off a managment routine that checks timeouts and removed expired registrations.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文