如何发起 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 发起请求
您可以使用 RestService 的 request 方法发起 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> 的请求选项对象。此泛型类型期望传入请求体的接口。当没有请求体时(例如在 GET 或 DELETE 请求中),您可以传入 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 加载服务都会正常工作。
抠丁客


