Web 开发基础:HTTP 与 REST

🌐 Web 开发基础:HTTP 与 REST

目录

  • [1. 引言](#1. 引言)
  • [2. 知识要点](#2. 知识要点)
  • [3. HTTP 协议基础](#3. HTTP 协议基础)
  • [4. RESTful API 设计原则](#4. RESTful API 设计原则)
  • [5. 用 Flask 写 REST 接口](#5. 用 Flask 写 REST 接口)
  • [6. 完整代码示例](#6. 完整代码示例)
  • [7. 逐行讲解](#7. 逐行讲解)
  • [8. 运行与验证](#8. 运行与验证)
  • [9. AtomCode 实战](#9. AtomCode 实战)
  • [10. 常见错误与排查](#10. 常见错误与排查)
  • [11. 小结与下一篇预告](#11. 小结与下一篇预告)

1. 引言

为什么学这个?

Web 开发的核心就是客户端(浏览器)与服务器之间的通信。这种通信遵循一个协议------HTTP(HyperText Transfer Protocol,超文本传输协议)。

无论你用什么框架(Flask、Django、Spring Boot、Express),最终都是:

  1. 客户端发一个 HTTP 请求(Request)
  2. 服务器回一个 HTTP 响应(Response)

不理解 HTTP 和 REST,你就无法真正理解 Web 开发。

解决了什么问题?

  • ❌ 不知道 GET 和 POST 的区别 → ✅ 彻底搞懂
  • ❌ 看到 404、500 报错就慌 → ✅ 知道每个状态码的含义
  • ❌ 写的 API 接口不规范 → ✅ 掌握 RESTful 设计规范
  • ❌ 不会用 Flask 写 API → ✅ 手写一个 REST API

2. 知识要点

编号 知识点 说明
1 HTTP 请求方法 GET / POST / PUT / DELETE
2 HTTP 状态码 200 / 201 / 400 / 404 / 500
3 HTTP 请求/响应结构 请求行、头部、体
4 RESTful 设计原则 资源、URL、动词
5 Flask 路由 @app.route() 装饰器
6 Flask JSON 响应 jsonify()
7 curl 调试 命令行测试 API

3. HTTP 协议基础

3.1 一次 HTTP 请求的完整过程

当你在浏览器输入 http://example.com/api/users 并回车时,发生了什么?

复制代码
┌─────────┐     HTTP 请求 (Request)     ┌──────────┐
│ 浏览器   │ ──────────────────────────→ │  服务器   │
│ (客户端) │                              │  (服务端) │
│          │ ←────────────────────────── │           │
└─────────┘     HTTP 响应 (Response)     └──────────┘

3.2 HTTP 请求结构

一个 HTTP 请求由三部分组成:

复制代码
┌─────────────────────────────────────┐
│ 请求行 (Request Line)                │
│ GET /api/users HTTP/1.1             │
├─────────────────────────────────────┤
│ 请求头 (Headers)                     │
│ Host: example.com                   │
│ Content-Type: application/json      │
│ Authorization: Bearer xxx           │
├─────────────────────────────────────┤
│ 请求体 (Body) --- 仅 POST/PUT 有      │
│ {"username": "张三", "age": 25}     │
└─────────────────────────────────────┘

请求行包含三个部分:

  • HTTP 方法:GET / POST / PUT / DELETE
  • 请求路径/api/users
  • HTTP 版本HTTP/1.1HTTP/2

3.3 HTTP 请求方法(动词)

方法 语义 有无 Body 幂等 安全
GET 获取资源
POST 创建资源
PUT 更新/替换资源
DELETE 删除资源
PATCH 部分更新资源

关键概念解释:

  • 幂等:同一个请求执行多次,结果一样。比如 DELETE 一次和 DELETE 十次,资源最终都是"已删除"状态。
  • 安全:不会改变服务器状态。GET 只是"看",不会"改"。

3.4 HTTP 状态码

状态码是服务器对请求的"一句话总结",分为 5 类:

状态码 含义 典型场景
1xx 信息 很少见
200 OK 成功 GET 请求成功
201 Created 已创建 POST 创建成功
204 No Content 无内容 DELETE 成功,无返回体
301 Moved Permanently 永久重定向 网站换了域名
400 Bad Request 请求错误 参数校验失败
401 Unauthorized 未认证 需要登录
403 Forbidden 禁止访问 权限不足
404 Not Found 不存在 路径写错了
405 Method Not Allowed 方法不允许 GET 写了 POST 请求
500 Internal Server Error 服务器错误 代码抛异常
502 Bad Gateway 网关错误 Nginx 代理问题

记住口诀: 1 消息、2 成功、3 重定向、4 客户端错、5 服务器错


4. RESTful API 设计原则

4.1 什么是 REST?

REST(Representational State Transfer,表现层状态转换)是一种 API 设计风格,不是标准,而是一套设计规范

4.2 核心原则

原则一:面向资源

把一切都看作"资源"(Resource),用 URL 表示资源:

bash 复制代码
# ❌ 不好的设计------用动词命名
/getUser
/createUser
/deleteUser

# ✅ 好的设计------用名词命名资源
/users          # 用户集合
/users/1        # 单个用户
/users/1/posts  # 用户 1 的帖子

原则二:用 HTTP 方法表示操作

操作 传统方式 RESTful 方式
查询用户 GET /getUser?id=1 GET /users/1
创建用户 POST /createUser POST /users
更新用户 POST /updateUser PUT /users/1
删除用户 POST /deleteUser DELETE /users/1

原则三:无状态

每个请求都包含服务器处理所需的所有信息,服务器不保存客户端状态。

4.3 留言板的 RESTful API

方法 路径 说明 状态码
GET /api/messages 获取全部留言 200
POST /api/messages 创建留言 201
GET /api/messages/1 获取 ID=1 的留言 200
DELETE /api/messages/1 删除 ID=1 的留言 204

5. 用 Flask 写 REST 接口

5.1 Flask 路由基础

Flask 使用 @app.route() 装饰器来定义路由:

python 复制代码
from flask import Flask, jsonify

app = Flask(__name__)

# 默认 GET 方法
@app.route('/api/hello')
def hello():
    return jsonify({'message': 'Hello, World!'})

# 指定 HTTP 方法
@app.route('/api/data', methods=['GET', 'POST'])
def data():
    if request.method == 'GET':
        return jsonify({'data': '这是 GET 响应'})
    elif request.method == 'POST':
        return jsonify({'data': '这是 POST 响应'})

5.2 接收请求参数

python 复制代码
from flask import request

# 查询参数(Query String): GET /api/users?name=张三
@app.route('/api/users')
def get_users():
    name = request.args.get('name', '默认值')
    return jsonify({'name': name})

# JSON 请求体: POST /api/users  body: {"name":"张三"}
@app.route('/api/users', methods=['POST'])
def create_user():
    data = request.get_json()
    name = data.get('name')
    return jsonify({'name': name}), 201

5.3 返回 JSON 响应

python 复制代码
from flask import jsonify

# 返回 JSON + 状态码
@app.route('/api/success')
def success():
    return jsonify({'status': 'ok'}), 200

@app.route('/api/error')
def error():
    return jsonify({'error': True, 'message': '出错了'}), 400

6. 完整代码示例

下面是一个完整的 Flask REST API 示例,包含留言板的原型:

python 复制代码
# demo_rest.py --- 完整示例
from flask import Flask, jsonify, request

app = Flask(__name__)

# 模拟数据库(用列表代替)
messages = [
    {'id': 1, 'username': '张三', 'content': '第一条留言', 'created_at': '2026-07-10 10:00:00'},
    {'id': 2, 'username': '李四', 'content': 'Hello World', 'created_at': '2026-07-10 11:00:00'},
]

next_id = 3


# ---- GET /api/messages ----
@app.route('/api/messages', methods=['GET'])
def get_messages():
    """返回所有留言"""
    return jsonify(messages)


# ---- POST /api/messages ----
@app.route('/api/messages', methods=['POST'])
def create_message():
    """创建新留言"""
    data = request.get_json()
    
    # 简单校验
    if not data or 'username' not in data or 'content' not in data:
        return jsonify({'error': True, 'message': '缺少必填字段'}), 400
    
    global next_id
    new_message = {
        'id': next_id,
        'username': data['username'],
        'content': data['content'],
        'created_at': '2026-07-11 00:00:00',
    }
    next_id += 1
    messages.append(new_message)
    
    return jsonify(new_message), 201


# ---- GET /api/messages/<id> ----
@app.route('/api/messages/<int:message_id>', methods=['GET'])
def get_message(message_id):
    """获取单条留言"""
    for msg in messages:
        if msg['id'] == message_id:
            return jsonify(msg)
    return jsonify({'error': True, 'message': '留言不存在'}), 404


# ---- DELETE /api/messages/<id> ----
@app.route('/api/messages/<int:message_id>', methods=['DELETE'])
def delete_message(message_id):
    """删除留言"""
    global messages
    for msg in messages:
        if msg['id'] == message_id:
            messages.remove(msg)
            return jsonify({'message': '删除成功'}), 200
    return jsonify({'error': True, 'message': '留言不存在'}), 404


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)

7. 逐行讲解

第 1-4 行:导入与初始化

python 复制代码
from flask import Flask, jsonify, request

app = Flask(__name__)
导入 用途
Flask Flask 核心类
jsonify 将 Python 字典转为 JSON 响应
request 解析 HTTP 请求(参数、Body 等)

app = Flask(__name__) 创建 Flask 应用实例,__name__ 用于定位模板等资源。

第 6-10 行:模拟数据库

python 复制代码
messages = [
    {'id': 1, 'username': '张三', 'content': '第一条留言', 'created_at': '2026-07-10 10:00:00'},
    {'id': 2, 'username': '李四', 'content': 'Hello World', 'created_at': '2026-07-10 11:00:00'},
]
next_id = 3

这段代码做了什么: 用 Python 列表模拟数据库,存储留言数据。next_id 跟踪下一个可用 ID。这是为了方便演示,实战中会改用 SQLite 数据库。

第 14-17 行:GET 接口

python 复制代码
@app.route('/api/messages', methods=['GET'])
def get_messages():
    return jsonify(messages)
  • @app.route('/api/messages', methods=['GET']) 定义路由,只接受 GET 方法
  • jsonify(messages) 将列表转为 JSON 数组返回

curl 测试:

bash 复制代码
curl http://localhost:5000/api/messages

预期输出:

json 复制代码
[
  {"id": 1, "username": "张三", "content": "第一条留言", "created_at": "2026-07-10 10:00:00"},
  {"id": 2, "username": "李四", "content": "Hello World", "created_at": "2026-07-10 11:00:00"}
]

第 20-36 行:POST 接口

python 复制代码
@app.route('/api/messages', methods=['POST'])
def create_message():
    data = request.get_json()
    if not data or 'username' not in data or 'content' not in data:
        return jsonify({'error': True, 'message': '缺少必填字段'}), 400
    
    global next_id
    new_message = {
        'id': next_id,
        'username': data['username'],
        'content': data['content'],
        'created_at': '2026-07-11 00:00:00',
    }
    next_id += 1
    messages.append(new_message)
    return jsonify(new_message), 201
  • request.get_json() 解析请求体中的 JSON 数据
  • 校验 usernamecontent 字段是否存在
  • return ..., 400 返回 400 状态码表示客户端错误
  • global next_id 声明全局变量
  • return jsonify(new_message), 201 返回 201 表示资源创建成功

curl 测试:

bash 复制代码
curl -X POST http://localhost:5000/api/messages \
  -H "Content-Type: application/json" \
  -d '{"username": "王五", "content": "新留言"}'

预期输出:

json 复制代码
{"id": 3, "username": "王五", "content": "新留言", "created_at": "2026-07-11 00:00:00"}

第 39-44 行:GET 单条留言

python 复制代码
@app.route('/api/messages/<int:message_id>', methods=['GET'])
def get_message(message_id):
    for msg in messages:
        if msg['id'] == message_id:
            return jsonify(msg)
    return jsonify({'error': True, 'message': '留言不存在'}), 404
  • <int:message_id> 是 Flask 的 URL 变量,int: 表示转换为整数
  • 如果找不到对应 ID,返回 404 状态码

第 47-54 行:DELETE 接口

python 复制代码
@app.route('/api/messages/<int:message_id>', methods=['DELETE'])
def delete_message(message_id):
    global messages
    for msg in messages:
        if msg['id'] == message_id:
            messages.remove(msg)
            return jsonify({'message': '删除成功'}), 200
    return jsonify({'error': True, 'message': '留言不存在'}), 404
  • messages.remove(msg) 从列表中删除元素
  • 删除成功返回 200,不存在返回 404

8. 运行与验证

8.1 启动服务

bash 复制代码
# 保存代码到文件
cat > demo_rest.py << 'EOF'
# ... 上面完整的代码 ...
EOF

# 运行
python demo_rest.py

预期输出:

复制代码
 * Serving Flask app 'demo_rest'
 * Debug mode: on
 * Running on all addresses (0.0.0.0)
 * Running on http://127.0.0.1:5000
 * Running on http://192.168.1.100:5000
Press CTRL+C to quit
 * Restarting with stat
 * Debugger is active!

8.2 用 curl 测试所有接口

bash 复制代码
# 1. GET 全部留言
echo "=== 1. GET 全部留言 ==="
curl -s http://localhost:5000/api/messages | python3 -m json.tool

echo ""
echo "=== 2. POST 创建留言 ==="
# 2. POST 创建留言
curl -s -X POST http://localhost:5000/api/messages \
  -H "Content-Type: application/json" \
  -d '{"username": "测试", "content": "Hello from curl!"}' | python3 -m json.tool

echo ""
echo "=== 3. GET 单条留言 ==="
# 3. GET 单条留言
curl -s http://localhost:5000/api/messages/1 | python3 -m json.tool

echo ""
echo "=== 4. GET 不存在的留言 ==="
# 4. 测试 404
curl -s http://localhost:5000/api/messages/999 | python3 -m json.tool

echo ""
echo "=== 5. DELETE 留言 ==="
# 5. DELETE
curl -s -X DELETE http://localhost:5000/api/messages/1 | python3 -m json.tool

预期输出:

json 复制代码
=== 1. GET 全部留言 ===
[
  {
    "content": "第一条留言",
    "created_at": "2026-07-10 10:00:00",
    "id": 1,
    "username": "张三"
  },
  {
    "content": "Hello World",
    "created_at": "2026-07-10 11:00:00",
    "id": 2,
    "username": "李四"
  }
]

=== 2. POST 创建留言 ===
{
  "content": "Hello from curl!",
  "created_at": "2026-07-11 00:00:00",
  "id": 3,
  "username": "测试"
}

=== 3. GET 单条留言 ===
{
  "content": "第一条留言",
  "created_at": "2026-07-10 10:00:00",
  "id": 1,
  "username": "张三"
}

=== 4. GET 不存在的留言 ===
{
  "error": true,
  "message": "留言不存在"
}

=== 5. DELETE 留言 ===
{
  "message": "删除成功"
}

8.3 验证 HTTP 状态码

bash 复制代码
# 查看响应头,确认状态码
curl -s -o /dev/null -w "%{http_code}" http://localhost:5000/api/messages
# 输出: 200

curl -s -o /dev/null -w "%{http_code}" -X POST http://localhost:5000/api/messages \
  -H "Content-Type: application/json" \
  -d '{"username": "test", "content": "hello"}'
# 输出: 201

curl -s -o /dev/null -w "%{http_code}" http://localhost:5000/api/messages/999
# 输出: 404

9. AtomCode 实战

场景:用 AtomCode 生成 REST API

提示词:

复制代码
"我是 Flask 新手,请帮我写一个完整的 REST API 留言板程序。
要求:
- 使用 Flask
- 用列表模拟数据库
- 实现 GET /api/messages(全部)、POST /api/messages(创建)、
  GET /api/messages/<id>(单条)、DELETE /api/messages/<id>(删除)
- 每个接口都返回 JSON 和正确的 HTTP 状态码
- 包含输入校验
- 请输出完整代码并逐行注释"

AtomCode 生成的代码:

AtomCode 会生成类似上面 demo_rest.py 的完整代码,并附带注释说明每一行的作用。

场景:AtomCode 解释 HTTP 状态码

提示词:

复制代码
"为什么 POST 创建成功后要返回 201,而不是 200?它们有什么区别?"

AtomCode 回复:

  • 200 OK:请求成功,通常用于 GET 获取资源
  • 201 Created:资源创建成功,通常用于 POST 创建资源
  • 语义更精确,客户端(如 axios)可以根据状态码做不同处理

10. 常见错误与排查

错误 1:忘记安装 Flask

报错信息:

复制代码
ModuleNotFoundError: No module named 'flask'

原因: 没有安装 Flask,或在虚拟环境外运行。

解决:

bash 复制代码
# 确保激活了虚拟环境
source venv/bin/activate

# 安装 Flask
pip install flask

错误 2:POST 请求取不到数据

报错信息:

python 复制代码
# 代码
data = request.get_json()
print(data)  # 输出: None

原因: 请求头缺少 Content-Type: application/json

解决: 确保 curl 请求包含:

bash 复制代码
curl -X POST http://localhost:5000/api/messages \
  -H "Content-Type: application/json" \   # ← 必须有这一行
  -d '{"username": "test", "content": "hello"}'

错误 3:405 Method Not Allowed

报错信息:

复制代码
<!DOCTYPE HTML PUBLIC ...>
<title>405 Method Not Allowed</title>

原因: 路由没有指定 methods=['POST'],Flask 默认只接受 GET。

解决:@app.route() 中明确指定方法:

python 复制代码
@app.route('/api/messages', methods=['GET', 'POST'])  # 指定允许的方法

错误 4:端口被占用

报错信息:

复制代码
OSError: [Errno 98] Address already in use

原因: 上次运行的 Flask 进程没有退出。

解决:

bash 复制代码
# 方法一:杀死进程
pkill -f "python demo_rest.py"

# 方法二:换端口
app.run(host='0.0.0.0', port=5001)

11. 小结与下一篇预告

本篇小结

  • ✅ 理解了 HTTP 请求的结构(请求行 + 请求头 + 请求体)
  • ✅ 掌握了 HTTP 方法(GET / POST / PUT / DELETE)
  • ✅ 学会了 HTTP 状态码(200 / 201 / 400 / 404 / 500)
  • ✅ 理解了 RESTful API 设计原则(面向资源、用动词表示操作)
  • ✅ 用 Flask 写了 4 个 REST 接口
  • ✅ 用 curl 测试了所有接口

下一篇预告

下一篇:04-前端构建工具Webpack速览.md

虽然我们的留言板项目直接用 CDN 引入 Vue,不需要 Webpack,但了解 Webpack 对后续学习很重要。下一篇我们将学习:

  1. Webpack 的安装与配置
  2. 开发环境 vs 生产环境
  3. Loaders 和 Babel
  4. 文件 hash 和 clean-webpack-plugin

准备好了吗?继续前进!🚀

相关推荐
顾昂_2 小时前
内存泄漏排查和 Chrome DevTools 使用教程
前端·javascript·面试
IT_陈寒3 小时前
Java线程池这个坑我算是踩明白了
前端·人工智能·后端
31535669134 小时前
Superpowers:GPT-5.6 时代下的流程毒瘤
前端·后端
清秋5 小时前
全网最全 ECMAScript 攻略( 更新至 ES2026)
前端·javascript·编程语言
Cao_Ron5 小时前
用 SVG image symbol 画 ECharts graph 圆形节点
前端
hxy06015 小时前
React-Call让你的React组件像异步函数一样调用
前端·react.js
Hilaku5 小时前
为什么 AI 写前端时,总是优先选择 React,而不是 Vue?
前端·javascript·程序员
京东云开发者5 小时前
GPT-5.6、ChatGPT Work 与 Codex 更新食用指南
前端
Cao_Ron5 小时前
用 ECharts graph 画出卡片式矩形节点
前端
skiyee5 小时前
换个底座的 Wot Starter 居然让 AI 更懂项目!
前端·ai编程