Skip to content

第4.3章:发送 POST/PUT/DELETE 请求

学习时间: 1 小时


1. 主动命令:从启动到报废

迄今为止,我们的控制中心只请求信息 (GET)。现在我们将学习发送主动命令

  • POST:“将新卫星发射到轨道!”
  • PUT:“对国际空间站系统进行全面升级!”
  • DELETE:“将旧设备 Debris-123 脱离轨道!”

为此,我们将使用 fetch,但会附加描述我们命令的额外参数。

💡 太空类比:

如果 GET 是被动监听无线电信号,那么 POSTPUTDELETE 则是主动发送命令。为此,我们不仅需要指定“频率”(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);
POST 请求中 fetch 的关键点:

  • method: 'POST' 必须指定 HTTP 方法。
  • headers: { 'Content-Type': 'application/json' } 极其重要的请求头。它告诉我们的 FastAPI 服务器请求体中包含 JSON,需要进行解析。
  • body: JSON.stringify(shipData) 我们不能直接发送 JavaScript 对象。需要将其序列化(转换)为 JSON 字符串。

3. 发送 DELETE 请求:设备报废

删除请求更简单——它通常不需要请求体,只需带有对象 ID 的 URL。

步骤 1:在我们的飞船列表中添加“删除”按钮 修改 app.js 中的 fetchAndDisplayFleet 函数,使其为每个元素添加删除按钮。

// app.js,在 fetchAndDisplayFleet 函数内部

// ...
ships.forEach(ship => {
    const listItem = document.createElement('li');
    // 添加一个带有存储 ID 的 data 属性的按钮
    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; // 从 data 属性获取 ID
        deleteShip(shipId);
    }
});

步骤 3:在 Spaceship 模型中添加 id

main.py 文件中的模型和数据库中添加 id

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. 提交表单时,向 /spaceships/{id} 发送 PUT 请求,并附带完整的对象体。
  5. 成功更新后,更新舰队列表。

提示: 您将需要使用 method: 'PUT'Content-Type 请求头和带有 JSON.stringify()body 来发送 fetch 请求,就像在 POST 请求中一样。


巩固测验

1. `fetch` 的哪个参数用于在请求体中传递数据?

2. 请求头 `'Content-Type': 'application/json'` 告诉服务器,它...

3. JavaScript 中的 `JSON.stringify(obj)` 函数有什么作用?

4. 使用 `fetch` 发送 `DELETE` 请求时,必须指定:

5. JavaScript 中的事件委托是指...


🚀 本章总结:

您的任务控制中心现在拥有了管理舰队的完整指令集!

  • ✅ 您学会了发送带有请求体和请求头的 POST 请求来创建新资源。
  • ✅ 您实现了 DELETE 请求来报废旧设备。
  • ✅ 您收到了实现 PUT 请求的任务,巩固了您的知识。

全面控制已建立! 但如果通信中断或服务器报告错误怎么办?在下一章中,我们将在前端创建一个集中的错误处理系统。

📌 检查:

  • 确保创建新飞船的表单正常工作,并在成功创建后页面上的列表得到更新。
  • 检查“报废设备”按钮是否工作,是否请求确认,并从列表中删除飞船。
  • 尝试使用无效数据(例如,名称非常短)创建飞船,并查看您的 FastAPI 服务器将返回的错误。

⚠️ 如果有错误:

  • 服务器返回 422 错误: 很可能您发送的数据未通过 Pydantic 验证。检查浏览器控制台 — errorData.detail 将显示哪个字段有问题。
  • 415 Unsupported Media Type 错误: 您忘记添加 'Content-Type': 'application/json' 请求头。
  • 删除按钮不起作用: 检查事件委托是否正确工作,以及您是否正确从 data-ship-id 获取了 shipId