处理来自 Angular 中相同 API 的多种响应类型

Handling multiple response types from same API in Angular

我是 Angular 的新手。我一直在开发一项服务,该服务从 API 和 return 中获取某些数据。 API 可以 return 两种类型的 JSON 响应,自定义数据类型数组或自定义错误代码和消息,以防服务器出现内部错误。根据两种可能的响应,我创建了两个接口,比如 I1I2。 API 编辑的 JSON 响应 return 也分别遵循相同的结构。

interface I1 {
  responseData: customData[]
}

interface I2 {
  errorCode: number,
  errorMessage: string
}

对于服务本身,我想编写一个函数,它首先获取数据并检查接收到的类型。如果类型是 I1,那么它将 return I1 中数组的可观察对象,或者如果类型是 I2,那么它将在控制台上记录错误,并且return 错误对象。我在服务中写了一个函数 class:

APIResponse: I1 | I2;

getData(): Observable<CustomData[] | I2> {
  this.http.get<I1 | I2>('url/to/api').subscribe(res => {
    this.APIResponse = res;
  });

  if('errorCode' in this.APIResponse) {
    console.log(this.APIResponse.errorCode);
    return of(this.APIResponse);
  }
  else if('responseData' in APIResponse) {
    return of(this.APIResponse.responseData);
  }
}

现在此代码在编译时显示错误,因为 this.APIResponse 的类型与其 return 值的函数预期的类型不同。我可以将函数的 return 类型更改为 Observable<I1 | I2>,但我将无法仅 return 数组。我无法以任何方式更改 API 结构。我曾尝试将 APIResponse 分配给 I2 类型的中间变量,然后从函数中 return 分配它,但它不允许我分配类型 'I1 | I2' 到类型 'I2'

我应该如何从这里开始才能得到想要的结果?

将您的 getData 方法更改为:-

getData(): Observable<CustomData[] | I2> {
  return this.http.get('url/to/api').pipe(map((res : I1|I2) => {
    if('errorCode' in res) {
       this.APIResponse = res;  
       console.log(this.APIResponse.errorCode);
       return res;
    } else {
         this.APIResponse = res;
         return res.responseData;
    }
  }));
}

任何你想使用这个方法的地方,像这样使用它:-

getData().subscribe((res) => console.log(res));

我不确定您为什么订阅服务,但这是反模式。

如果 APIResponse 变量仅在 getData() 函数中使用,只是为了创建一个新的可观察对象,那么您不需要它。

要记录响应或执行任何副作用,请使用 tap 运算符。

getData(): Observable<CustomData[] | I2> {
    return this.http.get<I1 | I2>('url/to/api').pipe(
      map(res => {
        if ('responseData' in res) {
          return res.responseData;
        } else if ('errorCode' in res) {
          return res;
        }
      }),
      tap(res => {
        // Side effects

        if ('errorCode' in res) {
          console.log(res.errorCode);
        }
      }),
    );
  }

如果您正在使用 APIResponse 变量并想为其分配响应,则必须在 tap 运算符中执行此操作,因为这是副作用。

  tap(res => {

    // Assign value to APIResponse;

    this.APIResponse = res;
  })

最后,要从服务器获取数据,请在您的组件中订阅 getData() 可观察流。

this._service.getData().subscribe(data => { // do what you want with data })

如果您的服务使用 2xx 以外的代码发送错误响应,那么您可以使用 catchError 运算符或使用 http 选项来完全控制请求。