(Angular) 拦截 HttpResponse 错误并继续 Observable

(Angular) Intercept HttpResponse error and continue the Observable

我有一个下拉文本框可以进行预输入搜索。当我搜索一个有效的项目名称(存在于数据库中)时,搜索工作正常并且 returns 下拉列表中的项目列表从我键入的 select 。但是当我搜索无效文本时,API returns 出现 400 错误(这很好),然后 HttpErrorInterceptorcatchError() 方法中拦截该响应,并抛出错误弹出窗口。我不想要错误弹出窗口,我希望它将错误转发给文本框逻辑,这样我就可以在下拉列表中显示 'No Items Found'。

文本框 html(使用 Angular 的 NgbTypeahead):

      <input
        id="searchText"
        type="text"
        [(ngModel)]="selectedItem"
        (selectItem)="onSelectItem($event)"
        formControlName="searchText"
        [ngbTypeahead]="search"
        #instance="ngbTypeahead" />

文本框逻辑:

  search = (input: Observable<string>) => {
    return input.pipe(
      debounceTime(500),
      distinctUntilChanged(),
      switchMap((text) => text.length < 2 ? this.clearItems() //clearItems() is irrelavant
        : this.itemService.getItemSearchData(text).pipe(
          map(responseObj => {
            const itemList = responseObj.data ? orderBy(responseObj.data, ['itemName'], ['asc']) : [];

            if (itemList.length === 0) {

            // this is what I want it to do when I get the error response
              itemList.push({ itemName: 'No Items Found' } as ItemList);
            }
            return itemList;
          })
        )));
  }

// This is in the ItemService class.
  getItemSearchData(searchTerm: string): Observable<any> {
    const searchItem = {
      "filterBy": {
        "key": "itemname",
        "value": searchTerm
      }
    }
 
    return this.http.post(this.itemApiUrl, searchItem, { headers: this.headers });
  }

这是拦截器:

@Injectable()
export class HttpErrorInterceptor implements HttpInterceptor {
    constructor(private matDialog: MatDialog) { }
 
  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(request)
      .pipe(
        catchError((error: HttpErrorResponse) => {
            let errorMessage = 'Unknown error!';
            if (error.error instanceof ErrorEvent) {
                errorMessage = `Error: ${error.error.message}`;
            } else {
                errorMessage = `Error Code: ${error.status}\nMessage: ${error.message}`;
            }
            
            // the error popup. I DON'T want to throw this when I get the 404 response.
            this.matDialog.open(PopupComponent, {
                data: { actionDesctiption: errorMessage, isError: true },
                panelClass: 'custom-dialog-container'
            });
 
            return throwError(error);
        })
      );
  }

我试过这个:,但是顶级解决方案的 return of(new HttpResponse...; 语句给了我错误 Type 'Observable<unknown>' is not assignable to type 'Observable<HttpEvent<any>>'。我也尝试返回 next.handle(request)new Observable<HttpEvent<any>>().

当我在 map(responseObj => 行放置断点时,它总是显示“responseObj 未定义”。

当出现 API returns 400 错误时,如何让下拉菜单显示 'No Items Found'?

不清楚从您的 API 返回的数据结构是什么。假设 API returns 这种格式的数据:{ itemName: string }[](即 { itemName: string } 对象的数组,您可以使用 http 拦截器检查 404 错误,然后更改像这样的回应:

import { HttpRequest, HttpResponse, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';
import { HttpErrorResponse } from '@angular/common/http';
import { of, throwError } from 'rxjs';

@Injectable()
export class HttpErrorInterceptor implements HttpInterceptor {
    constructor(private matDialog: MatDialog) { }
 
  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(request)
      .pipe(
        catchError((error) => {
            let errorMessage = 'Unknown error!';
            if (error.error instanceof ErrorEvent) {
                errorMessage = `Error: ${error.error.message}`;
            } else {
                errorMessage = `Error Code: ${error.status}\nMessage: ${error.message}`;
            }
            
            // check for a 404 error response
            if (error instanceof HttpErrorResponse && error.status === 404) {
                return this.returnCustomData([{ itemName: 'No Items Found' }]); // returns a response, and doesn't throw the error
            }
            
            // the error popup. I DON'T want to throw this when I get the 404 response.
            this.matDialog.open(PopupComponent, {
                data: { actionDesctiption: errorMessage, isError: true },
                panelClass: 'custom-dialog-container'
            });

            return throwError(error);
        })
      );
    }

    private returnCustomData(body) {
        return of(new HttpResponse({ status: 200, body }));
    }

}

注意:同样,我假设您的 API returns 是一个 { itemName: string } 对象数组,这就是我使用一个对象的原因调用时数组中的对象 returnCustomData。请记住更改发送到 returnCustomData 的数据对象以匹配您的 API 返回的实际数据格式,就好像它只返回一个结果,包含单词 'No Items Found'.

我知道您的拦截器可以处理任何请求的所有 HTTP 错误,但是,由于您需要在您的组件中出现该消息错误,您在服务管道中也添加一个 catchError 怎么样?

search = (input: Observable<string>) => {
    return input.pipe(
      debounceTime(500),
      distinctUntilChanged(),
      switchMap((text) => text.length < 2 ? this.clearItems() 
        : this.itemService.getItemSearchData(text)
          .pipe(
             map(responseObj => {
               const itemList = responseObj.data ? orderBy(responseObj.data, ['itemName'], ['asc']) : [];
               return itemList;
             }),
             catchError(() => {
               itemList.push({ itemName: 'No Items Found' } as ItemList));
               of('');
             }
        )));
  }