Spec-Zone.ru › Angular

Создание HTTP-запросов

HttpClient предоставляет методы, соответствующие различным HTTP-глаголам, используемым для загрузки данных и внесения изменений на сервере. Каждый метод возвращает Observable RxJS, который при подписке отправляет запрос, а затем выдаёт результаты, когда сервер отвечает.

ПРИМЕЧАНИЕ: Observables, созданные с помощью HttpClient, можно подписывать любое количество раз, и для каждой подписки будет отправлен новый запрос на сервер.

С помощью объекта параметров, передаваемого методу запроса, можно настроить различные свойства запроса и тип возвращаемого ответа.

Получение данных JSON

Для получения данных с сервера часто требуется отправить GET-запрос с помощью метода HttpClient.get(). Этот метод принимает два аргумента: строку с URL конечной точки, из которой нужно получить данные, и необязательный объект параметров для настройки запроса.

Например, чтобы получить данные конфигурации из гипотетического API с помощью метода HttpClient.get():

http.get<Config>('/api/config').subscribe((config) => {
  // process the configuration.
});

Обратите внимание на аргумент обобщённого типа, указывающий, что данные, возвращаемые сервером, будут иметь тип Config. Этот аргумент необязателен. Если его не указать, возвращаемые данные будут иметь тип Object.

СОВЕТ: Если структура данных неизвестна и возможны значения undefined или null, рассмотрите возможность использовать тип unknown вместо Object в качестве типа ответа.

ВАЖНО: Обобщённый тип методов запросов — это утверждение о типе данных, возвращаемых сервером. HttpClient не проверяет, соответствует ли фактически возвращённое значение этому типу.

Получение данных других типов

По умолчанию HttpClient предполагает, что серверы возвращают данные 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('The image is ' + buffer.byteLength + ' bytes large');
});

Литеральное значение для responseType

Поскольку значение responseType влияет на тип, возвращаемый HttpClient, оно должно иметь литеральный тип, а не тип string.

Это происходит автоматически, если объект параметров, переданный методу запроса, является литеральным объектом. Но если вы извлекаете параметры запроса в переменную или вспомогательный метод, может потребоваться явно указать литеральный тип, например responseType: 'text' as const.

Изменение состояния сервера

Для API серверов, выполняющих изменения, часто требуется отправлять POST-запросы с телом запроса, в котором указано новое состояние или изменение, которое нужно внести.

Метод HttpClient.post() работает аналогично get() и принимает дополнительный аргумент body перед параметрами:

http.post<Config>('/api/config', newConfig).subscribe((config) => {
  console.log('Updated config:', config);
});

В качестве body запроса можно передавать значения самых разных типов; HttpClient сериализует их соответствующим образом:

Тип body Формат сериализации
string Обычный текст
number, boolean, array или plain object JSON
ArrayBuffer Исходные данные из буфера
Blob Исходные данные с типом содержимого Blob
FormData Данные в формате multipart/form-data
HttpParams или URLSearchParams Строка в формате application/x-www-form-urlencoded

ВАЖНО: Не забудьте .subscribe() на Observableах запросов, изменяющих данные, чтобы запрос действительно был отправлен.

Настройка параметров URL

Укажите параметры запроса, которые следует включить в URL запроса, с помощью параметра params.

Самый простой способ настроить параметры URL — передать литеральный объект:

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

Если вам нужно больше контроля над созданием или сериализацией параметров, можно передать экземпляр HttpParams.

ВАЖНО: Экземпляры HttpParams неизменяемы, поэтому их нельзя изменить напрямую. Вместо этого методы изменения, такие как append(), возвращают новый экземпляр HttpParams с применённым изменением.

const baseParams = new HttpParams().set('filter', 'all');

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

Можно создать экземпляр HttpParams с пользовательским HttpParameterCodec, определяющим, как HttpClient будет кодировать параметры в URL.

Пользовательское кодирование параметров

По умолчанию HttpParams использует встроенный HttpUrlEncodingCodec для кодирования и декодирования ключей и значений параметров.

Чтобы настроить кодирование и декодирование, можно предоставить собственную реализацию HttpParameterCodec.

import {HttpClient, HttpParams, HttpParameterCodec} from '@angular/common/http';
import {inject} from '@angular/core';

export class CustomHttpParamEncoder implements HttpParameterCodec {
  encodeKey(key: string): string {
    return encodeURIComponent(key);
  }

  encodeValue(value: string): string {
    return encodeURIComponent(value);
  }

  decodeKey(key: string): string {
    return decodeURIComponent(key);
  }

  decodeValue(value: string): string {
    return decodeURIComponent(value);
  }
}

export class ApiService {
  private http = inject(HttpClient);

  search() {
    const params = new HttpParams({
      encoder: new CustomHttpParamEncoder(),
    })
      .set('email', 'dev+alerts@example.com')
      .set('q', 'a & b? c/d = e');

    return this.http.get('/api/items', {params});
  }
}

Настройка заголовков запроса

Укажите заголовки, которые следует включить в запрос, с помощью параметра headers.

Самый простой способ настроить заголовки запроса — передать литеральный объект:

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

Если вам нужно больше контроля над созданием заголовков, можно передать экземпляр HttpHeaders.

ВАЖНО: Экземпляры 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('Response status:', res.status);
  console.log('Body:', res.body);
});

Литеральное значение для observe

Поскольку значение observe влияет на тип, возвращаемый HttpClient, оно должно иметь литеральный тип, а не тип string.

Это происходит автоматически, если объект параметров, переданный методу запроса, является литеральным объектом. Но если вы извлекаете параметры запроса в переменную или вспомогательный метод, может потребоваться явно указать литеральный тип, например observe: 'response' as const.

Получение необработанных событий о ходе выполнения

Помимо тела ответа или объекта ответа, HttpClient может возвращать поток необработанных событий, соответствующих определённым этапам жизненного цикла запроса. Эти события включают отправку запроса, получение заголовка ответа и завершение передачи тела. Они также могут включать события о ходе выполнения, сообщающие о ходе загрузки и выгрузки больших тел запросов или ответов.

События о ходе выполнения по умолчанию отключены (из-за связанных с ними затрат производительности), но их можно включить с помощью параметра reportProgress.

ПРИМЕЧАНИЕ: Стандартный fetch-бэкенд HttpClient не сообщает о событиях, отслеживающих ход отправки данных. Если приложению нужны такие события, настройте HttpClient, указав withXhr() в provideHttpClient(...).

Чтобы отслеживать поток событий, установите для параметра 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-запрос может завершиться ошибкой по трём причинам:

  • Сетевая ошибка или ошибка соединения может помешать запросу достичь сервера.
  • Запрос не получил ответ вовремя, если был задан параметр timeout.
  • Сервер может получить запрос, но не обработать его и вернуть ответ с ошибкой.

HttpClient перехватывает все перечисленные типы ошибок в виде HttpErrorResponse и передаёт его через канал ошибок Observable. Сетевые ошибки и ошибки тайм-аута имеют код status, равный 0, и свойство error, являющееся экземпляром ProgressEvent. Ошибки сервера содержат код status, возвращённый сервером, и ответ с ошибкой в свойстве error. Изучите ответ, чтобы определить причину ошибки и выбрать способ её обработки.

Библиотека RxJS предоставляет несколько операторов, которые могут пригодиться для обработки ошибок.

Оператор catchError позволяет преобразовать ответ с ошибкой в значение для пользовательского интерфейса. Это значение может указывать интерфейсу на необходимость отобразить страницу или сообщение об ошибке, а также при необходимости содержать причину ошибки.

Иногда временные ошибки, например перебои в сети, приводят к неожиданному сбою запроса, и повторная отправка запроса позволяет его выполнить успешно. RxJS предоставляет несколько операторов повторной попытки, которые при определённых условиях автоматически повторно подписываются на неудачный Observable. Например, оператор retry() автоматически попытается выполнить повторную подписку заданное число раз.

Тайм-ауты

Чтобы задать тайм-аут запроса, укажите для параметра timeout количество миллисекунд вместе с другими параметрами запроса. Если сервер не завершит обработку запроса за указанное время, запрос будет прерван и будет выдана ошибка.

ПРИМЕЧАНИЕ: Тайм-аут применяется только к самому HTTP-запросу к серверу. Он не ограничивает по времени всю цепочку обработки запроса. Поэтому на этот параметр не влияют задержки, создаваемые перехватчиками.

http
  .get('/api/config', {
    timeout: 3000,
  })
  .subscribe({
    next: (config) => {
      console.log('Config fetched successfully:', config);
    },
    error: (err) => {
      // If the request times out, an error will have been emitted.
    },
  });

Расширенные параметры fetch

HttpClient Angular поддерживает расширенные параметры Fetch API, которые помогают повысить производительность и улучшить взаимодействие с пользователем. Эти параметры доступны при использовании fetch-бэкенда, который используется по умолчанию.

Параметры fetch

Следующие параметры обеспечивают точную настройку поведения запросов при использовании fetch-бэкенда.

Поддержание соединений

Параметр keepalive позволяет запросу выполняться после закрытия страницы, с которой он был отправлен. Это особенно полезно для запросов аналитики и журналирования, которые должны завершиться, даже если пользователь покинет страницу.

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

Управление кэшированием HTTP

Параметр cache определяет взаимодействие запроса с HTTP-кэшем браузера, что может значительно повысить производительность при повторных запросах.

//  Use cached response regardless of freshness
http
  .get('/api/config', {
    cache: 'force-cache',
  })
  .subscribe((config) => {
    // ...
  });

// Always fetch from network, bypass cache
http
  .get('/api/live-data', {
    cache: 'no-cache',
  })
  .subscribe((data) => {
    // ...
  });

// Use cached response only, fail if not in cache
http
  .get('/api/static-data', {
    cache: 'only-if-cached',
  })
  .subscribe((data) => {
    // ...
  });

Приоритет запроса для Core Web Vitals

Параметр priority позволяет указать относительную важность запроса и помогает браузерам оптимизировать загрузку ресурсов для улучшения показателей Core Web Vitals.

// High priority for critical resources
http
  .get('/api/user-profile', {
    priority: 'high',
  })
  .subscribe((profile) => {
    // ...
  });

// Low priority for non-critical resources
http
  .get('/api/recommendations', {
    priority: 'low',
  })
  .subscribe((recommendations) => {
    // ...
  });

// Auto priority (default) lets the browser decide
http
  .get('/api/settings', {
    priority: 'auto',
  })
  .subscribe((settings) => {
    // ...
  });

Доступные значения priority:

  • 'high': высокий приоритет, ранняя загрузка (например, критически важные данные пользователя и содержимое, видимое без прокрутки)
  • 'low': низкий приоритет, загрузка при наличии свободных ресурсов (например, данные аналитики и предварительной загрузки)
  • 'auto': приоритет определяется браузером с учётом контекста запроса (по умолчанию)

СОВЕТ: Используйте priority: 'high' для запросов, влияющих на Largest Contentful Paint (LCP), и priority: 'low' для запросов, не влияющих на первоначальное взаимодействие пользователя с приложением.

Режим запроса

Параметр mode определяет, как запрос обрабатывает запросы к другим источникам и какой тип ответа возвращает.

// Same-origin requests only
http
  .get('/api/local-data', {
    mode: 'same-origin',
  })
  .subscribe((data) => {
    // ...
  });

// CORS-enabled cross-origin requests
http
  .get('https://api.external.com/data', {
    mode: 'cors',
  })
  .subscribe((data) => {
    // ...
  });

// No-CORS mode for simple cross-origin requests
http
  .get('https://external-api.com/public-data', {
    mode: 'no-cors',
  })
  .subscribe((data) => {
    // ...
  });

Доступные значения mode:

  • 'same-origin': разрешать только запросы к тому же источнику; запросы к другим источникам завершаются ошибкой
  • 'cors': разрешать запросы к другим источникам с помощью CORS (по умолчанию)
  • 'no-cors': разрешать простые запросы к другим источникам без CORS; ответ будет непрозрачным

СОВЕТ: Используйте mode: 'same-origin' для конфиденциальных запросов, которые ни при каких обстоятельствах не должны отправляться к другим источникам.

Обработка перенаправлений

Параметр redirect определяет, как обрабатывать ответы сервера с перенаправлением.

// Follow redirects automatically (default behavior)
http
  .get('/api/resource', {
    redirect: 'follow',
  })
  .subscribe((data) => {
    // ...
  });

// Prevent automatic redirects
http
  .get('/api/resource', {
    redirect: 'manual',
  })
  .subscribe((response) => {
    // Handle redirect manually
  });

// Treat redirects as errors
http
  .get('/api/resource', {
    redirect: 'error',
  })
  .subscribe({
    next: (data) => {
      // Success response
    },
    error: (err) => {
      // Redirect responses will trigger this error handler
    },
  });

Доступные значения redirect:

  • 'follow': автоматически переходить по перенаправлениям (по умолчанию)
  • 'error': считать перенаправления ошибками
  • 'manual': не переходить по перенаправлениям автоматически, возвращать ответ с перенаправлением

СОВЕТ: Используйте redirect: 'manual', если нужно обрабатывать перенаправления с помощью пользовательской логики.

Передача учётных данных

Параметр credentials определяет, передаются ли файлы cookie, заголовки авторизации и другие учётные данные в запросах к другим источникам. Это особенно важно при настройке аутентификации.

// Include credentials for cross-origin requests
http
  .get('https://api.example.com/protected-data', {
    credentials: 'include',
  })
  .subscribe((data) => {
    // ...
  });

// Never send credentials (default for cross-origin)
http
  .get('https://api.example.com/public-data', {
    credentials: 'omit',
  })
  .subscribe((data) => {
    // ...
  });

// Send credentials only for same-origin requests
http
  .get('/api/user-data', {
    credentials: 'same-origin',
  })
  .subscribe((data) => {
    // ...
  });

// withCredentials overrides credentials setting
http
  .get('https://api.example.com/data', {
    credentials: 'omit', // This will be ignored
    withCredentials: true, // This forces credentials: 'include'
  })
  .subscribe((data) => {
    // Request will include credentials despite credentials: 'omit'
  });

// Legacy approach (still supported)
http
  .get('https://api.example.com/data', {
    withCredentials: true,
  })
  .subscribe((data) => {
    // Equivalent to credentials: 'include'
  });

ВАЖНО: Параметр withCredentials имеет приоритет над параметром credentials. Если указаны оба параметра, withCredentials: true всегда приводит к значению credentials: 'include', независимо от явно заданного значения credentials.

Доступные значения credentials:

  • 'omit': никогда не передавать учётные данные
  • 'same-origin': передавать учётные данные только для запросов к тому же источнику (по умолчанию)
  • 'include': всегда передавать учётные данные, в том числе в запросах к другим источникам

СОВЕТ: Используйте credentials: 'include', если нужно передавать файлы cookie или заголовки аутентификации в другой домен, поддерживающий CORS. Не используйте одновременно параметры credentials и withCredentials, чтобы избежать путаницы.

Реферер

Параметр referrer позволяет управлять информацией о реферере, передаваемой вместе с запросом. Это важно для обеспечения конфиденциальности и безопасности.

// Send a specific referrer URL
http
  .get('/api/data', {
    referrer: 'https://example.com/page',
  })
  .subscribe((data) => {
    // ...
  });

// Use the current page as referrer (default behavior)
http
  .get('/api/analytics', {
    referrer: 'about:client',
  })
  .subscribe((data) => {
    // ...
  });

Параметр referrer принимает следующие значения:

  • Допустимая строка URL: задаёт конкретный URL реферера для отправки
  • Пустая строка '': информация о реферере не отправляется
  • 'about:client': используется реферер по умолчанию (URL текущей страницы)

СОВЕТ: Используйте referrer: '' для конфиденциальных запросов, если не хотите раскрывать URL страницы, с которой был отправлен запрос.

Политика реферера

Параметр referrerPolicy определяет, сколько информации о реферере — URL страницы, с которой отправляется запрос, — передаётся вместе с HTTP-запросом. Эта настройка влияет на конфиденциальность и аналитику и позволяет найти баланс между доступностью данных и безопасностью.

// Send no referrer information regardless of the current page
http
  .get('/api/data', {
    referrerPolicy: 'no-referrer',
  })
  .subscribe();

// Send origin only (e.g. https://example.com)
http
  .get('/api/analytics', {
    referrerPolicy: 'origin',
  })
  .subscribe();

Параметр referrerPolicy принимает следующие значения:

  • 'no-referrer' Никогда не отправлять заголовок Referer.
  • 'no-referrer-when-downgrade' Отправлять реферер для запросов к тому же источнику и защищённых запросов (HTTPS→HTTPS), но не передавать его при переходе с защищённого источника на менее защищённый (HTTPS→HTTP).
  • 'origin' Отправлять только источник реферера (схему, хост и порт), не указывая путь и параметры запроса.
  • 'origin-when-cross-origin' Отправлять полный URL для запросов к тому же источнику, а для запросов к другим источникам — только источник.
  • 'same-origin' Отправлять полный URL для запросов к тому же источнику, а для запросов к другим источникам не отправлять реферер.
  • 'strict-origin' Отправлять только источник и только если уровень защиты протокола не понижается (например, HTTPS→HTTPS). При понижении уровня защиты реферер не передаётся.
  • 'strict-origin-when-cross-origin' Поведение браузера по умолчанию. Отправлять полный URL для запросов к тому же источнику, источник — для запросов к другим источникам без понижения уровня защиты и не передавать реферер при понижении уровня защиты.
  • 'unsafe-url'Всегда отправлять полный URL (включая путь и параметры запроса). Это может раскрыть конфиденциальные данные, поэтому используйте этот вариант с осторожностью.

СОВЕТ: Для запросов, требующих повышенной конфиденциальности, отдавайте предпочтение строгим значениям, например 'no-referrer', 'origin' или 'strict-origin-when-cross-origin'.

Проверка целостности

Параметр integrity позволяет убедиться, что ответ не был изменён: для этого указывается криптографический хеш ожидаемого содержимого. Это особенно полезно при загрузке скриптов и других ресурсов из CDN.

// Verify response integrity with SHA-256 hash
http
  .get('/api/script.js', {
    integrity: 'sha256-ABC123...',
    responseType: 'text',
  })
  .subscribe((script) => {
    // Script content is verified against the hash
  });

ВАЖНО: Параметр integrity требует точного соответствия содержимого ответа указанному хешу. Если содержимое не совпадёт, запрос завершится сетевой ошибкой.

СОВЕТ: Используйте проверку целостности подресурсов при загрузке важных ресурсов из внешних источников, чтобы убедиться, что они не были изменены. Создавайте хеши с помощью таких инструментов, как openssl.

Observable HTTP

Каждый метод запроса HttpClient создаёт и возвращает Observable с запрошенным типом ответа. При использовании HttpClient важно понимать, как работают эти Observable.

HttpClient создаёт так называемые «холодные» Observable в терминологии RxJS: фактический запрос не выполняется, пока на Observable не подпишутся. Только после этого запрос отправляется на сервер. Повторная подписка на тот же Observable приведёт к выполнению нескольких запросов к серверу. Каждая подписка независима.

СОВЕТ: Можно считать Observable HttpClient шаблонами фактических запросов к серверу.

После подписки отмена подписки прервёт выполняющийся запрос. Это особенно полезно, если подписка на Observable осуществляется через конвейер async: при переходе пользователя с текущей страницы запрос будет автоматически отменён. Кроме того, если вы используете Observable вместе с оператором-комбинатором RxJS, например switchMap, отмена позволит очистить все устаревшие запросы.

После получения ответа Observable, возвращаемые HttpClient, обычно завершаются (хотя перехватчики могут повлиять на это).

Поскольку выполнение обычно завершается автоматически, подписки HttpClient обычно не приводят к утечкам памяти, даже если их не очищать. Тем не менее, как и в случае любой асинхронной операции, мы настоятельно рекомендуем очищать подписки при уничтожении использующего их компонента: в противном случае может выполниться функция обратного вызова подписки и возникнуть ошибки при попытке взаимодействия с уничтоженным компонентом.

СОВЕТ: Использование конвейера async или операции toSignal для подписки на Observable гарантирует корректную отмену подписок.

Рекомендации

Хотя HttpClient можно внедрять и использовать непосредственно в компонентах, мы обычно рекомендуем создавать повторно используемые внедряемые сервисы, которые изолируют и инкапсулируют логику доступа к данным. Например, этот UserService инкапсулирует логику запроса данных пользователя по его идентификатору:

@Service()
export class UserService {
  private http = inject(HttpClient);

  getUser(id: string): Observable<User> {
    return this.http.get<User>(`/api/user/${id}`);
  }
}

В компоненте можно объединить @if с конвейером async, чтобы отображать интерфейс с данными только после завершения их загрузки:

import {AsyncPipe} from '@angular/common';

@Component({
  imports: [AsyncPipe],
  template: `
    @if (user$ | async; as user) {
      <p>Name: {{ user.name }}</p>
      <p>Biography: {{ user.biography }}</p>
    }
  `,
})
export class UserProfile {
  userId = input.required<string>();
  user$!: Observable<User>;

  private userService = inject(UserService);

  constructor(): void {
    effect(() => {
      this.user$ = this.userService.getUser(this.userId());
    });
  }
}

Super-powered by Google ©2010–2025.
Code licensed under an MIT-style License. Documentation licensed under CC BY 4.0.
https://angular.dev/guide/http/making-requests

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API