HTTP リクエストを行う

HttpClient には、データの読み込みとサーバーへの変更の適用に使用されるさまざまなHTTP動詞に対応するメソッドがあります。各メソッドはRxJS Observable を返し、これはサブスクライブされるとリクエストを送信し、サーバーが応答すると結果を発行します。

NOTE: HttpClient によって作成された Observable は、何度でもサブスクライブでき、各サブスクライブごとに新しいバックエンドリクエストが行われます。

リクエストメソッドに渡されるオプションオブジェクトを通じて、リクエストのさまざまなプロパティと返されるレスポンスタイプを調整できます。

JSON データの取得

バックエンドからデータを取得するには、多くの場合、HttpClient.get() メソッドを使用してGETリクエストを行う必要があります。このメソッドは、2つの引数を取ります。1つ目は、フェッチする文字列のエンドポイントURL、2つ目はリクエストを構成するためのオプションオブジェクトです(省略可能)。

たとえば、HttpClient.get() メソッドを使用して、仮説上のAPIから構成データを取得するには、次のようになります。

http.get<Config>('/api/config').subscribe(config => {  // 構成を処理します。});

サーバーによって返されるデータが Config 型であることを指定するジェネリック型引数に注意してください。この引数は省略可能であり、省略すると、返されるデータは Object 型になります。

TIP: 不確実な構造のデータや undefined または null の値を扱う場合、レスポンスタイプとして Object の代わりに unknown 型を使用することを検討してください。

CTIRICAL: リクエストメソッドのジェネリック型は、サーバーによって返されるデータに関するアサーションです。HttpClient は、実際の戻り値データがこの型と一致することを検証しません。

他のタイプのデータの取得

デフォルトでは、HttpClient はサーバーがJSONデータを返すことを想定しています。JSON以外のAPIと対話する場合、HttpClient にリクエストを行うときに期待されるレスポンス型と返す型を伝えることができます。これは、responseType オプションを使用して行います。

responseType 返されるレスポンス型
'json' (デフォルト) 指定されたジェネリック型の JSON データ
'text' 文字列データ
'arraybuffer' 応答バイトの生のデータを格納した ArrayBuffer
'blob' Blob インスタンス

たとえば、HttpClient.jpeg イメージの生のバイトを ArrayBuffer にダウンロードするように依頼できます。

http.get('/images/dog.jpg', {responseType: 'arraybuffer'}).subscribe(buffer => {  console.log('画像は ' + buffer.byteLength + ' バイトです');});

responseType のリテラル値

responseType の値は、HttpClient によって返される型に影響を与えるため、string 型ではなく、リテラル型である必要があります。

これは、リクエストメソッドに渡されるオプションオブジェクトがリテラルオブジェクトの場合に自動的に発生しますが、リクエストオプションを変数やヘルパーメソッドに抽出している場合は、responseType: 'text' as const のように明示的にリテラルとして指定する必要があるかもしれません。

サーバーの状態を変更する

変更を伴うサーバーAPIは多くの場合、新しい状態または行うべき変更を指定したリクエストボディを使用してPOSTリクエストを行う必要があります。

HttpClient.post() メソッドは get() と同様に動作し、オプションの前に body 引数を追加で受け取ります。

http.post<Config>('/api/config', newConfig).subscribe(config => {  console.log('更新された構成:', config);});

さまざまなタイプの値をリクエストの body として提供でき、HttpClient はそれに応じてシリアル化します。

body シリアル化されるもの
文字列 プレーンテキスト
数値、ブール値、配列、またはプレーンオブジェクト JSON
ArrayBuffer バッファーからの生データ
Blob Blob のコンテンツタイプを使用した生データ
FormData multipart/form-data エンコードされたデータ
HttpParams または URLSearchParams application/x-www-form-urlencoded 形式の文字列

IMPORTANT: 変更リクエスト Observable.subscribe() することを忘れないでください。そうしないと、リクエストは実際に発行されません。

URL パラメータの設定

リクエストURLに含めるべきリクエストパラメータは、params オプションを使用して指定します。

オブジェクトリテラルを渡すことは、URLパラメータを構成するための最も簡単な方法です。

http.get('/api/config', {  params: {filter: 'all'},}).subscribe(config => {  // ...});

あるいは、パラメータの構築やシリアル化をより細かく制御する必要がある場合は、HttpParams のインスタンスを渡します。

IMPORTANT: HttpParams のインスタンスは変更不可能であり、直接変更できません。代わりに、append() などの変更メソッドは、変更が適用された新しい HttpParams のインスタンスを返します。

const baseParams = new HttpParams().set('filter', 'all');http.get('/api/config', {  params: baseParams.set('details', 'enabled'),}).subscribe(config => {  // ...});

HttpParams を、HttpClient がパラメータをURLにエンコードする方法を決定するカスタム HttpParameterCodec でインスタンス化できます。

リクエストヘッダーの設定

リクエストに含めるべきリクエストヘッダーは、headers オプションを使用して指定します。

オブジェクトリテラルを渡すことは、リクエストヘッダーを構成するための最も簡単な方法です。

http.get('/api/config', {  headers: {    'X-Debug-Level': 'verbose',  }}).subscribe(config => {  // ...});

あるいは、ヘッダーの構築をより細かく制御する必要がある場合は、HttpHeaders のインスタンスを渡します。

IMPORTANT: HttpHeaders のインスタンスは変更不可能であり、直接変更できません。代わりに、append() などの変更メソッドは、変更が適用された新しい HttpHeaders のインスタンスを返します。

const baseHeaders = new HttpHeaders().set('X-Debug-Level', 'minimal');http.get<Config>('/api/config', {  headers: baseHeaders.set('X-Debug-Level', 'verbose'),}).subscribe(config => {  // ...});

サーバーレスポンスイベントとの対話

便宜上、HttpClient はデフォルトでサーバーによって返されるデータ(レスポンスボディ)の Observable を返します。場合によっては、特定のレスポンスヘッダーを取得するなど、実際のレスポンスを調べる必要がある場合もあります。

レスポンス全体にアクセスするには、observe オプションを 'response' に設定します。

http.get<Config>('/api/config', {observe: 'response'}).subscribe(res => {  console.log('レスポンスステータス:', res.status);  console.log('ボディ:', res.body);});

observe のリテラル値

observe の値は、HttpClient によって返される型に影響を与えるため、string 型ではなく、リテラル型である必要があります。

これは、リクエストメソッドに渡されるオプションオブジェクトがリテラルオブジェクトの場合に自動的に発生しますが、リクエストオプションを変数やヘルパーメソッドに抽出している場合は、observe: 'response' as const のように明示的にリテラルとして指定する必要があるかもしれません。

生の進捗イベントを受信する

HttpClient は、レスポンスボディとレスポンスオブジェクトに加えて、リクエストライフサイクルの特定の時点に対応する生のイベントのストリームを返せます。これらのイベントには、リクエストが送信されたとき、レスポンスヘッダーが返されたとき、ボディが完了したときなどが含まれます。これらのイベントには、大きなリクエストボディとレスポンスボディのアップロードとダウンロードの状態を報告する進捗イベントも含まれる場合があります。

進捗イベントは、デフォルトでは無効になっています(パフォーマンス上のコストがかかるため)が、reportProgress オプションを使用して有効にできます。

NOTE: HttpClient のオプションの fetch 実装は、アップロードの進捗イベントを報告しません。

イベントストリームを観察するには、observe オプションを 'events' に設定します。

http.post('/api/upload', myData, {  reportProgress: true,  observe: 'events',}).subscribe(event => {  switch (event.type) {    case HttpEventType.UploadProgress:      console.log('Uploaded ' + event.loaded + ' out of ' + event.total + ' bytes');      break;    case HttpEventType.Response:      console.log('Finished uploading!');      break;  }});

observe のリテラル値

observe の値は、HttpClient によって返される型に影響を与えるため、string 型ではなく、リテラル型である必要があります。

これは、リクエストメソッドに渡されるオプションオブジェクトがリテラルオブジェクトの場合に自動的に発生しますが、リクエストオプションを変数やヘルパーメソッドに抽出している場合は、observe: 'events' as const のように明示的にリテラルとして指定する必要があるかもしれません。

イベントストリームで報告される各 HttpEvent には、イベントを表す type があります。

type イベントの意味
HttpEventType.Sent リクエストがサーバーに送信されました
HttpEventType.UploadProgress リクエストボディのアップロードの進捗状況を報告する HttpUploadProgressEvent
HttpEventType.ResponseHeader レスポンスのヘッダーが受信されました。ステータスとヘッダーが含まれています
HttpEventType.DownloadProgress レスポンスボディのダウンロードの進捗状況を報告する HttpDownloadProgressEvent
HttpEventType.Response レスポンス全体が受信されました。レスポンスボディが含まれています
HttpEventType.User Http インターセプターからのカスタムイベント

リクエスト失敗の処理

HTTPリクエストは、次の3つの方法で失敗する可能性があります。

  • ネットワークエラーまたは接続エラーにより、リクエストがバックエンドサーバーに到達できない場合があります。
  • timeoutオプションが設定されている場合に、リクエストが時間内に応答しませんでした。
  • バックエンドがリクエストを受け取りますが、処理に失敗し、エラーレスポンスを返す場合があります。

HttpClient は、上記のすべての種類のエラーを HttpErrorResponse に捕捉し、Observable のエラーチャネルを通じて返します。ネットワークエラーとタイムアウトエラーの status コードは 0 で、errorProgressEvent のインスタンスです。バックエンドエラーの status コードは、バックエンドによって返された失敗したコードであり、error はエラーレスポンスです。レスポンスを調べて、エラーの原因とエラーを処理するための適切なアクションを特定します。

RxJS ライブラリ は、エラー処理に役立つ演算子をいくつか提供しています。

catchError 演算子を使用して、エラーレスポンスをUI用の値に変換できます。この値は、UIにエラーページまたは値を表示し、必要に応じてエラーの原因を捕捉します。

ネットワークの中断など、一時的なエラーにより、予期せずリクエストが失敗することがあります。リクエストを再試行するだけで成功する場合があります。RxJSは、特定の条件下で失敗した Observable に自動的に再購読する、複数の再試行演算子を提供しています。たとえば、retry() 演算子は、指定された回数だけ自動的に再サブスクライブを試みます。

タイムアウト

リクエストにタイムアウトを設定するには、他のリクエストオプションと一緒に timeout オプションをミリ秒数に設定できます。バックエンドリクエストが指定された時間内に完了しない場合、リクエストは中止され、エラーが発行されます。

NOTE: タイムアウトは、バックエンドHTTPリクエスト自体にのみ適用されます。リクエスト処理チェーン全体のタイムアウトではありません。したがって、このオプションは、インターセプターによって導入される遅延の影響を受けません。

http.get('/api/config', {  timeout: 3000,}).subscribe({  next: config => {    console.log('設定の取得に成功:', config);  },  error: err => {    // リクエストがタイムアウトした場合、エラーが発行されます。  }});

高度な fetch オプション

withFetch() プロバイダーを使用する場合、Angularの HttpClient は、パフォーマンスとユーザー体験を改善できる高度なfetch APIオプションへのアクセスを提供します。これらのオプションは、fetchバックエンドを使用する場合にのみ利用できます。

Fetch オプション

以下のオプションは、fetchバックエンドを使用する際のリクエスト動作を細かく制御します。

Keep-alive 接続

keepalive オプションにより、リクエストはそれを開始したページよりも長く存続できます。これは、ユーザーがページから移動した場合でも完了する必要がある分析やログリクエストに特に有用です。

http.post('/api/analytics', analyticsData, {  keepalive: true}).subscribe();

HTTP キャッシュ制御

cache オプションは、リクエストがブラウザのHTTPキャッシュとどのように相互作用するかを制御し、繰り返しリクエストのパフォーマンスを大幅に改善できます。

//  新鮮度に関係なくキャッシュされたレスポンスを使用http.get('/api/config', {  cache: 'force-cache'}).subscribe(config => {  // ...});// 常にネットワークから取得、キャッシュをバイパスhttp.get('/api/live-data', {  cache: 'no-cache'}).subscribe(data => {  // ...});// キャッシュされたレスポンスのみを使用、キャッシュにない場合は失敗http.get('/api/static-data', {  cache: 'only-if-cached'}).subscribe(data => {  // ...});

Core Web Vitals のためのリクエスト優先度

priority オプションを使用すると、リクエストの相対的な重要度を示すことができ、ブラウザがより良いCore Web Vitalsスコアのためにリソースの読み込みを最適化するのに役立ちます。

// 重要なリソースの高優先度http.get('/api/user-profile', {  priority: 'high'}).subscribe(profile => {  // ...});// 重要でないリソースの低優先度http.get('/api/recommendations', {  priority: 'low'}).subscribe(recommendations => {  // ...});// 自動優先度 (デフォルト) はブラウザーが決定http.get('/api/settings', {  priority: 'auto'}).subscribe(settings => {  // ...});

利用可能な priority 値:

  • 'high': 高優先度、早期に読み込まれる(例:重要なユーザーデータ、above-the-foldコンテンツ)
  • 'low': 低優先度、リソースが利用可能なときに読み込まれる(例:分析、プリフェッチデータ)
  • 'auto': ブラウザがリクエストコンテキストに基づいて優先度を決定(デフォルト)

TIP: Largest Contentful Paint (LCP) に影響するリクエストには priority: 'high' を使用し、初期ユーザー体験に影響しないリクエストには priority: 'low' を使用してください。

リクエストモード

mode オプションは、リクエストがクロスオリジンリクエストを処理する方法を制御し、レスポンスタイプを決定します。

// 同一オリジンリクエストのみhttp.get('/api/local-data', {  mode: 'same-origin'}).subscribe(data => {  // ...});// CORS が有効なクロスオリジンリクエストhttp.get('https://api.external.com/data', {  mode: 'cors'}).subscribe(data => {  // ...});// シンプルなクロスオリジンリクエストの No-CORS モードhttp.get('https://external-api.com/public-data', {  mode: 'no-cors'}).subscribe(data => {  // ...});

利用可能な mode 値:

  • 'same-origin': 同一オリジンリクエストのみを許可、クロスオリジンリクエストは失敗
  • 'cors': CORSでクロスオリジンリクエストを許可(デフォルト)
  • 'no-cors': CORSなしでシンプルなクロスオリジンリクエストを許可、レスポンスは不透明

TIP: クロスオリジンに行くべきでない機密リクエストには mode: 'same-origin' を使用してください。

リダイレクト処理

redirect オプションは、サーバーからのリダイレクトレスポンスを処理する方法を指定します。

// リダイレクトを自動的に追跡(デフォルトの動作)http.get('/api/resource', {  redirect: 'follow'}).subscribe(data => {  // ...});// 自動リダイレクトを防ぐhttp.get('/api/resource', {  redirect: 'manual'}).subscribe(response => {  // リダイレクトを手動で処理});// リダイレクトをエラーとして扱うhttp.get('/api/resource', {  redirect: 'error'}).subscribe({  next: data => {    // 成功レスポンス  },  error: err => {    // リダイレクトレスポンスはこのエラーハンドラーをトリガーします  }});

利用可能な redirect 値:

  • 'follow': リダイレクトを自動的に追跡(デフォルト)
  • 'error': リダイレクトをエラーとして扱う
  • 'manual': リダイレクトを自動的に追跡せず、リダイレクトレスポンスを返す

TIP: カスタムロジックでリダイレクトを処理する必要がある場合は redirect: 'manual' を使用してください。

認証情報の処理

credentials オプションは、Cookie、認証ヘッダー、その他の認証情報がクロスオリジンリクエストと一緒に送信されるかどうかを制御します。これは認証シナリオで特に重要です。

// クロスオリジンリクエストに認証情報を含めるhttp.get('https://api.example.com/protected-data', {  credentials: 'include'}).subscribe(data => {  // ...});// 認証情報を送信しない(クロスオリジンのデフォルト)http.get('https://api.example.com/public-data', {  credentials: 'omit'}).subscribe(data => {  // ...});// 同一オリジンリクエストのみに認証情報を送信http.get('/api/user-data', {  credentials: 'same-origin'}).subscribe(data => {  // ...});// withCredentials は credentials 設定を上書きしますhttp.get('https://api.example.com/data', {  credentials: 'omit',        // これは無視されます  withCredentials: true       // これにより credentials: 'include' が強制されます}).subscribe(data => {  // credentials: 'omit' にもかかわらず、リクエストは認証情報を含みます});// レガシーアプローチ(まだサポートされています)http.get('https://api.example.com/data', {  withCredentials: true}).subscribe(data => {  // credentials: 'include' と同等});

IMPORTANT: withCredentials オプションは credentials オプションより優先されます。両方が指定されている場合、明示的な credentials 値に関係なく、withCredentials: true は常に credentials: 'include' になります。

利用可能な credentials 値:

  • 'omit': 認証情報を送信しない
  • 'same-origin': 同一オリジンリクエストのみに認証情報を送信(デフォルト)
  • 'include': クロスオリジンリクエストでも常に認証情報を送信

TIP: CORSをサポートする異なるドメインに認証Cookieやヘッダーを送信する必要がある場合は credentials: 'include' を使用してください。混乱を避けるため、credentialswithCredentials オプションを混在させることは避けてください。

Http Observable

HttpClient の各リクエストメソッドは、要求されたレスポンス型の Observable を構築して返します。これらの Observable の仕組みを理解することは、HttpClient を使用する場合に重要です。

HttpClient は、RxJSが「コールド」と呼ぶ Observable を生成します。つまり、Observable がサブスクライブされるまでは、実際のリクエストは行われません。リクエストが実際にサーバーに送信されるのは、そのときだけです。同じ Observable を複数回購読すると、複数のバックエンドリクエストがトリガーされます。各サブスクライブは独立しています。

Tip: HttpClientObservable は、実際のリクエストのブループリントと考えることができます。

いったんサブスクライブされると、アン購読すると、進行中のリクエストが中止されます。これは、Observableasync パイプを使用してサブスクライブされている場合に非常に便利です。ユーザーが現在のページから移動すると、リクエストが自動的にキャンセルされます。さらに、ObservableswitchMap などのRxJSコンビネータと使用する場合、このキャンセルによって、古いリクエストがすべてクリーンアップされます。

レスポンスが返されると、HttpClient からの Observable は通常完了します(ただし、インターセプターがこれに影響を与える可能性があります)。

自動完了のため、HttpClient のサブスクライブがクリーンアップされなくても、通常はメモリリークのリスクはありません。ただし、他の非同期操作と同様に、サブスクライブを使用しているコンポーネントが破棄されたときにサブスクライブをクリーンアップすることを強くお勧めします。そうしないと、サブスクライブコールバックが実行され、破棄されたコンポーネントと対話するときにエラーが発生する可能性があります。

Tip: async パイプまたは toSignal 演算子を使用して Observable に購読すると、サブスクライブが適切に破棄されます。

ベストプラクティス

HttpClient はコンポーネントから直接注入して使用できますが、一般的にはデータアクセスロジックを分離してカプセル化する、再利用できる注入可能なサービスを作成することをお勧めします。たとえば、この UserService は、IDでユーザーのデータをリクエストするロジックをカプセル化しています。

@Injectable({providedIn: 'root'})export class UserService {  private http = inject(HttpClient);  getUser(id: string): Observable<User> {    return this.http.get<User>(`/api/user/${id}`);  }}

コンポーネント内で、@ifasync パイプと組み合わせて、データの読み込みが完了した後にのみ、データのUIをレンダリングできます。

import { AsyncPipe } from '@angular/common';@Component({  imports: [AsyncPipe],  template: `    @if (user$ | async; as user) {      <p>名前: {{ user.name }}</p>      <p>経歴: {{ user.biography }}</p>    }  `,})export class UserProfileComponent {  userId = input.required<string>();  user$!: Observable<User>;  private userService = inject(UserService);  constructor(): void {    effect(() => {      this.user$ = this.userService.getUser(this.userId());    });  }}