Обработка ответов сервера

Angular предоставляет встроенный модуль для работы с серверными запросами — HttpClient, входящий в пакет @angular/common/http. Он построен поверх RxJS и полностью основан на Observable-потоках, что определяет ключевые принципы обработки ответов сервера.

Подключение выполняется через импорт HttpClientModule в корневом или функциональном модуле:

import { HttpClientModule } from '@angular/common/http';

@NgModule({
  imports: [HttpClientModule]
})
export class AppModule {}

HttpClient автоматически:

  • сериализует JSON-тела запросов;
  • десериализует JSON-ответы;
  • корректно работает с заголовками;
  • поддерживает типизацию ответов.

Базовая структура ответа сервера

Типичный HTTP-ответ включает:

  • тело ответа (body);
  • статус код (status);
  • заголовки (headers);
  • метаданные запроса.

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

this.http.get<User[]>('/api/users');

Для доступа ко всем данным:

this.http.get<User[]>('/api/users', {
  observe: 'response'
});

В этом случае возвращается HttpResponse<User[]>.


Типизация ответов

Angular поощряет строгую типизацию данных, приходящих с сервера. Тип указывается через generic-параметр:

interface User {
  id: number;
  name: string;
  email: string;
}

this.http.get<User>('/api/user/1');

Преимущества:

  • автодополнение в IDE;
  • раннее обнаружение ошибок;
  • документирование контракта API.

Типизация не выполняет runtime-проверку, но служит важным инструментом архитектурной стабильности.


Обработка успешных ответов

Так как HttpClient возвращает Observable, обработка выполняется через subscribe или RxJS-операторы.

Простейший вариант:

this.http.get<User[]>('/api/users')
  .subscribe(users => {
    this.users = users;
  });

Для более сложной логики применяются операторы pipe:

this.http.get<User[]>('/api/users').pipe(
  map(users => users.filter(u => u.active)),
  tap(users => console.log(users))
);

Распространённые операторы:

  • map — преобразование данных;
  • tap — побочные эффекты без изменения потока;
  • filter — фильтрация;
  • switchMap — подмена запроса другим.

Обработка ошибок сервера

Ошибки перехватываются через оператор catchError или второй аргумент subscribe.

this.http.get<User[]>('/api/users').subscribe({
  next: users => this.users = users,
  error: err => this.errorMessage = err.message
});

Более масштабируемый подход:

this.http.get<User[]>('/api/users').pipe(
  catchError(error => {
    if (error.status === 404) {
      return of([]);
    }
    return throwError(() => error);
  })
);

Тип ошибки — HttpErrorResponse, содержащий:

  • status — HTTP-код;
  • message — текст ошибки;
  • error — тело ответа сервера.

Централизованная обработка ошибок

Для исключения дублирования логики используется HTTP-interceptor.

@Injectable()
export class ErrorInterceptor implements HttpInterceptor {
  intercept(req: HttpRequest<any>, next: HttpHandler) {
    return next.handle(req).pipe(
      catchError((error: HttpErrorResponse) => {
        console.error(error);
        return throwError(() => error);
      })
    );
  }
}

Регистрация:

providers: [
  {
    provide: HTTP_INTERCEPTORS,
    useClass: ErrorInterceptor,
    multi: true
  }
]

Интерсепторы позволяют:

  • обрабатывать ошибки глобально;
  • логировать ответы;
  • модифицировать данные;
  • добавлять заголовки авторизации.

Работа со статус-кодами

Angular не навязывает логику обработки HTTP-статусов, но предоставляет всю информацию.

this.http.get('/api/data', { observe: 'response' })
  .subscribe(response => {
    if (response.status === 204) {
      // пустой ответ
    }
  });

На практике статус-коды обрабатываются:

  • в интерсепторах;
  • в сервисах;
  • через унифицированные response-объекты API.

Обработка нестандартных форматов

Если сервер возвращает не JSON, необходимо указать тип ответа:

this.http.get('/api/file', {
  responseType: 'blob'
});

Варианты responseType:

  • json (по умолчанию);
  • text;
  • blob;
  • arraybuffer.

Пример обработки текстового ответа:

this.http.get('/api/version', { responseType: 'text' })
  .subscribe(version => {
    this.version = version;
  });

Преобразование ответов сервера

Часто сервер возвращает данные в формате, неудобном для фронтенда. Преобразование выполняется на уровне сервиса:

this.http.get<any[]>('/api/users').pipe(
  map(data => data.map(item => ({
    id: item.user_id,
    name: item.full_name
  })))
);

Это:

  • изолирует API-контракт;
  • упрощает компоненты;
  • повышает тестируемость.

Кэширование и повторное использование ответов

RxJS позволяет кэшировать результаты запросов:

users$ = this.http.get<User[]>('/api/users').pipe(
  shareReplay(1)
);

shareReplay:

  • предотвращает повторные запросы;
  • сохраняет последний ответ;
  • особенно полезен для справочных данных.

Отмена запросов

Отписка от Observable автоматически отменяет HTTP-запрос:

const sub = this.http.get('/api/data').subscribe();
sub.unsubscribe();

В связке с switchMap отмена происходит автоматически при новом запросе:

this.search$.pipe(
  switchMap(term => this.http.get(`/api/search?q=${term}`))
);

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

Корректная обработка ответов сервера предполагает:

  • отсутствие HTTP-логики в компонентах;
  • единый сервис для каждого API-ресурса;
  • централизованные интерсепторы;
  • строгую типизацию.

Пример сервиса:

@Injectable({ providedIn: 'root' })
export class UserService {
  constructor(private http: HttpClient) {}

  getUsers() {
    return this.http.get<User[]>('/api/users');
  }

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

Компонент получает уже готовые данные без знания деталей HTTP-ответа.


Асинхронная обработка в шаблонах

Angular поддерживает работу с Observable напрямую в шаблонах через async pipe:

<ul>
  <li *ngFor="let user of users$ | async">
    {{ user.name }}
  </li>
</ul>

async:

  • автоматически подписывается;
  • автоматически отписывается;
  • корректно обрабатывает обновления данных.

Это снижает количество ручного кода и уменьшает риск утечек памяти.


Итоговая архитектурная роль обработки ответов

Обработка ответов сервера в Angular — это не просто получение данных, а полноценный слой приложения, включающий:

  • типизацию;
  • трансформацию;
  • обработку ошибок;
  • управление состоянием;
  • интеграцию с RxJS.

Грамотно выстроенная система обработки ответов делает приложение устойчивым к изменениям API, упрощает поддержку и масштабирование, а также формирует чёткое разделение ответственности между слоями приложения.