【问题标题】:Handle errors in a Resolve service处理 Resolve 服务中的错误
【发布时间】:2017-03-22 04:56:03
【问题描述】:

假设我有一个ValuesComponent,它在 HTML 表格中显示Value 的数组。

// in value.ts
export class Value { ... }
// in values.component.ts
@Component(...)
export class ValuesComponent { ... }

作为一个优秀的程序员,我创建了一个不同的类来负责提供值。我们称之为ValuesService

// in values.service.ts
@Injectable()
export class ValuesService
{
    public getValues(): Observable<Value[]>
    {
       ...
    }
}

假设服务从 Web 服务获取其值:/api/values

现在我不想将服务直接注入到我的组件中,而是让 Angular 路由器在导航到组件之前预取值。

为此,我创建了一个Resolve 服务类并将其插入路由器模块。

// in values-resolver.service.ts
export class ValuesResolverService implements Resolve<Value[]> {
    constructor(private backend: ValuesService) {
    }

    public resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Value[]> {
        return this.backend.getValues();
    }
}
// In app.module.ts (in @NgModule())
imports: [
  RouterModule.forRoot([{
    path: 'values',
    component: ValuesComponent,
    resolve: { values: ValuesResolverService }
  }])
]
// In values.component.ts
public values: Value[];

constructor(private route: ActivatedRoute) {
}

ngOnInit() {
    this.values = route.data.values as Value[];
}

到目前为止我做得怎么样?好的?那么当ValuesService.getValues() 失败时,我应该在哪里进行错误处理? (连接错误,内部服务器错误,...)

当用户尝试导航到/#/values 路由并发生错误时,我想将错误记录到控制台并停止导航。理想情况下,我想将用户重定向到 /#/error 路由(示例代码中未显示)。

【问题讨论】:

  • 这个问题没有提到错误应该发生什么。
  • 一个简单的console.log 就可以了。我只需要知道将它放在哪里以及返回什么(如果有的话)。
  • 那么问题是关于可观察对象,而不是解析器。这是return this.backend.getValues().catch(...)
  • 显然,您可能已经想到了这一点,但您可能需要从解析器返回 PromiseObservable 或其他一些单子抽象,而不是值数组本身。这样,您可以在解析器中附加错误处理,并且通过将路由器也注入其中,您可以在.catch 中触发导航。实际上,看起来您正在返回一个 Observable&lt;Value[]&gt;,因此您可以在对 ValuesService 的调用中附加错误处理,然后在 catch 块中重定向。我认为这会奏效。
  • @AluanHaddad 我没想到。您应该将您的评论转换为答案。

标签: angular typescript rxjs


【解决方案1】:

在这种情况下,服务应该处理错误,并重定向到想要的路由。然后,catch 方法应该返回一个空的 Observable(供 resolve 守卫解析),同时在 resolve 期间处理空的情况。

类似的东西

export class ValuesService {
    constructor(private http:Http, private router:Router) {}

    getValue() {
       this.http.get('/some/url').map((response:Response) => {
           // Map your response to a model here
       }).catch((response:Response) => {
           // Handler case for different status
           this.router.navigate(['/error']);
           return Observable.empty();
       })
    }
}

而守卫是这样的

export class ValueResolveGuard implements Resolve<Value[]> {

    constructor(private valueService:ValueService) {}

    resolve() { // You don't need the route & state if you're not gonna use them
        this.valueService.getValue().toPromise().then((values:Values[]) => {
            if (!values) { // For the Observable.empty() case
                // return some default stuff
            }
            return values;
        })
    }
}

或者,如果您想专门返回一个 observable(在这种情况下不应该有任何区别,因为 resolve 守卫同时接受 observable 和 promise),您可以这样做。

 this.valueService.getValue().first().catch({} => { 
      // return some default value.
 });

注意 first() 是必需的,因此返回的 observable 将是一个终止的 observable,否则解析守卫将无法解析。需要 catch 是因为对空的 observable 调用 first() 会引发错误。

【讨论】:

  • 我不同意让服务处理一般的导航,尤其是导航是失败的 getter 的副作用。我更喜欢我的吸气剂没有副作用。
  • 我认为这一切都归结为服务的使用方式。服务应该是一个通用功能点。如果预计发生错误时,应该始终更新路线,那么我认为这是正确的地方。如果服务从不同的执行点被多次调用,并且需要相同的行为,那么不在服务中放置相同的路由会导致您的代码不是 DRY。您应该问一个问题,这项服务的目的应该是什么?这将帮助您更好地识别所需的功能。
  • 您可以提取catch 函数并将其放在单独的文件中。然后很容易在任何地方重用:.getValues().catch(navigateToErrorPage)。这对我的口味来说已经够干了。
  • 尽管我不想承认,这似乎是唯一的方法,所以你可以做的是创建一个通用的记录器类,它有一个可观察的属性,在错误的 UI 组件中观察到某处并在 catch 块内调用记录器......我个人仍然使用 Javascript 来注入 UI 消息,更简单、更干净、更健壮。
  • hmmm...resolve() 不需要返回一些东西吗?该返回语句是异步的。
猜你喜欢
  • 2022-11-05
  • 2016-08-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-11
  • 2011-05-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多