处理跨所有视图模型的令牌过期事件的策略

本文关键字:令牌 过期 事件 策略 模型 视图 处理 | 更新日期: 2023-09-27 18:13:30

应用程序架构为:MVVM[Views -> ViewModels] -> repository -> API。

API层可以抛出一个TokenExpiredException异常,我们最终希望在UI层处理(通过显示一个消息框并重定向到登录)。

今天,我们的虚拟机与存储库的交互是这样的:

SomeCommand {
    await _repo.DoSomethingAsync();
}

我的问题是从API层找到一个好的模式来处理这个异常。我能想到三种方法:

1)将每个存储库调用包装在一个BaseViewModel方法中,该方法负责捕获和处理这个视图模型无关的异常。

SomeCommand {
    await base.RepoRequest(() => _repo.DoSomethingAsync());
}

其中BaseViewModel有:

RepoRequest(action) {
    try { action() }
    catch (TokenExpiredException) {
        // show message box
        // redirect
    }

任何其他异常,如验证错误,将在VM中处理。我在这里看到的问题是,很容易忘记使用这个模式。我可能会在某个地方直接调用存储库,而错过异常处理。

2)每个VM捕获这个异常

SomeCommand {
    try { await _repo.DoSomethingAsync(); }
    catch (InvalidUsernameException) { ... }
    catch (TokenExpiredException) {
        // show message box
        // redirect
    }

与1)没有什么不同,同样的问题,需要更多的代码复制。

3)使用事件聚合器从API层向BaseViewModel发布消息。

ApiRequest {
    var response = await _httpClient.ExecuteAsync<..>(...);
    if (response.ErrorId == "InvalidUsername")
        throw new InvalidUsernameException();
    else if (response.ErrorId == "TokenExpired")
        EventAggregator.Publish(new TokenExpiredException());
}

BaseViewModel

onMessage(TokenExpiredException e) {
    // show message box
    // redirect
}

这样做的好处是使所有vm(除了基本vm)免于连接。缺点是我不太愿意在API层使用事件聚合器。我们正在使用mvvm-light,这意味着在我们的深层中引用这些库只是为了Messenger(它的事件聚合器)。

有没有人有一个建议,如何干净地实现这个功能?

处理跨所有视图模型的令牌过期事件的策略

我必须找出同样的问题,但我使用WCF与城堡WCF设施,这帮助了我,因为该设施已经有扩展点拦截调用。所以我只是创建了我的自定义AbstractWcfPolicy并拦截了我想要管理的所有异常。

同样的想法,你可以考虑使用一个代理类,基于城堡动态代理,这样你的调用将保持await _repo.DoSomethingAsync();,但在wood下,你的ExceptionInterceptor将拦截所有的异常,并做任何你想做的:

[Serializable]
public class Interceptor : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        Console.WriteLine("Before target call");
        try
        {
           invocation.Proceed();
        }
        catch(Exception)
        {
           Console.WriteLine("Target threw an exception!");
           throw;
        }
        finally
        {
           Console.WriteLine("After target call");
        }
    }
}

然后你可以抛出一些你可能想要抛出的异常,对于一些异常或所有异常,你也可以实现一个发布-订阅(一个全局消息代理,MVVM Light中的信使)机制,在那里你可以推送异常,然后在你的应用程序的任何一点你可以订阅这些错误并做一些事情(日志,以一种不引人注目的方式向用户显示错误,等等)。