使用 HTTPClient.get()
方法從服務(wù)器獲取數(shù)據(jù)。該異步方法會發(fā)送一個 HTTP 請求,并返回一個 Observable
,它會在收到響應(yīng)時發(fā)出所請求到的數(shù)據(jù)。返回的類型取決于你調(diào)用時傳入的 observe
和 responseType
參數(shù)。
get()
方法有兩個參數(shù)。要獲取的端點 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
選項用于指定要返回的響應(yīng)內(nèi)容。responseType
選項指定返回數(shù)據(jù)的格式。你可以使用
options
對象來配置傳出請求的各個方面。例如,在Adding headers
中,該服務(wù)使用headers
選項屬性設(shè)置默認頭。
使用
params
屬性可以配置帶 HTTP URL 參數(shù)的請求,reportProgress
選項可以在傳輸大量數(shù)據(jù)時監(jiān)聽進度事件。
應(yīng)用經(jīng)常會從服務(wù)器請求 JSON 數(shù)據(jù)。在 ConfigService
例子中,該應(yīng)用需要服務(wù)器 "config.json" 上的一個配置文件來指定資源的 URL
。
Path:"assets/config.json" 。
{
"heroesUrl": "api/heroes",
"textfile": "assets/textfile.txt"
}
要獲取這類數(shù)據(jù),get()
調(diào)用需要以下幾個選項: {observe: 'body', responseType: 'json'}。這些是這些選項的默認值,所以下面的例子不會傳遞 options
對象。后面幾節(jié)展示了一些額外的選項。
這個例子符合通過定義一個可重用的可注入服務(wù)來執(zhí)行數(shù)據(jù)處理功能來創(chuàng)建可伸縮解決方案的最佳實踐。除了提取數(shù)據(jù)外,該服務(wù)還可以對數(shù)據(jù)進行后處理,添加錯誤處理,并添加重試邏輯。
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
并調(diào)用了 getConfig
服務(wù)方法。
由于該服務(wù)方法返回了一個 Observable
配置數(shù)據(jù),該組件會訂閱該方法的返回值。訂閱回調(diào)只會對后處理進行最少量的處理。它會把數(shù)據(jù)字段復制到組件的 config
對象中,該對象在組件模板中是數(shù)據(jù)綁定的,用于顯示。
Path:"app/config/config.component.ts (showConfig v.1)" 。
showConfig() {
this.configService.getConfig()
.subscribe((data: Config) => this.config = {
heroesUrl: data['heroesUrl'],
textfile: data['textfile']
});
}
你可以構(gòu)造自己的 HttpClient
請求來聲明響應(yīng)對象的類型,以便讓輸出更容易、更明確。所指定的響應(yīng)類型會在編譯時充當類型斷言。
注:
- 指定響應(yīng)類型是在向 TypeScript 聲明,它應(yīng)該把你的響應(yīng)對象當做給定類型來使用。這是一種構(gòu)建期檢查,它并不能保證服務(wù)器會實際給出這種類型的響應(yīng)對象。該服務(wù)器需要自己確保返回服務(wù)器 API 中指定的類型。
要指定響應(yīng)對象類型,首先要定義一個具有必需屬性的接口。這里要使用接口而不是類,因為響應(yīng)對象是普通對象,無法自動轉(zhuǎn)換成類的實例。
export interface Config {
heroesUrl: string;
textfile: string;
}
接下來,在服務(wù)器中把該接口指定為 HttpClient.get()
調(diào)用的類型參數(shù)。
Path:"app/config/config.service.ts (getConfig v.2)" 。
getConfig() {
// now returns an Observable of Config
return this.http.get<Config>(this.configUrl);
}
當把接口作為類型參數(shù)傳給
HttpClient.get()
方法時,你可以使用RxJS map 操作符來根據(jù) UI 的需求轉(zhuǎn)換響應(yīng)數(shù)據(jù)。然后,把轉(zhuǎn)換后的數(shù)據(jù)傳給異步管道。
修改后的組件方法,其回調(diào)函數(shù)中獲取一個帶類型的對象,它易于使用,且消費起來更安全:
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 獲得的普通對象顯式轉(zhuǎn)換為所需的響應(yīng)類型。例如,以下 subscribe
回調(diào)會將 data
作為對象接收,然后進行類型轉(zhuǎn)換以訪問屬性。
.subscribe(data => this.config = {
heroesUrl: (data as any).heroesUrl,
textfile: (data as any).textfile,
});
OBSERVE
和 RESPONSE
的類型是字符串的聯(lián)合類型,而不是普通的字符串。
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
就是以這種方式顯式輸入的,因此編譯器可以根據(jù)你提供的選項報告正確的返回類型。
使用 as const
,可以讓 TypeScript 知道你并不是真的要使用字面字符串類型:
const options = {
responseType: 'text' as const,
};
client.get('/foo', options);
在前面的例子中,對 HttpClient.get()
的調(diào)用沒有指定任何選項。默認情況下,它返回了響應(yīng)體中包含的 JSON 數(shù)據(jù)。
你可能還需要關(guān)于這次對話的更多信息。比如,有時候服務(wù)器會返回一個特殊的響應(yīng)頭或狀態(tài)碼,來指出某些在應(yīng)用的工作流程中很重要的條件。
可以用 get()
方法的 observe
選項來告訴 HttpClient
,你想要完整的響應(yīng)對象:
getConfigResponse(): Observable<HttpResponse<Config>> {
return this.http.get<Config>(
this.configUrl, { observe: 'response' });
}
現(xiàn)在,HttpClient.get()
會返回一個 HttpResponse
類型的 Observable
,而不只是 JSON 數(shù)據(jù)。
該組件的 showConfigResponse()
方法會像顯示配置數(shù)據(jù)一樣顯示響應(yīng)頭:
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 };
});
}
注:
- 該響應(yīng)對象具有一個帶有正確類型的
body
屬性。
當服務(wù)器不支持 CORS
協(xié)議時,應(yīng)用程序可以使用 HttpClient
跨域發(fā)出 JSONP 請求。
Angular 的 JSONP 請求會返回一個 Observable
。 遵循訂閱可觀察對象變量的模式,并在使用async
管道管理結(jié)果之前,使用 RxJS map 操作符轉(zhuǎn)換響應(yīng)。
在 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
作為第一個參數(shù),并將回調(diào)函數(shù)名稱作為第二個參數(shù)。響應(yīng)被包裝在回調(diào)函數(shù)中,該函數(shù)接受 JSONP 方法返回的可觀察對象,并將它們通過管道傳給錯誤處理程序。
不是所有的 API 都會返回 JSON 數(shù)據(jù)。在下面這個例子中,DownloaderService
中的方法會從服務(wù)器讀取文本文件, 并把文件的內(nèi)容記錄下來,然后把這些內(nèi)容使用 Observable<string>
的形式返回給調(diào)用者。
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()
方法通過訂閱這個服務(wù)中的方法來發(fā)起一次請求。
Path:"app/downloader/downloader.component.ts (download)" 。
download() {
this.downloaderService.getTextFile('assets/textfile.txt')
.subscribe(results => this.contents = results);
}
更多建議: