【问题标题】:How to handle error in a Resolver如何处理解析器中的错误
【发布时间】:2017-10-09 11:36:11
【问题描述】:

我正在尝试使用解析器来制作更好的用户体验。在幸福的道路上一切都很好。我似乎无法弄清楚如何处理异常。我的解析器调用了一个服务,该服务访问了一个 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);
  });
} 

FooService:

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

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);
}

在 FooComponent 内部,我这样做(如果服务/解析器返回错误,这永远不会命中):

FooComponent

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

我尝试抛出错误(如图所示) - 我在控制台中收到此异常:

未捕获(承诺中):状态为 500 内部服务器错误的响应 网址:

并返回new Observable&lt;Foo&gt;(),它给出:

无法读取未定义的属性“订阅”

我有几个解析器,在服务器上都可以遇到异常,但是遇到这些异常我不知道该怎么办。

【问题讨论】:

    标签: angular rxjs


    【解决方案1】:

    这是我使用 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);
                });
        }
    }
    

    您可以在此处找到完整示例:https://github.com/DeborahK/Angular-Routing 在 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;
    }
    
    1. 解析到那个界面:
    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 });
          }),
        );
      }
    }
    
    1. 在组件中,拉出你需要的界面部分:
    ngOnInit(): void {
      const resolvedData: ProductResolved = this.route.snapshot.data['resolvedData'];
      this.errorMessage = resolvedData.error;
      this.product = resolvedData.product;
    }
    

    【讨论】:

    • 接受这个作为答案,因为它给了我一个更完整的例子。这正是我想要的!
    • 总是解决某些问题,即使发生错误,也与解决的目的相矛盾。通过使用解析,我是说负责此路由的组件需要此信息,如果由于某种原因,该信息不可用,则不应呈现。 resolve Guard 中的 Promise (Observable) 拒绝这样做并阻止渲染路由的组件,但应该有一个统一的机制来捕获 resolve 错误并使用某些组件处理它们以进行错误处理。
    • 最好使用Observable.throw(theError); 而不是null
    • 在 YouTube 上观看来自 NGConf 的演讲,谷歌搜索问题和我看到的第一个答案 - 又是你,哈哈
    • @AlirezaMirian 你会推荐什么?当我遇到错误时,我也不确定是否导航到组件,因为解析器的目的之一是防止显示部分页面。当您从前一页导航时,这很容易。您只是呆在那里,但是如果您在使用解析器的页面上按 f5 会怎样?这种情况我该怎么办?
    【解决方案2】:

    您需要返回一个以 false 结尾的 observable

    handleError() {
      return Observable.of([false]);
    }
    

    【讨论】:

    • 谢谢。我接受了另一个答案,因为它给出了一个更完整的例子。
    【解决方案3】:

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

    为什么?好吧,我认为解析器的工作是解决问题,而不是弄清楚如果无法解决该怎么办。我们可能在两个或三个不同的组件中使用这个解析器,如果解析器失败,每个组件都可能决定以不同的方式做出反应。我们可能希望在其中一个页面中重定向到 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);
                })
            );
        }
    }
    

    【讨论】:

    • 我自己偶然发现了这个怪癖。我还必须注意,由于在解析器中以这种方式捕获错误,现在您可以通过某种方式在组件类中捕获错误。去图吧!
    猜你喜欢
    • 1970-01-01
    • 2015-03-12
    • 2016-02-07
    • 1970-01-01
    • 2020-03-06
    • 2021-06-12
    • 2022-11-16
    • 1970-01-01
    • 2011-03-11
    相关资源
    最近更新 更多