JavaRush /Курсы /Модуль 4: Node.js, Next.js и Angular /Выполнение HTTP-запросов: h...

Выполнение HTTP-запросов: http.get, post, put, delete

Модуль 4: Node.js, Next.js и Angular
17 уровень , 2 лекция
Открыта

1. Введение

Если бы Angular-приложение не умело делать HTTP-запросы, оно было бы похоже на чат-бота, который разговаривает сам с собой. В реальных приложениях мы почти всегда получаем или отправляем данные через интернет: будь то список товаров, регистрация пользователя или удаление котика из базы данных (надеюсь, только из базы!).

В Angular для работы с HTTP-запросами используется сервис HttpClient. Он позволяет отправлять запросы на сервер и получать ответы, а также работать с асинхронными потоками данных через RxJS.

Как подключить HttpClientModule

Перед тем как отправлять запросы, убедитесь, что вы подключили HttpClientModule в главный модуль приложения (обычно это app.module.ts):

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

@NgModule({
  imports: [
    // ... другие модули
    HttpClientModule
  ],
})
export class AppModule { }

Без этого Angular будет смотреть на ваши попытки отправить запросы как на попытки сварить борщ в чайнике.

Сервис HttpClient: базовые методы

HttpClient предоставляет методы для всех основных HTTP-запросов:

  • get<T>(url, options?) — получить данные.
  • post<T>(url, body, options?) — создать новый ресурс.
  • put<T>(url, body, options?) — заменить существующий ресурс.
  • delete<T>(url, options?) — удалить ресурс.

Все эти методы возвращают Observable, а значит, мы можем подписываться на результат, обрабатывать ошибки, использовать RxJS-операторы и получать данные, когда они придут.

2. Практика: создаём сервис для работы с сервером

Допустим, мы разрабатываем приложение для списка задач (todo-list). Давайте создадим сервис, который будет отправлять HTTP-запросы к серверу.

Шаг 1. Генерируем сервис

ng generate service services/todo

или коротко:

ng g s services/todo

Шаг 2. Пример сервиса с CRUD-методами

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

export interface Todo {
  id: number;
  title: string;
  completed: boolean;
}

@Injectable({
  providedIn: 'root'
})
export class TodoService {
  private apiUrl = 'https://jsonplaceholder.typicode.com/todos';

  constructor(private http: HttpClient) {}

  // Получить все задачи
  getTodos(): Observable<Todo[]> {
    return this.http.get<Todo[]>(this.apiUrl);
  }

  // Получить одну задачу по id
  getTodo(id: number): Observable<Todo> {
    return this.http.get<Todo>(`${this.apiUrl}/${id}`);
  }

  // Добавить новую задачу
  addTodo(todo: Partial<Todo>): Observable<Todo> {
    return this.http.post<Todo>(this.apiUrl, todo);
  }

  // Обновить задачу
  updateTodo(todo: Todo): Observable<Todo> {
    return this.http.put<Todo>(`${this.apiUrl}/${todo.id}`, todo);
  }

  // Удалить задачу
  deleteTodo(id: number): Observable<void> {
    return this.http.delete<void>(`${this.apiUrl}/${id}`);
  }
}

Примечание: Мы используем JSONPlaceholder — это бесплатный фейковый REST API для тестирования и обучения.

3. Использование сервиса в компоненте

Давайте теперь используем наш сервис в компоненте. Например, выведем список задач и добавим кнопку для удаления.

Шаг 1. Импортируем сервис

import { Component, OnInit } from '@angular/core';
import { TodoService, Todo } from '../services/todo.service';

@Component({
  selector: 'app-todo-list',
  template: `
    <h2>Список задач</h2>
    <ul>
      <li *ngFor="let todo of todos">
        <input type="checkbox" [checked]="todo.completed" />
        {{ todo.title }}
        <button (click)="delete(todo.id)">Удалить</button>
      </li>
    </ul>
    <button (click)="addSample()">Добавить задачу</button>
  `
})
export class TodoListComponent implements OnInit {
  todos: Todo[] = [];

  constructor(private todoService: TodoService) {}

  ngOnInit() {
    this.todoService.getTodos().subscribe((data) => {
      this.todos = data.slice(0, 10); // Ограничим до 10 задач для примера
    });
  }

  delete(id: number) {
    this.todoService.deleteTodo(id).subscribe(() => {
      this.todos = this.todos.filter(todo => todo.id !== id);
    });
  }

  addSample() {
    const newTodo: Partial<Todo> = { title: 'Новая задача', completed: false };
    this.todoService.addTodo(newTodo).subscribe((created) => {
      // Обычно сервер вернёт новый объект с id
      this.todos.unshift(created);
    });
  }
}

4. Разбор каждого метода

GET-запрос: получение данных

this.http.get<Todo[]>(this.apiUrl)
  • Используется для загрузки данных с сервера.
  • Возвращает Observable, который эмитит массив задач.
  • Можно обработать результат через subscribe() или через async pipe в шаблоне.

POST-запрос: создание ресурса

this.http.post<Todo>(this.apiUrl, todo)
  • Используется для создания новой задачи.
  • Вторым параметром указывается тело запроса (обычно объект).
  • Сервер возвращает созданный объект (часто с новым id).

PUT-запрос: обновление ресурса

this.http.put<Todo>(`${this.apiUrl}/${todo.id}`, todo)
  • Используется для полного обновления существующего ресурса.
  • URL содержит идентификатор задачи.
  • В теле запроса — новый объект задачи.

DELETE-запрос: удаление ресурса

this.http.delete<void>(`${this.apiUrl}/${id}`)
  • Используется для удаления задачи по id.
  • Обычно сервер возвращает пустой ответ (void).

5. Пример: форма для добавления задачи

Давайте добавим форму для создания новой задачи. Для простоты — прямо в шаблон компонента:

<form (ngSubmit)="addTodo()" #todoForm="ngForm">
  <input name="title" [(ngModel)]="newTitle" placeholder="Название задачи" required />
  <button type="submit">Добавить</button>
</form>
newTitle = '';

addTodo() {
  if (!this.newTitle.trim()) return;
  const todo: Partial<Todo> = { title: this.newTitle, completed: false };
  this.todoService.addTodo(todo).subscribe((created) => {
    this.todos.unshift(created);
    this.newTitle = '';
  });
}

6. Полезные нюансы

Работа с HTTP-опциями: заголовки, параметры

Иногда нужно добавить заголовки (например, для авторизации) или query-параметры. Для этого используется третий параметр методов http.get, http.post и т.д. — объект опций.

import { HttpHeaders, HttpParams } from '@angular/common/http';

const headers = new HttpHeaders().set('Authorization', 'Bearer my-token');
const params = new HttpParams().set('limit', '10');

this.http.get<Todo[]>(this.apiUrl, { headers, params }).subscribe(...);

Обработка ошибок HTTP-запросов

Сеть — штука капризная, серверы иногда падают, а программисты (да-да, мы!) иногда ошибаются в адресах. Поэтому важно уметь обрабатывать ошибки.

В Angular для этого удобно использовать RxJS-оператор catchError:

import { catchError } from 'rxjs/operators';
import { of } from 'rxjs';

this.todoService.getTodos().pipe(
  catchError(error => {
    console.error('Ошибка загрузки задач', error);
    return of([]); // Возвращаем пустой массив вместо ошибки
  })
).subscribe(data => this.todos = data);

Советы и нюансы

  • Не забывайте отписываться! Если вы создаёте подписки вручную (например, в сервисах или компонентах с долгим временем жизни), используйте ngOnDestroy и Subscription.unsubscribe(). Или используйте async pipe — он всё сделает за вас.
  • Типизация — ваш друг! Всегда указывайте типы данных (<Todo[]>, <Todo>), чтобы избежать неожиданностей.
  • Работайте с сервером асинхронно. Все методы HttpClient возвращают Observable — не пытайтесь получить данные "сразу", используйте подписки.
  • Не отправляйте секретные данные в открытом виде! Для авторизации используйте токены, HTTPS и серверные проверки.

7. Типичные ошибки при работе с HTTP-запросами

Ошибка №1: забыли подключить HttpClientModule.
Angular выдаст загадочную ошибку вроде "NullInjectorError: No provider for HttpClient!" — не забудьте добавить модуль в imports.

Ошибка №2: не подписались на Observable.
Методы http.get, http.post и т.д. не выполняются, пока вы не подпишетесь (subscribe()) или не используете async pipe. Observable — это как письмо, которое лежит на почте, пока вы не придёте его забрать.

Ошибка №3: неправильный URL.
Пропущен слэш, опечатка или забыли указать протокол (http://). Сервер не отвечает, а вы ищете баг в Angular.

Ошибка №4: не обработали ошибку запроса.
Если сервер вернёт ошибку (404, 500 и т.д.), подписка получит ошибку, и если её не обработать — приложение может "упасть" или зависнуть.

Ошибка №5: забыли задать тип данных.
Не указали тип <Todo[]>, получили any, и потом долго ловили баги, связанные с неправильной структурой данных.

Ошибка №6: попытка изменить массив напрямую.
Например, после удаления задачи забыли обновить локальный массив задач. Сервер удалил, а на экране всё осталось по-старому.

Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ