HttpClientを理解する
APIサーバーとの通信を行うhttpClient
。ただの通信を行うだけなのだが、実は中身はかなり複雑で公式ドキュメントの情報量を多い。しかしコーディングする上では分かりにくい。get
がどのように実装されているのか。どのようにコーディングすればよいのかを理解すれば、その他のメソッドであるpost
、delete
などのメソッドもコーディングしやすくなる。
Link
- Angular - Communicating with backend services using HTTP
- https://github.com/angular/angular/blob/master/packages/common/http/src/client.ts#L1101
- HTTP レスポンスステータスコード - HTTP | MDN
GETがどのように実装されているか
getメソッドの実態のコードを見てみる。
https://github.com/angular/angular/blob/master/packages/common/http/src/client.ts#L1101
get(url: string, options: {
headers?: HttpHeaders|{[header: string]: string | string[]},
observe?: HttpObserve,
params?: HttpParams|{[param: string]: string | string[]},
reportProgress?: boolean,
responseType?: 'arraybuffer'|'blob'|'json'|'text',
withCredentials?: boolean,
} = {}): Observable<any> {
return this.request<any>('GET', url, options as any);
}
2
3
4
5
6
7
8
9
10
型情報が大雑把でany
も使っているが、これは10以上のメソッドをオーバーロードしているから。
getの戻り値
responseType?: 'arraybuffer'|'blob'|'json'|'text'
に記述されている通り、データ形式は4つある。その中でもよく使うであろう
jsonを取得する場合(responseType: 'json'
)でもいくつか記述方法が存在する。オーバーロードされた3つのソースをピックアップしてみた。
get(url: string, options?: {
headers?: HttpHeaders|{[header: string]: string | string[]},
observe?: 'body',
params?: HttpParams|{[param: string]: string | string[]},
reportProgress?: boolean,
responseType?: 'json',
withCredentials?: boolean,
}): Observable<Object>;
get(url: string, options: {
headers?: HttpHeaders|{[header: string]: string | string[]},
observe: 'response',
params?: HttpParams|{[param: string]: string | string[]},
reportProgress?: boolean,
responseType?: 'json',
withCredentials?: boolean,
}): Observable<HttpResponse<Object>>;
get(url: string, options: {
headers?: HttpHeaders|{[header: string]: string | string[]},
observe: 'events',
params?: HttpParams|{[param: string]: string | string[]},
reportProgress?: boolean,
responseType?: 'json',
withCredentials?: boolean,
}): Observable<HttpEvent<Object>>;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
注目してもらいたいものはobserve
によって、戻り値が変わるところ。observer
は3種類存在する。
export type HttpObserve = 'body'|'events'|'response';
observe | return | 用途 |
---|---|---|
body | Observable<Object> | 情報のみ取得 |
response | Observable<HttpResponse<Object>> | 情報のみ取得した場合 + header情報を取得 |
events | Observable<HttpEvent<Object>> | 全ての情報を取得したい場合 |
型を指定しなければObject型を返すが、ジェネリクスも実装されている。通常はジェネリクスで宣言されたメソッドを使うことになる。
get<T>(url: string, options?: {
headers?: HttpHeaders|{[header: string]: string | string[]},
observe?: 'body',
params?: HttpParams|{[param: string]: string | string[]},
reportProgress?: boolean,
responseType?: 'json',
withCredentials?: boolean,
}): Observable<T>;
2
3
4
5
6
7
8
リクエストJsonの型を定義し、3種類のobserve
を定義した場合は下記のようになる。
import { HttpClient, HttpEvent, HttpResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { environment } from 'src/environments/environment';
export interface Config {
heroesUrl: string;
textfile: string;
}
@Injectable({
providedIn: 'root',
})
export class HttpStatusTestService {
constructor(private httpClient: HttpClient) {}
public requestGetBody(): Observable<Config> {
return this.httpClient.get<Config>(`${environment.apiUrl}`, {
responseType : 'json',
observe: 'body',
});
}
public requestGetResponse(): Observable<HttpResponse<Config>> {
return this.httpClient.get<Config>(`${environment.apiUrl}`, {
responseType : 'json',
observe: 'response',
});
}
public requestGetEvent(): Observable<HttpEvent<Config>> {
return this.httpClient.get<Config>(`${environment.apiUrl}`, {
responseType : 'json',
observe: 'events',
});
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
戻り値はresponseTypeとobserveにより変化することを覚えておいてほしい。HttpEvent
、HttpResponse
の型定義は@angular/common/http
に存在する
import { HttpClient, HttpEvent, HttpResponse } from '@angular/common/http';
responseType | observe | 戻り値 |
---|---|---|
arraybuffer | body | Observable<ArrayBuffer> |
arraybuffer | response | Observable<HttpResponse<ArrayBuffer>> |
arraybuffer | events | Observable<HttpEvent<ArrayBuffer>> |
blob | body | Observable<Blob> |
blob | response | Observable<HttpResponse<Blob>> |
blob | events | Observable<HttpEvent<Blob>> |
json | body | Observable<Object> |
json | response | Observable<HttpResponse<Object>> |
json | events | Observable<HttpEvent<Object>> |
text | body | Observable<string> |
text | response | Observable<HttpResponse<string>> |
text | events | Observable<HttpEvent<string>> |
※ObjectはジェネリクスT
により型を設定できる。
オーバーロードされていること、パラメーターにより戻り値が変わることを理解すればコーディングができる。
post
、put
、delete
などのメソッドも似たコードになっている。IDE上ではオーバーロードされた情報が表示されるためすっごく見にくいのでコードを見ることをお勧めする。
https://github.com/angular/angular/blob/master/packages/common/http/src/client.ts
HTTPステータスによる動作の確認
※下記サンプルコードはsubscribeに注力するためにサービス定義をしないコーディングをしている。本来はサービスを実装・仕様によりエラーハンドラを設定するべき。
まずはsubscribeの処理の確認から。subscribeでは、正常処理、エラー、完了通知に分けることができる。なおエラーと完了通知はコーディング省略可能。これらの処理はHTTPレスポンスステータスコードにより処理の流れが変わる。
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
// 型
export interface Config {
heroesUrl: string;
textfile: string;
}
// componentメソッド
debug(): void {
this.httpClient
.get<Config>('http://localhost:8080/config', { observe: 'body' })
.subscribe(
(value) => {}, // 1.正常処理
(error : HttpErrorResponse) => {}, // 2.エラー(省略可能)
() => {} // 3.完了通知(省略可能)
);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
HTTP Status Code | 処理順 |
---|---|
情報レスポンス(100-199) | 2.エラー |
成功レスポンス (200–299) | 1.正常処理 → 3.完了通知 |
リダイレクト (300–399) | 2.エラー |
クライアントエラー (400–499) | 2.エラー |
サーバエラー (500–599) | 2.エラー |
つまり、成功レスポンスと、それ以外で分けることができる。
responseTypeの使い分け方
responseType | 用途 |
---|---|
arraybuffer | 固定長のバイナリーデータのバッファを示すために使われるデータタイプ |
blob | ファイルに似たオブジェクトで、immutable な生データ |
json | jsonデータ |
text | text |
よく使うのはjson
。arraybutterとblobはよくわからない。バイナリやファイルをHttpClientで取得するのがベストなのか?
ちなみにresponseType
で指定されたデータ型をがレスポンスとして必ず取得できる訳ではない。APIサーバーはresponseType
の情報を受け取らないためAPIサーバーの仕様通りのレスポンスを返す。
responseType
はクライアント側でのコーディングを明示的に行うための記述であることに注意が必要である。
observeの使い分け
observe | 用途 |
---|---|
body | リクエストしたデータのみ欲しい場合 |
response | HttpResponseが欲しい場合 |
events | HttpEventが欲しい場合 |
リクエストデータのみを取得したい場合はobserve:'body'
を指定する。
debug(): void {
this.httpClient
.get<Config>('http://localhost:8080/config', { observe: 'body' , responseType : 'json' })
.subscribe(
(value : Config) => {
console.log(value); // レスポンスデータ。ここではConfigのJSONデータ
},
(error : HttpErrorResponse) => {},
() => {}
);
}
2
3
4
5
6
7
8
9
10
11
HttpResponseが欲しい場合はobserve:'response'
を指定する。
HttpResponseはリクエストデータ情報の他に、headers情報、statusなど情報を使いたい場合に使用する。
HttpResponse.body
からリクエストデータを取得することができる。
debug(): void {
this.httpClient
.get<Config>('http://localhost:8080/config', { observe: 'response' , responseType : 'json' })
.subscribe(
(value : HttpResponse<Config>) => {
console.log(value.status); // 正常処理であるコードステータス(大抵は200)
console.log(value.body); // レスポンスデータ。ここではConfigのJSONデータ
},
(error : HttpErrorResponse) => {},
() => {}
);
}
2
3
4
5
6
7
8
9
10
11
12
HttpEventは複数のデータ型の定義となる。コードを見てみるとすぐわかる。
type HttpEvent<T> = HttpSentEvent | HttpHeaderResponse | HttpResponse<T> | HttpProgressEvent | HttpUserEvent<T>;
HttpEvent
がどの型なのかtype
により調査しなければならない。
import { HttpClient ,HttpEventType } from '@angular/common/http';
debug(): void {
this.httpClient
.get<Config>('http://localhost:8080/config', { observe: 'events' ,responseType : 'json'})
.subscribe(
(value : HttpEvent<Config>) => {
if (value.type === HttpEventType.Response) { // typeを検査した後に処理を行う。
// HttpResponseと確定される
console.log(value.status); // 正常処理であるコードステータス(大抵は200)
console.log(value.body); // レスポンスデータ。ここではConfigのJSONデータ
}
},
(error : HttpErrorResponse) => {},
() => {}
);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17