如何处理解析器中的错误

Mat*_*t M 36 rxjs angular

我正在尝试使用解析器来制作更好的用户体验.在快乐的道路上,一切都很美好.我似乎无法弄清楚的是如何处理异常.我的解析器调用一个服务,该服务命中webapi项目.一个例子:

FooResolver:

resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Foo> {
      return this.fooService.getById(route.params['id']).catch(err => {
    ****not sure what to do/return in the case of a server error****
    return Observable.throw(err);
  });
} 
Run Code Online (Sandbox Code Playgroud)

FooService接口:

  public getById(id: string): Observable<Foo> {
    return this.http.get(`${ this.apiUrl }/${ id }`)
        .map(this.extractData)
        .catch(this.handleError);
}
Run Code Online (Sandbox Code Playgroud)

handleError函数:

   protected handleError (error: Response | any) {
    // Todo: Log the error   
    // Errors will be handled uniquely by the component that triggered them
    return Observable.throw(error);
}
Run Code Online (Sandbox Code Playgroud)

在FooComponent内部,我执行此操作(如果从服务/解析程序返回错误,则永远不会命中此内容):

FooComponent:

ngOnInit(): void {
    this.foo= this.route.snapshot.data['foo'];
    if (this.foo) {
       this.createForm(this.foo);
    }
}
Run Code Online (Sandbox Code Playgroud)

我试过抛出错误(如图所示) - 我在控制台中得到了这个异常:

未捕获(承诺):状态响应:URL内部服务器错误500:

并返回new Observable<Foo>(),这给出:

无法读取undefined的属性'subscribe'

我有一些解析器,所有这些都可以在服务器上遇到异常,但我不知道在发生这些异常时该怎么办.

Deb*_*ahK 46

以下是我的一个带有错误处理功能的解析器的示例,使用了Gunter建议的技术:

import { Injectable } from '@angular/core';
import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router';

import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
import 'rxjs/add/observable/of';

import { IProduct } from './product';
import { ProductService } from './product.service';

@Injectable()
export class ProductResolver implements Resolve<IProduct> {

    constructor(private productService: ProductService,
                private router: Router) { }

    resolve(route: ActivatedRouteSnapshot,
            state: RouterStateSnapshot): Observable<IProduct> {
        let id = route.params['id'];
        if (isNaN(+id)) {
            console.log(`Product id was not a number: ${id}`);
            this.router.navigate(['/products']);
            return Observable.of(null);
        }
        return this.productService.getProduct(+id)
            .map(product => {
                if (product) {
                    return product;
                }
                console.log(`Product was not found: ${id}`);
                this.router.navigate(['/products']);
                return null;
            })
            .catch(error => {
                console.log(`Retrieval error: ${error}`);
                this.router.navigate(['/products']);
                return Observable.of(null);
            });
    }
}
Run Code Online (Sandbox Code Playgroud)

你可以在这里找到完整的例子:https://github.com/DeborahK/Angular-Routing in APM-final文件夹.

更新2019年2月

这是解析器中错误处理的更好答案:

1)使用可选的错误属性将您的界面包装在另一个界面中:

/* Defines the product entity */
export interface Product {
  id: number;
  productName: string;
  productCode: string;
  category: string;
  tags?: string[];
  releaseDate: string;
  price: number;
  description: string;
  starRating: number;
  imageUrl: string;
}

export interface ProductResolved {
  product: Product;
  error?: any;
}
Run Code Online (Sandbox Code Playgroud)

2)解析到该接口:

import { Injectable } from '@angular/core';
import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';

import { Observable, of } from 'rxjs';
import { map, catchError } from 'rxjs/operators';

import { ProductResolved } from './product';
import { ProductService } from './product.service';

@Injectable({
  providedIn: 'root'
})
export class ProductResolver implements Resolve<ProductResolved> {

  constructor(private productService: ProductService) { }

  resolve(route: ActivatedRouteSnapshot,
          state: RouterStateSnapshot): Observable<ProductResolved> {
    const id = route.paramMap.get('id');
    if (isNaN(+id)) {
      const message = `Product id was not a number: ${id}`;
      console.error(message);
      return of({ product: null, error: message });
    }

    return this.productService.getProduct(+id)
      .pipe(
        map(product => ({ product: product })),
        catchError(error => {
          const message = `Retrieval error: ${error}`;
          console.error(message);
          return of({ product: null, error: message });
        })
      );
  }

}
Run Code Online (Sandbox Code Playgroud)

3)在组件中,拉下您需要的接口:

  ngOnInit(): void {
    const resolvedData: ProductResolved =
      this.route.snapshot.data['resolvedData'];
    this.errorMessage = resolvedData.error;
    this.product = resolvedData.product;
  }
Run Code Online (Sandbox Code Playgroud)

  • 即使出现错误,也要始终解决某些问题,这与解决问题的目的相矛盾.通过使用resolve,我说负责此路由的组件需要此信息,如果由于某种原因,信息不可用,则不应该呈现.解析守卫内部的Promise(Observable)拒绝执行此操作并阻止渲染路由的组件,但应该有一个统一的机制来捕获解决错误并使用某些组件处理它们以进行错误处理. (8认同)
  • 更好地做`Observable.throw(theError);`而不是null (3认同)

Gün*_*uer 13

您需要返回一个完成的observable false

handleError() {
  return Observable.of([false]);
}
Run Code Online (Sandbox Code Playgroud)


RTY*_*TYX 7

我只想提供一个非常相似的更新答案,但有一些更新的代码。我个人认为错误不应该在解析器内部进行管理。

为什么?好吧,我认为解析器的工作是解决,而不是弄清楚如果不能解决该怎么办。我们可能会在两个或三个不同的组件中使用这个解析器,如果解析器失败,它们中的每一个都可能决定以不同的方式做出反应。我们可能希望将其中一个重定向到 404 页面,但在其他页面中,我们可能只是尝试通过显示其他内容来优雅地修复错误。

当然,我们可能还想根据我们得到的错误做出不同的反应:也许用户没有被授权,或者那个项目可能被删除了,或者根本就不存在,谁知道呢。我们可能想要显示不同的结果,在这种情况下,我完全赞成 DeborahK的更新答案。但在大多数情况下,我认为这使问题变得过于复杂(仅用于解析器的额外接口,确保其中的错误是描述性的......)而且我们可能不会真正关心解析器失败的原因:它只是做了,让需要该项目的组件弄清楚要做什么,然后继续前进。

import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot } from '@angular/router';
import { Observable, of } from 'rxjs';
import { catchError } from 'rxjs/internal/operators';
import { Product } from '../_interfaces';
import { ProductsService } from '../_services';

@Injectable()
export class ProductResolver implements Resolve<Product> {

    constructor(private productsService: ProductsService) {
    }

    public resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> {
        const id = route.paramMap.get('id');
        return this.productsService.getProduct(id).pipe(
            catchError(error => {
                console.error(`Can't resolve product with id ${id} because of the error:`);
                console.error(error);
                return of(null);
            })
        );
    }
}
Run Code Online (Sandbox Code Playgroud)