【问题标题】:Handling multiple response types from same API in Angular在 Angular 中处理来自同一 API 的多种响应类型
【发布时间】:2020-06-20 08:08:55
【问题描述】:

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

interface I1 {
  responseData: customData[]
}

interface I2 {
  errorCode: number,
  errorMessage: string
}

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

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 的类型与函数返回值的预期不同。我可以将函数的返回类型更改为Observable&lt;I1 | I2&gt;,但我将无法仅返回数组。我无法以任何方式更改 API 结构。我尝试将 APIResponse 分配给I2 类型的中间变量,然后从函数中返回它,但它不允许我将'I1 | I2' 类型分配给'I2' 类型。

我应该如何从这里开始以获得所需的结果?

【问题讨论】:

    标签: angular rxjs observable httpclient


    【解决方案1】:

    将您的 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));
    

    【讨论】:

    • 由于I1 是一个接口而不是一个类,instanceof 将不起作用。此外,当我写this.APIResponse.responseData 时,它显示错误消息“'I1 | I2' 类型上不存在属性'responseData'。'I2' 类型上不存在属性'responseData'。”
    【解决方案2】:

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

    如果APIResponse 变量仅用于getData() 函数中只是为了创建一个新的observable,那么你不需要它。

    要记录响应或执行任何副作用,请使用 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 =&gt; { // do what you want with data })

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

    【讨论】:

      猜你喜欢
      • 2022-11-28
      • 2022-01-13
      • 1970-01-01
      • 1970-01-01
      • 2018-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-28
      相关资源
      最近更新 更多