Angular9 请求数据
使用 HTTPClient.get()
方法从服务器获取数据。该异步方法会发送一个 HTTP 请求,并返回一个 Observable
,它会在收到响应时发出所请求到的数据。返回的类型取决于你调用时传入的 observe
和 responseType
参数。
get()
方法有两个参数。要获取的端点 URL
,以及一个可以用来配置请求的选项对象。
options: {
headers?: HttpHeaders | {[header: string]: string | string[]},
observe?: 'body' | 'events' | 'response',
params?: HttpParams|{[param: string]: string | string[]},
reportProgress?: boolean,
responseType?: 'arraybuffer'|'blob'|'json'|'text',
withCredentials?: boolean,
}
这些重要的选项包括 observe
和 responseType
属性。
observe
选项用于指定要返回的响应内容。
responseType
选项指定返回数据的格式。
你可以使用
options
对象来配置传出请求的各个方面。例如,在Adding headers
中,该服务使用headers
选项属性设置默认头。
使用
params
属性可以配置带 HTTP URL 参数的请求,reportProgress
选项可以在传输大量数据时监听进度事件。
应用经常会从服务器请求 JSON 数据。在 ConfigService
例子中,该应用需要服务器 "config.json" 上的一个配置文件来指定资源的 URL
。
Path:"assets/config.json" 。
{
"heroesUrl": "api/heroes",
"textfile": "assets/textfile.txt"
}
要获取这类数据,get()
调用需要以下几个选项: {observe: 'body', responseType: 'json'}。这些是这些选项的默认值,所以下面的例子不会传递 options
对象。后面几节展示了一些额外的选项。
这个例子符合通过定义一个可重用的可注入服务来执行数据处理功能来创建可伸缩解决方案的最佳实践。除了提取数据外,该服务还可以对数据进行后处理,添加错误处理,并添加重试逻辑。
ConfigService
使用 HttpClient.get()
方法获取这个文件。
Path:"app/config/config.service.ts (getConfig v.1)" 。
configUrl = 'assets/config.json';
getConfig() {
return this.http.get(this.configUrl);
}
ConfigComponent
注入了 ConfigService
并调用了 getConfig
服务方法。
由于该服务方法返回了一个 Observable
配置数据,该组件会订阅该方法的返回值。订阅回调只会对后处理进行最少量的处理。它会把数据字段复制到组件的 config
对象中,该对象在组件模板中是数据绑定的,用于显示。
Path:"app/config/config.component.ts (showConfig v.1)" 。
showConfig() {
this.configService.getConfig()
.subscribe((data: Config) => this.config = {
heroesUrl: data['heroesUrl'],
textfile: data['textfile']
});
}
请求输入一个类型的响应
你可以构造自己的 HttpClient
请求来声明响应对象的类型,以便让输出更容易、更明确。所指定的响应类型会在编译时充当类型断言。
注:
- 指定响应类型是在向 TypeScript 声明,它应该把你的响应对象当做给定类型来使用。这是一种构建期检查,它并不能保证服务器会实际给出这种类型的响应对象。该服务器需要自己确保返回服务器 API 中指定的类型。
要指定响应对象类型,首先要定义一个具有必需属性的接口。这里要使用接口而不是类,因为响应对象是普通对象,无法自动转换成类的实例。
export interface Config {
heroesUrl: string;
textfile: string;
}
接下来,在服务器中把该接口指定为 HttpClient.get()
调用的类型参数。
Path:"app/config/config.service.ts (getConfig v.2)" 。
getConfig() {
// now returns an Observable of Config
return this.http.get<Config>(this.configUrl);
}
当把接口作为类型参数传给
HttpClient.get()
方法时,你可以使用RxJS map 操作符来根据 UI 的需求转换响应数据。然后,把转换后的数据传给异步管道。
修改后的组件方法,其回调函数中获取一个带类型的对象,它易于使用,且消费起来更安全:
Path:"app/config/config.component.ts (showConfig v.2)" 。
config: Config;
showConfig() {
this.configService.getConfig()
// clone the data object, using its known Config shape
.subscribe((data: Config) => this.config = { ...data });
}
要访问接口中定义的属性,必须将从 JSON 获得的普通对象显式转换为所需的响应类型。例如,以下 subscribe
回调会将 data
作为对象接收,然后进行类型转换以访问属性。
.subscribe(data => this.config = {
heroesUrl: (data as any).heroesUrl,
textfile: (data as any).textfile,
});
OBSERVE
和 RESPONSE
的类型是字符串的联合类型,而不是普通的字符串。
options: {
...
observe?: 'body' | 'events' | 'response',
...
responseType?: 'arraybuffer'|'blob'|'json'|'text',
...
}
这会引起混乱。例如:
// this works
client.get('/foo', {responseType: 'text'})
// but this does NOT work
const options = {
responseType: 'text',
};
client.get('/foo', options)
在第二种情况下,TypeScript 会把 options
的类型推断为 {responseType: string}
。该类型的 HttpClient.get
太宽泛,无法传递给 HttpClient.get
,它希望 responseType
的类型是特定的字符串之一。而 HttpClient
就是以这种方式显式输入的,因此编译器可以根据你提供的选项报告正确的返回类型。
使用 as const
,可以让 TypeScript 知道你并不是真的要使用字面字符串类型:
const options = {
responseType: 'text' as const,
};
client.get('/foo', options);
读取完整的响应体
在前面的例子中,对 HttpClient.get()
的调用没有指定任何选项。默认情况下,它返回了响应体中包含的 JSON 数据。
你可能还需要关于这次对话的更多信息。比如,有时候服务器会返回一个特殊的响应头或状态码,来指出某些在应用的工作流程中很重要的条件。
可以用 get()
方法的 observe
选项来告诉 HttpClient
,你想要完整的响应对象:
getConfigResponse(): Observable<HttpResponse<Config>> {
return this.http.get<Config>(
this.configUrl, { observe: 'response' });
}
现在,HttpClient.get()
会返回一个 HttpResponse
类型的 Observable
,而不只是 JSON 数据。
该组件的 showConfigResponse()
方法会像显示配置数据一样显示响应头:
Path:"app/config/config.component.ts (showConfigResponse)" 。
showConfigResponse() {
this.configService.getConfigResponse()
// resp is of type `HttpResponse<Config>`
.subscribe(resp => {
// display its headers
const keys = resp.headers.keys();
this.headers = keys.map(key =>
`${key}: ${resp.headers.get(key)}`);
// access the body directly, which is typed as `Config`.
this.config = { ... resp.body };
});
}
注:
- 该响应对象具有一个带有正确类型的
body
属性。
发起 JSONP 请求
当服务器不支持 CORS
协议时,应用程序可以使用 HttpClient
跨域发出 JSONP 请求。
Angular 的 JSONP 请求会返回一个 Observable
。 遵循订阅可观察对象变量的模式,并在使用async
管道管理结果之前,使用 RxJS map 操作符转换响应。
在 Angular 中,通过在 NgModule
的 imports
中包含 HttpClientJsonpModule
来使用 JSONP。在以下示例中,searchHeroes()
方法使用 JSONP 请求来查询名称包含搜索词的英雄。
/* GET heroes whose name contains search term */
searchHeroes(term: string): Observable {
term = term.trim();
let heroesURL = `${this.heroesURL}?${term}`;
return this.http.jsonp(heroesUrl, 'callback').pipe(
catchError(this.handleError('searchHeroes', [])) // then handle the error
);
};
该请求将 heroesURL
作为第一个参数,并将回调函数名称作为第二个参数。响应被包装在回调函数中,该函数接受 JSONP 方法返回的可观察对象,并将它们通过管道传给错误处理程序。
请求非 JSON 数据
不是所有的 API 都会返回 JSON 数据。在下面这个例子中,DownloaderService
中的方法会从服务器读取文本文件, 并把文件的内容记录下来,然后把这些内容使用 Observable<string>
的形式返回给调用者。
Path:"app/downloader/downloader.service.ts (getTextFile)" 。
getTextFile(filename: string) {
// The Observable returned by get() is of type Observable<string>
// because a text response was specified.
// There's no need to pass a <string> type parameter to get().
return this.http.get(filename, {responseType: 'text'})
.pipe(
tap( // Log the result or error
data => this.log(filename, data),
error => this.logError(filename, error)
)
);
}
这里的 HttpClient.get()
返回字符串而不是默认的 JSON 对象,因为它的 responseType
选项是 'text'
。
RxJS 的 tap
操作符(如“窃听”中所述)使代码可以检查通过可观察对象的成功值和错误值,而不会干扰它们。
在 DownloaderComponent
中的 download()
方法通过订阅这个服务中的方法来发起一次请求。
Path:"app/downloader/downloader.component.ts (download)" 。
download() {
this.downloaderService.getTextFile('assets/textfile.txt')
.subscribe(results => this.contents = results);
}
更多建议: