Глава 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
.
Ваша миссия, если вы решите ее принять:
- Добавить кнопку "Изменить" рядом с кнопкой "Удалить" для каждого корабля.
- При нажатии на "Изменить" заполнять форму (можно использовать ту же, что и для создания) текущими данными корабля.
- Изменить текст кнопки "Запустить" на "Обновить".
- При отправке формы отправлять
PUT
-запрос на/spaceships/{id}
с полным телом объекта. - После успешного обновления — обновлять список флота.
Подсказка: Вам понадобится
fetch
сmethod: 'PUT'
, заголовкамиContent-Type
иbody
сJSON.stringify()
, точно так же, как вPOST
-запросе.
Квиз для закрепления
🚀 Итог главы:
Ваш ЦУП теперь обладает полным набором команд для управления флотом!
- ✅ Вы научились отправлять
POST
-запросы с телом и заголовками для создания новых ресурсов. - ✅ Вы реализовали
DELETE
-запросы для списания устаревших аппаратов. - ✅ Вы получили задание на реализацию
PUT
-запроса, закрепив свои знания.
Полный контроль установлен! Но что делать, если связь прервалась или сервер сообщил об ошибке? В следующей главе мы создадим централизованную систему обработки ошибок на фронтенде.
📌 Проверка:
- Убедитесь, что форма создания нового корабля работает и после успешного создания список на странице обновляется.
- Проверьте, что кнопка "Списать аппарат" работает, запрашивает подтверждение и удаляет корабль из списка.
- Попробуйте создать корабль с невалидными данными (например, с очень коротким названием) и посмотрите на ошибку, которую вернет ваш FastAPI-сервер.
⚠️ Если ошибки:
- Ошибка
422
от сервера: Скорее всего, данные, которые вы отправляете, не проходят валидацию Pydantic. Проверьте консоль браузера —errorData.detail
покажет, в каком поле проблема.- Ошибка
415 Unsupported Media Type
: Вы забыли добавить заголовок'Content-Type': 'application/json'
.- Кнопки удаления не работают: Проверьте, правильно ли работает делегирование событий и корректно ли вы получаете
shipId
изdata-ship-id
.