项目

如何发起 HTTP 请求

关于 HttpClient

Angular 提供了强大的 HttpClient 用于与后端服务通信。它是在 XMLHttpRequest Web API 基础上构建的简化封装层,也是 Angular 官方推荐的 HTTP 请求代理。在 ABP 项目中使用 HttpClient 完全没有问题。

HttpClient 将错误处理留给调用方(方法)。换句话说,HTTP 错误需要手动处理,并通过订阅返回的 Observable 观察者来实现:

getConfig() {
  this.http.get(this.configUrl).subscribe(
    config => this.updateConfig(config),
    error => {
      // 在此处理错误
    },
  );
}

虽然这种方式清晰灵活,但即使将错误处理委托给存储或其他可注入服务,这样的错误处理仍然是重复性工作。

HttpInterceptor 能够捕获 HttpErrorResponse 并实现集中式错误处理。然而,在需要禁用默认错误处理程序(即拦截器)的情况下,需要额外的工作和对 Angular 内部机制的理解。详情请参阅此议题

RestService

ABP 核心模块提供了一个用于 HTTP 请求的工具服务:RestService。除非显式配置,否则它会捕获 HTTP 错误并分发 RestOccurError 动作。随后,该动作会被 ThemeSharedModule 引入的 ErrorHandler 捕获。由于您的应用应该已经导入了该模块,因此在使用 RestService 时,默认情况下所有 HTTP 错误都会自动处理。

开始使用 RestService

要使用 RestService,您必须将其作为依赖项注入到类中:

import { RestService } from '@abp/ng.core';
import { inject } from '@angular/core';

@Injectable({
  /* 此处为类的元数据 */
})
class DemoService {
  private rest = inject(RestService);
}

您无需在模块或组件/指令级别提供 RestService,因为它已经在根级别提供

如何使用 RestService 发起请求

您可以使用 RestServicerequest 方法发起 HTTP 请求。示例如下:

getFoo(id: number) {
  const request: Rest.Request<null> = {
    method: 'GET',
    url: '/api/some/path/to/foo/' + id,
  };

  return this.rest.request<null, FooResponse>(request);
}

request 方法总是返回一个 Observable<T>。因此,您可以在使用 getFoo 方法的地方执行以下操作:

doSomethingWithFoo(id: number) {
  this.demoService.getFoo(id).subscribe(
    foo => {
      // 对 foo 执行某些操作
    }
  )
}

您无需担心取消订阅问题。 RestService 在底层使用 HttpClient,因此它返回的每个 observable 都是有限 observable,即在成功或错误时会自动关闭订阅。

如您所见,request 方法接收一个类型为 Rest.Request<T> 的请求选项对象。此泛型类型期望传入请求体的接口。当没有请求体时(例如在 GETDELETE 请求中),您可以传入 null。以下是一个有请求体的示例:

postFoo(body: Foo) {
  const request: Rest.Request<Foo> = {
    method: 'POST',
    url: '/api/some/path/to/foo',
   body
  };

  return this.rest.request<Foo, FooResponse>(request);
}

您可以在此处查看完整的 Rest.Request<T> 类型,它与 Angular 中的 HttpRequest 类相比只有少量更改。

如何禁用 RestService 的默认错误处理程序

使用默认配置的 request 方法总是会处理错误。让我们看看如何改变这一行为并自行处理错误:

deleteFoo(id: number) {
  const request: Rest.Request<null> = {
    method: 'DELETE',
    url: '/api/some/path/to/foo/' + id,
  };

  return this.rest.request<null, void>(request, { skipHandleError: true });
}

skipHandleError 配置选项设置为 true 时,将禁用错误处理程序,返回的 observable 会抛出错误,您可以在订阅中捕获:

removeFooFromList(id: number) {
  this.demoService.deleteFoo(id).subscribe(
    foo => {
      // 对 foo 执行某些操作
    },
    error => {
      // 对错误执行某些操作
    }
  )
}

如何从应用配置中获取特定 API 端点

request 方法接收的另一个良好配置选项是 apiName(自 v2.4 起可用),可用于从应用配置中获取特定模块端点:

putFoo(body: Foo, id: string) {
  const request: Rest.Request<Foo> = {
    method: 'PUT',
    url: '/' + id,
   body
  };

  return this.rest.request<Foo, void>(request, {apiName: 'foo'});
}

只要环境变量如下设置,上面的 putFoo 将请求 https://localhost:44305/api/some/path/to/foo/{id}

// environment.ts

export const environment = {
  apis: {
    default: {
      url: "https://localhost:44305",
    },
    foo: {
      url: "https://localhost:44305/api/some/path/to/foo",
    },
  },

  /* 其余环境变量在此 */
};

如何观察响应对象或 HTTP 事件而非响应体

RestService 假定您通常对响应体感兴趣,默认将 observe 属性设置为 'body'。但有时您可能对其他内容感兴趣,例如自定义专有标头。为此,request 方法在其配置对象中接收 observe 属性:

getSomeCustomHeaderValue() {
  const request: Rest.Request<null> = {
    method: 'GET',
    url: '/api/some/path/that/sends/some-custom-header',
  };

  return this.rest.request<null, HttpResponse<any>>(
    request,
    {observe: Rest.Observe.Response},
  ).pipe(
    map(response => response.headers.get('Some-Custom-Header'))
  );
}

您可以在此处找到 Rest.Observe 枚举。

如何跳过 HTTP 拦截器和 ABP 标头

ABP 会向 HttpClient 添加多个 HTTP 标头,例如"认证令牌"或"租户 ID"。ABP 服务器必须拥有这些信息,但 ABP 用户可能不希望将这些信息发送到外部服务器。

V6.0.4 版本中新增了 ExternalHttpClient 和 IS_EXTERNAL_REQUEST HttpContext 令牌。ABP Http 拦截器会检查 IS_EXTERNAL_REQUEST 令牌的值。如果该令牌为 True,则不会向 Http 请求添加 ABP 特定标头。

ExternalHttpClient 继承自 HTTPClient,并将 IS_EXTERNAL_REQUEST 上下文令牌设置为 true。当您在组件中使用 ExternalHttpClient 作为 HttpClient 时,它不会添加 ABP 特定标头。

注意:无论是否使用 IS_EXTERNAL_REQUEST,ABP 加载服务都会正常工作。

另请参阅

在本文档中