处理跨所有视图模型的令牌过期事件的策略
本文关键字:令牌 过期 事件 策略 模型 视图 处理 | 更新日期: 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中的信使)机制,在那里你可以推送异常,然后在你的应用程序的任何一点你可以订阅这些错误并做一些事情(日志,以一种不引人注目的方式向用户显示错误,等等)。