Самый распространенный способ получить данные из web служб — это через Http. И в этой статье мы посмотрим как это можно сделать Http-запрос в Angular 4.3 через новый HttpClient.
Начиная с версии Angular 4.3 появился новый HttpClient. В этой статье описывается только новый клиент.
Angular > 4.3
HttpClientModule
HttpClient
Angular <= 4.3
HttpModule
Http
Замечание: Старая реализация http все еще существует в 4.3, поэтому убедитесь, что вы используете правильный.
Импорт модуля
Прежде всего, нам нужно импортировать HttpClientModule в родительский модуль. Если у вас новый Angular проект, то это скорее всего будет AppModule. Чтобы импортировать модуль, просто добавьте его в раздел imports родительского модуля.
src/app/app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
// Убедитесь, что вы используете именно этот импорт, чтобы использовать новую версию модуля
import { HttpClientModule } from '@angular/common/http';
@NgModule({
declarations: [
AppComponent,
],
imports: [
BrowserModule,
HttpClientModule // Импортируем модуль
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Создаем простой сервис
Правильным подходом при построении Angular приложении считается вынос всех HTTP-запросов из компонентов в сервисы, чтобы сделать их полностью независимыми, а все запросы завернуть в отдельные сервисы. Потом например, при тестировании можно будет сделать mocke версию сервиса. Да и код при таком подходе получается более аккуратным.
Обычно для служб заводят отдельную папку в модуле, где же у вас будет сервис, полностью ваш выбор.
У каждой службы есть определенная цель. Например, если мы хотим запросить фотографии кошек через http, то наша служба будет называться CatPictureService. Основа сервиса будет выглядеть примерно так:
src/app/services/catPicture.service.ts
import { Injectable } from '@angular/core';
@Injectable()
export class CatPictureService {
constructor() { }
}
Чтобы сделать http-запрос из нашего сервиса, нам нужен Angular HttpClient. Мы можем его легко добавить посредством инъекции зависимостей (dependency injection, DI).
src/app/services/catPicture.service.ts
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
@Injectable()
export class CatPictureService {
constructor(private httpClient: HttpClient) { }
}
Создание запроса
Чтобы сделать запрос, мы добавим новый метод в наш сервис. Методы обычно называются глаголом. На данный момент мы назовем метод «get». Он получит по URL-адресу фотографию кота.
public get(url: string): Observable<any>{
return this.httpClient.get(url);
}
Как вы можете видеть, Angular HttpClient довольно прост. Все, что нам нужно сделать, это вызвать метода get и передать ему url. Данный метод get возвращает объект Observable. Этот класс является частью библиотеки rxjs, которая используется во многих местах Angular'а. Одним из примеров использования является HttpClient.
Подобно обещанию(Promise), наблюдатель(Observable) не содержит в себе сразу значения. Вместо этого у него есть метод подписки(subscribe), где мы можем зарегистрировать обратный вызов(callback). Этот callback вызывается, как только результат будет доступен. Помимо обещания, Observable может вернуть более одного значения. Вы можете вернуть себе поток результатов. Но это не имеет значения для этого урока. В нашем случае Observable возвращает только одно значение.
Подписка на наблюдатели (Subscribing to Observables)
Итак, как мы можем подписаться на наблюдателя, которого вернул наш новый метод, чтобы получить фактическое значение? Это довольно легко. Мы просто вызываем метод подписки subscribe и регистрируем один (или два) метода для обратного вызова. Первый обратный вызов вызывается, когда результат доступен. Он получает результат как параметр. Второй обратный вызов запускается, когда с запросом возникает какая-либо ошибка. В качестве параметра он получает объект ошибки.
Вот как это выглядит в коде. Я составил экземпляр picService. Вам необходимо запровайдить и запросить этот сервис самостоятельно.
this.picService.get('http://anyurl.com').subscibe(value =>{
// value - результат
},
error => {
// error - объект ошибки
});
ПРИМЕЧАНИЕ. Вам всегда нужно подписываться(вызывать метод subscibe) в противном случае запрос не будет сделан!
Опции
Http поддерживает огромный выбор различных параметров, заголовков и форматов. Чтобы сделать все эти разные запросы от HttpClient, все его методы берут в качестве необязательного параметра объект options.
Форматы ответов
Начиная с Angular 4.3, формат ответа по умолчанию — JSON. Это делает использование HttpClient очень простым. Вам больше не нужно анализировать ответ вручную. Angular делает это для вас.
Хотя JSON является наиболее распространенным форматом ответов, есть много примеров, когда вы не можете использовать JSON. Например, при запросе фотографий кошечек.
Чтобы избежать автоматического анализа ответа, мы можем определить заранее свойство responseType через объекта options.
{ responseType: 'text' }
Заголовки
Чтобы добавить заголовки в запрос, мы используем свойство headers объекта options. Для этого нам нужен объект HttpHeaders. Он содержит все наши определения заголовков. Не используйте объект Headers, так как он является частью старого клиента Http.
const headers = new HttpHeaders({ 'Content-Type': 'application/json' });
const options = { headers: headers };
URL параметры
Мы также можем определить параметры url внутри объекта options. Для этого нам нужно создать объект HttpParams. Таким образом, нам не нужно добавлять их в строку url.
const params = new HttpParams().set('id', '3');
const options = {params: params};
Отслеживание прогресса
Одной из новых особенностей нового HttpClient является возможность отслеживание хода выполнения запроса. Например, если вы хотите загрузить большой файл, вы, вероятно, хотите сообщить о ходе загрузки пользователю.
Для этого нам нужно разбить наш запрос в отдельном объекте запроса. Чтобы получить прогресс, нам нужно установить для свойства reportProgress значение true.
import { Observable } from 'rxjs/Observable';
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { HttpRequest } from "@angular/common/http";
import { Subject } from "rxjs/Subject";
import { HttpEventType } from "@angular/common/http";
import { HttpResponse } from "@angular/common/http";
public post(url: string, file: File): Observable<number>{
var subject = new Subject<number>()
const req = new HttpRequest('POST', url, file, {
reportProgress: true,
});
this.httpClient.request(req).subscribe(event => {
if (event.type === HttpEventType.UploadProgress) {
const percentDone = Math.round(100 * event.loaded / event.total);
subject.next(percentDone);
} else if (event instanceof HttpResponse) {
subject.complete();
}
});
return subject.asObservable();
}
Метод post возвращает объект наблюдателя(Observable), представляющий ход загрузки.
Перехватчики (Interceptors)
Еще одной замечательной особенностью нового HttpClient являются перехватчики. В некоторых случаях вам может потребоваться изменить запрос до того, как он попадет на сервер. Или вы хотите изменить каждый ответ. Вы можете сделать это с помощью Interceptor. Это своего рода промежуточное ПО между http-api и фактическим запросом.
Одним из распространенных вариантов использования может быть аутентификация. Чтобы получить ответ с сервера, вам часто нужно добавить какой-то механизм проверки подлинности в запрос. Конечно, вы можете просто изменить заголовок авторизации в своей службе. Но эта задача всегда одна и та же, не так ли? Это всегда один и тот же протокол. Он никогда не меняется, даже между приложением. Так почему бы нам не написать логику один раз и повторно использовать ее повсюду?
Определение перехватчика
Подобно сервису, перехватчик является инъекционным(Injectable).
import { Observable } from 'rxjs/Observable';
import {Injectable} from '@angular/core';
import {HttpEvent, HttpInterceptor, HttpHandler, HttpRequest} from '@angular/common/http';
@Injectable()
export class AuthenticationInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
req.headers.append('Authorization', '<SOME-TOKEN>')
return next.handle(req);
}
}
Поскольку приложение может иметь несколько перехватчиков, они организованы в цепочку. Первый элемент вызывается Angular'ом. Впоследствии мы несем ответственность за передачу запроса следующему перехватчику. Чтобы это сделать, мы вызываем метод handle следующего элемента в цепочке, как только мы закончим.
Прежде чем мы это сделаем, мы можем изменить запрос, как нам нравится. Например, добавьте токен в заголовок авторизации.
Этот пример отнюдь не является полным или многоразовым. Но это должно дать вам представление о том, как можно отсюда продолжить.
Представление перехватчиков
Так же, как сервисы, мы также должны запровайдить перехватчики. Для этого ваш родительский модуль (AppModule) должен выглядеть примерно так:
src/app/app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
// Убедитесь, что вы используете именно этот импорт, чтобы использовать новую версию модуля
import { HttpClientModule } from '@angular/common/http';
import { HTTP_INTERCEPTORS } from '@angular/common/http';
@NgModule({
declarations: [
AppComponent,
],
imports: [
BrowserModule,
HttpClientModule // импорт модуля
],
providers: [{
provide: HTTP_INTERCEPTORS,
useClass: AuthenticationInterceptor,
multi: true,
}],
bootstrap: [AppComponent]
})
export class AppModule { }
Итого
Теперь мы знаем, как можем отправлять или получать внешние данные через http. Мы также узнали, какие варианты мы можем использовать и как отслеживать прогресс.
Надеюсь, вам понравилась эта статья.
Комментарии (17)
xGromMx
24.08.2017 13:45Который раз вижу, что используют сабжект неправильно! Зачем вы такое делаете?
Hooter Автор
24.08.2017 14:44Извините, но что Вы имеете в виду? Статья содержит базовые примеры с акцентами на изменения, которые привнесли в HttpClient. И эти примеры мало чем отличаются от официальной документации.
kemsky
24.08.2017 20:51Первая версия была очень ограниченной, в итоге пришлось на писать свою обертку над XHR, со всеми интерсепторами, терпимой поддержкой аплоада и прочим. А эта вторая версия умеет загружать файлы (с мониторингом прогресса)?
Hooter Автор
24.08.2017 21:06В статье как раз пример мониторинга прогресса с загрузкой на сервер HttpEventType.UploadProgress, при загрузке с сервера было бы HttpEventType.DownloadProgress.
Пока что единственное с чем еще не разобрался, это как сделать фейковый бекенд, на Http все делалось через MockBackend. Здесь же можно легко протестировать, а вот отдельный бекенд как то не собирается.kemsky
24.08.2017 21:54Похоже они решили вместо того, чтобы просто принимать
Subject<HttpEvent>
, дать низкоуровневый апиrequest
, который выдает все события. Это лучше чем ничего.
Правда
HttpEventType
не соответсвует XHR событиям, как понять что случился таймаут или ошибка соединения? Какого типа свалится еррор в сабскрайбера?
Еще один вопрос, клиенту попрежнему нельзя сказать какие статус коды являются успешными, а какие нет?
nomoreload
24.08.2017 22:54Можете подписаться на все события, мониторить окончание запроса и в зависимости от результата смапить его в результат, либо в ошибку. Ну то есть в зависимости от требуемого кейса, успешным может быть не только 200, но и 201, например.
DAiMor
24.08.2017 23:59А с какой целью нужен фейковый бекенд? Для использования в тестах?
В этой статье вроде рабочий пример тестов с новым HttpClient.
Ну и конечно поиск по github. Отличный источник готовых примеров, с учетом распространнености Angular, то довольно быстро появляются примеры.Hooter Автор
25.08.2017 11:05Особенность работы HttpTestingController в том что он вызывается сразу после самого запроса. Для тестирования так как раз и нужно. А для моих целей не совсем. Мне как раз бы лучше развести это все дело в разные места.
А я использую фейковый для эмуляции работы бекенда, пока нет самого бекенда, или пока он не поднят на environment. При этом учитывая, что фронт будет загружен на amazon и там же как то будет, потом, бекенд, то собирать временный бекенд из чего-то другого смысла нет.
Вот на старом было удобно сварганить быстренько API бекенда, чтобы показать работу системы, и по готовности бекенда переводить UI на нормальную работу, исправляя конфиг с endPoint'ами.DAiMor
25.08.2017 11:40Ok, a in-memory-web-api чем не подходит? Я симуляцию бека с ним делал, в том числе там есть поодержка простеньких фильтров в запросе.
Hooter Автор
25.08.2017 12:34Он как раз на Http, а не на HttpClient. На Http можно и проще сделать. Простой пример fake-backend. Сейчас пробую через CachingInterceptor собрать для HttpClient.
MOTORIST
25.08.2017 13:33Мне больше нравится json-server. На in-memory-web-api не смог настроить singular routes.
x07
Описано все тоже самое, что и в старом. Чем новый лучше старого?
nomoreload
Interceptor'ы- киллер фича. По сути middleware, который работают как на отправку, так и на получение. Отличная штука. Не надо больше сервисы-обвязки городить для того, чтобы токены в заголовки проставлять.
DAiMor
Раньше это тоже вполне работало, в частности если нужно было токен добавить.
иApx
А в 1.х ангуляре их что-ли не было?
Poccomaxa_zt
Можете заглянуть по этой ссылке — github.com/angular/angular/commit/37797e2.
Кратко — избавляет Вас от необходимости использовать сервисы обёртки, которые разработчики пишут каждый по своему…
Из часто используемого — благодаря релизу теперь у Вас есть тип ответа JSON по умолчанию, и возможность обработки запросов (добавление хедеров, обратка ошибок и тд) более структурированно…
Apx
Описано как в документации…