Skip to content

Глава 4.3: Отправка POST/PUT/DELETE запросов

Время изучения: 1 час


1. Активные команды: От запуска до списания

До сих пор наш ЦУП только запрашивал информацию (GET). Теперь мы научимся отправлять активные команды:

  • POST: "Запустить новый спутник на орбиту!"
  • PUT: "Провести полную модернизацию систем на МКС!"
  • DELETE: "Свести с орбиты старый аппарат Debris-123!"

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

💡 Космическая аналогия:

Если GET — это пассивное прослушивание радиоэфира, то POST, PUT и DELETE — это активная передача команд. Для этого нам нужно не только указать "частоту" (URL), но и содержание команды (тело запроса) и протокол связи (заголовки).


2. Отправка POST-запроса: Запуск нового корабля

Чтобы создать новый ресурс, мы отправляем POST-запрос. Самое главное здесь — передать тело (body) запроса с данными нового объекта.

Шаг 1: Добавляем форму создания в index.html Разместим ее после блока "Запрос по ID".

<!-- index.html -->
<hr>
<h2>Запустить новый аппарат</h2>
<form id="create-ship-form">
    <input type="text" id="create-name" placeholder="Название" required><br>
    <input type="text" id="create-type" placeholder="Тип" required><br>
    <input type="number" id="create-year" placeholder="Год запуска" required><br>
    <input type="text" id="create-status" placeholder="Статус" required><br>
    <button type="submit">Запустить</button>
</form>
<div id="create-status-message"></div>

Шаг 2: Добавляем логику в app.js

// app.js, в конец файла

const createShipForm = document.getElementById('create-ship-form');
const createStatusMessage = document.getElementById('create-status-message');

async function createShip(event) {
    event.preventDefault();

    // 1. Собираем данные из формы в объект
    const shipData = {
        name: document.getElementById('create-name').value,
        type: document.getElementById('create-type').value,
        launch_year: parseInt(document.getElementById('create-year').value),
        status: document.getElementById('create-status').value
    };

    try {
        createStatusMessage.textContent = 'Отправка команды на запуск...';

        // 2. Отправляем fetch-запрос с параметрами
        const response = await fetch(`${API_BASE_URL}/spaceships`, {
            method: 'POST', // Указываем метод
            headers: {
                'Content-Type': 'application/json' // Говорим серверу, что отправляем JSON
            },
            body: JSON.stringify(shipData) // Превращаем объект JavaScript в JSON-строку
        });

        if (!response.ok) {
            // Если сервер вернул ошибку, пытаемся прочитать ее тело
            const errorData = await response.json();
            throw new Error(errorData.detail || `Ошибка сервера: ${response.status}`);
        }

        const newShip = await response.json();
        createStatusMessage.textContent = `🚀 Успешный запуск! Аппарату присвоен ID: ${newShip.id}`;

        createShipForm.reset(); // Очищаем форму
        fetchAndDisplayFleet(); // Обновляем общий список флота

    } catch (error) {
        console.error('Ошибка при запуске аппарата:', error);
        createStatusMessage.textContent = `🔴 Ошибка: ${error.message}`;
    }
}

createShipForm.addEventListener('submit', createShip);
Ключевые моменты fetch для POST:

  • method: 'POST': Обязательно указываем HTTP-метод.
  • headers: { 'Content-Type': 'application/json' }: Критически важный заголовок. Он сообщает нашему FastAPI-серверу, что в теле запроса находится JSON, и его нужно распарсить.
  • body: JSON.stringify(shipData): Мы не можем отправить JavaScript-объект напрямую. Его нужно сериализовать (превратить) в JSON-строку.

3. Отправка DELETE-запроса: Списание аппарата

Запрос на удаление проще — ему обычно не нужно тело, только URL с ID объекта.

Шаг 1: Добавляем кнопку "Удалить" в наш список кораблей Изменим функцию fetchAndDisplayFleet в app.js, чтобы она добавляла кнопку удаления для каждого элемента.

// app.js, внутри функции fetchAndDisplayFleet

// ...
ships.forEach(ship => {
    const listItem = document.createElement('li');
    // Добавляем кнопку с data-атрибутом, хранящим ID
    listItem.innerHTML = `
        <strong>${ship.name} (ID: ${ship.id})</strong><br>
        Тип: ${ship.type} | Год: ${ship.launch_year} | Статус: ${ship.status}<br>
        <button class="delete-btn" data-ship-id="${ship.id}">Списать аппарат</button>
    `;
    fleetList.appendChild(listItem);
});
// ...

Шаг 2: Добавляем обработчик для всех кнопок "Удалить" Мы используем делегирование событий — один обработчик на весь список.

// app.js, в конец файла

async function deleteShip(shipId) {
    if (!confirm(`Вы уверены, что хотите списать аппарат с ID ${shipId}? Это действие необратимо.`)) {
        return;
    }

    try {
        const response = await fetch(`${API_BASE_URL}/spaceships/${shipId}`, {
            method: 'DELETE' // Указываем метод
        });

        if (!response.ok) {
            throw new Error(`Не удалось списать аппарат. Статус: ${response.status}`);
        }

        alert(`Аппарат с ID ${shipId} успешно списан.`);
        fetchAndDisplayFleet(); // Обновляем список

    } catch (error) {
        console.error('Ошибка при списании:', error);
        alert(`Ошибка: ${error.message}`);
    }
}

// Делегирование событий: слушаем клики на всем списке
fleetList.addEventListener('click', (event) => {
    // Проверяем, был ли клик по кнопке с классом 'delete-btn'
    if (event.target.classList.contains('delete-btn')) {
        const shipId = event.target.dataset.shipId; // Получаем ID из data-атрибута
        deleteShip(shipId);
    }
});

Шаг 3: Добавляем id в модель Spaceship

Добавляем id в модели и бд в файле main.py

class Spaceship(BaseModel):
    id: int
    # Остальной код модели...

db_spaceships = {
    1: {
        "id": 1,
        # Данные элемента 1
    },
    2: {
        "id": 2,
        # Данные элемента 2
    },
    3: {
        "id": 3,
        # Данные элемента 3
    }
}
  • method: 'DELETE': Указываем метод. Тело и заголовки здесь не нужны.
  • confirm(): Простое встроенное окно подтверждения, чтобы случайно не удалить что-то важное.

4. Отправка PUT-запроса (Самостоятельное задание)

Реализация PUT-запроса для обновления очень похожа на POST.

Ваша миссия, если вы решите ее принять:

  1. Добавить кнопку "Изменить" рядом с кнопкой "Удалить" для каждого корабля.
  2. При нажатии на "Изменить" заполнять форму (можно использовать ту же, что и для создания) текущими данными корабля.
  3. Изменить текст кнопки "Запустить" на "Обновить".
  4. При отправке формы отправлять PUT-запрос на /spaceships/{id} с полным телом объекта.
  5. После успешного обновления — обновлять список флота.

Подсказка: Вам понадобится fetch с method: 'PUT', заголовками Content-Type и body с JSON.stringify(), точно так же, как в POST-запросе.


Квиз для закрепления

1. Какой параметр `fetch` используется для передачи данных в теле запроса?

2. Заголовок `'Content-Type': 'application/json'` сообщает серверу, что...

3. Функция `JSON.stringify(obj)` в JavaScript делает что?

4. Для отправки `DELETE`-запроса с помощью `fetch` обязательно нужно указать:

5. Делегирование событий в JavaScript — это когда...


🚀 Итог главы:

Ваш ЦУП теперь обладает полным набором команд для управления флотом!

  • ✅ Вы научились отправлять POST-запросы с телом и заголовками для создания новых ресурсов.
  • ✅ Вы реализовали DELETE-запросы для списания устаревших аппаратов.
  • ✅ Вы получили задание на реализацию PUT-запроса, закрепив свои знания.

Полный контроль установлен! Но что делать, если связь прервалась или сервер сообщил об ошибке? В следующей главе мы создадим централизованную систему обработки ошибок на фронтенде.

📌 Проверка:

  • Убедитесь, что форма создания нового корабля работает и после успешного создания список на странице обновляется.
  • Проверьте, что кнопка "Списать аппарат" работает, запрашивает подтверждение и удаляет корабль из списка.
  • Попробуйте создать корабль с невалидными данными (например, с очень коротким названием) и посмотрите на ошибку, которую вернет ваш FastAPI-сервер.

⚠️ Если ошибки:

  • Ошибка 422 от сервера: Скорее всего, данные, которые вы отправляете, не проходят валидацию Pydantic. Проверьте консоль браузера — errorData.detail покажет, в каком поле проблема.
  • Ошибка 415 Unsupported Media Type: Вы забыли добавить заголовок 'Content-Type': 'application/json'.
  • Кнопки удаления не работают: Проверьте, правильно ли работает делегирование событий и корректно ли вы получаете shipId из data-ship-id.