HttpClientを理解する

APIサーバーとの通信を行うhttpClient。ただの通信を行うだけなのだが、実は中身はかなり複雑で公式ドキュメントの情報量を多い。しかしコーディングする上では分かりにくい。getがどのように実装されているのか。どのようにコーディングすればよいのかを理解すれば、その他のメソッドであるpostdeleteなどのメソッドもコーディングしやすくなる。

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);
  }
1
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>>;
1
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';
1
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>;
1
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',
    });
  }
}
1
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により変化することを覚えておいてほしい。HttpEventHttpResponseの型定義は@angular/common/httpに存在する

import { HttpClient, HttpEvent, HttpResponse } from '@angular/common/http';
1
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により型を設定できる。

オーバーロードされていること、パラメーターにより戻り値が変わることを理解すればコーディングができる。 postputdeleteなどのメソッドも似たコードになっている。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.完了通知(省略可能)
      );
  }
1
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) => {},
        () => {}
      );
  }
1
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) => {},
        () => {}
      );
  }
1
2
3
4
5
6
7
8
9
10
11
12

HttpEventは複数のデータ型の定義となる。コードを見てみるとすぐわかる。

type HttpEvent<T> = HttpSentEvent | HttpHeaderResponse | HttpResponse<T> | HttpProgressEvent | HttpUserEvent<T>;
1

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) => {},
        () => {}
      );
  }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17