【问题标题】:Angular Node server returns You provided 'undefined' where a stream was expectedAngular Node 服务器返回您在预期流的位置提供了“未定义”
【发布时间】:2020-04-07 17:50:36
【问题描述】:

我正在使用 Angular 9 + 通用。运行ng serve 时没有错误,然后我使用npm run build:ssr 构建应用程序并尝试使用节点运行:node dist/app/server/main.js 并在终端中收到以下错误:

Node Express 服务器监听 http://localhost:4000 TypeError: You 在预期流的地方提供了“未定义”。您可以提供一个 可观察、承诺、数组或可迭代。 在 subscribeTo (C:\Users\andri\OneDrive\Desktop\devfox\autorent\ng-videocms\dist\autorent\server\main.js:1:2547459) 在 subscribeToResult (C:\Users\andri\OneDrive\Desktop\devfox\autorent\ng-videocms\dist\autorent\server\main.js:1:2775326) 在 CatchSubscriber.error (C:\Users\andri\OneDrive\Desktop\devfox\autorent\ng-videocms\dist\autorent\server\main.js:1:1997435) 在 Observable_Observable._trySubscribe (C:\Users\andri\OneDrive\Desktop\devfox\autorent\ng-videocms\dist\autorent\server\main.js:1:1952954) 在 Observable_Observable.subscribe (C:\Users\andri\OneDrive\Desktop\devfox\autorent\ng-videocms\dist\autorent\server\main.js:1:1952574) 在 CatchOperator.call (C:\Users\andri\OneDrive\Desktop\devfox\autorent\ng-videocms\dist\autorent\server\main.js:1:1996823) 在 Observable_Observable.subscribe (C:\Users\andri\OneDrive\Desktop\devfox\autorent\ng-videocms\dist\autorent\server\main.js:1:1952428) 在 _task (C:\Users\andri\OneDrive\Desktop\devfox\autorent\ng-videocms\dist\autorent\server\main.js:1:1751796) 在 Observable_Observable.Observable.a.observer [as _subscribe] (C:\Users\andri\OneDrive\Desktop\devfox\autorent\ng-videocms\dist\autorent\server\main.js:1:1752141) 在 Observable_Observable._trySubscribe (C:\Users\andri\OneDrive\Desktop\devfox\autorent\ng-videocms\dist\autorent\server\main.js:1:1952792)

正如我所探索的,我的应用程序在启动时执行了 2 次 api 调用:

app.component.ts:

ngOnInit(){
get1();
get2();
}

  get1() {
    const loc = this.locationService.getPickupLocations().subscribe((data: Location[]) => {
      this.pickupLocations = data;
      this.formGroup.get(LocationFields.pickup).setValue(data[0].getId());
      this.pickupLocationsList = this.pickupLocations.map((data): ISelectOption => {
        return {
          label: data.getName(),
          value: data.getId(),
        };
      });
    },
    (error)=> {
      console.log(error)
    },
    () => {
      this.subs.add(loc);
      this.pickupDateChange(this.formGroup.get(this.LocationFields.pickupDate).value);
    });
  }


  get2() {
    const drop = this.locationService.getDropOffLocations().subscribe((data: Location[]) => {
      this.dropoffLocations = data;
      this.formGroup.get(LocationFields.dropoff).setValue(data[1].getId());
      this.dropoffLocationsList = this.dropoffLocations.map((data): ISelectOption => {
        return {
          label: data.getName(),
          value: data.getId(),
        };
      });
    },(error)=> {
      console.log(error)
    },
    () => {
      this.subs.add(drop);
    });
  }

LocationService.ts:

  static locationsEndpoint = 'public/locations/rental';
  getPickupLocations(): Observable<Location[]> {
    const reqHeader = new HttpHeaders({ 'Content-Type': 'application/json', 'No-Auth': 'True' });
    return this.http.get(`${LocationsService.locationsEndpoint}/pickup`, { headers: reqHeader }).pipe(
     map((data: ILocationResponse) => this.hydrateCollectionData(data, LocationsHydrator))
    );
  }

  getDropOffLocations(): Observable<Location[]> {
    const reqHeader = new HttpHeaders({ 'Content-Type': 'application/json', 'No-Auth': 'True' });
    return this.http.get(`${LocationsService.locationsEndpoint}/dropoff`, { headers: reqHeader }).pipe(
     map((data: ILocationResponse) => this.hydrateCollectionData(data, LocationsHydrator))
    );
  }

和拦截器:

 private static BASE_URL = environment.apiUrl;

  readonly HEADER_AUTHORIZATION = 'Authorization';
  readonly HEADER_ACCEPT = 'Accept';
  readonly HEADER_CONTENT_TYPE = 'Content-Type';
  readonly ACCEPT_LANGUAGE = 'Accept-Language';

  constructor(
    private authService: AuthService,
    private localeService: LocaleService
  ) { }

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    if (req.headers.get('skip')) {
          return next.handle(req);
    }
    if (req.url.startsWith('./assets')) {
      return next.handle(req);
    }
    req = req.clone({
      url: this._prefixUrl(req.url)
    });

    req = req.clone({
      headers: req.headers.set(this.HEADER_ACCEPT, 'application/json')
    });

    req = req.clone({
      headers: req.headers.set(this.HEADER_CONTENT_TYPE, 'application/json')
    });

    req = req.clone({
      headers: req.headers.set(this.ACCEPT_LANGUAGE, this.localeService.getLocale())
    });

    // Set token if exists
    const token = this.authService.getToken();

    if (token) {
      req = req.clone({
        headers: req.headers.set(this.HEADER_AUTHORIZATION, `Bearer ${token}`)
      });
    }

    return next.handle(req).pipe(
      catchError((httpErrorResponse: HttpErrorResponse) => {
        if(httpErrorResponse.error !== undefined){
          const customError: ApiErrors = {
            name: httpErrorResponse.error.name,
            message: httpErrorResponse.error.message,
            errors: httpErrorResponse.error.errors
          };
          return throwError(customError);
        }
      })
    );
  }

  private _prefixUrl(path: string): string {
    if (path.indexOf('/') === 0) {
      path = path.substr(1, path.length - 1);
    }

    return `${Interceptor.BASE_URL}/${path}`;
  }

我试过没有这些电话,试着评论其中一个。

当我禁用这些调用(评论它们)时,应用程序工作正常。

当我稍后给他们打电话时,在我评论他们之后,(点击),应用程序工作正常。

当我禁用拦截器时,它可以工作(所以问题出在它们身上,要改变什么??)

如何解决?为什么会这样?

【问题讨论】:

    标签: javascript node.js angular typescript rxjs


    【解决方案1】:

    跟踪似乎表明问题与CatchOperator 有关。

    我只看到在您的代码中使用catchError 运算符的地方

    return next.handle(req).pipe(
          catchError((httpErrorResponse: HttpErrorResponse) => {
            if(httpErrorResponse.error !== undefined){
              const customError: ApiErrors = {
                name: httpErrorResponse.error.name,
                message: httpErrorResponse.error.message,
                errors: httpErrorResponse.error.errors
              };
              return throwError(customError);
            }
          })
        );
    

    在您的代码中,您似乎假设传递给catchError 的函数将始终收到HttpErrorResponse 的实例,但事实并非如此。 catchError 将用于任何错误,因此传递给它的函数可以接收任何类型的错误。

    如果httpErrorResponse.error 为空,但上游 Observables 的某个地方仍然存在错误,会发生什么?根据上面的代码你没有返回任何东西,所以这可能是日志显示You provided 'undefined' where a stream was expected的原因。

    你可以抛出一个错误,而不是什么都不做,这样你就应该得到错误的详细信息,导致你输入catchError运算符,所以像这样

      catchError(err => {
        if(error instanceof HttpErrorResponse && httpErrorResponse.error !== undefined){
          const customError: ApiErrors = {
            name: httpErrorResponse.error.name,
            message: httpErrorResponse.error.message,
            errors: httpErrorResponse.error.errors
          };
          return throwError(customError);
        } else {
          throw err
        }
      })
    );
    

    【讨论】:

      猜你喜欢
      • 2022-01-17
      • 2020-03-10
      • 2021-11-29
      • 2023-04-06
      • 1970-01-01
      • 2018-11-08
      • 2018-05-31
      • 2021-09-10
      • 2020-03-26
      相关资源
      最近更新 更多