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

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

相关推荐
小徐_23333 小时前
wot-ui-cli 1.1.0 发布:支持 OpenCode、Antigravity,wot-ui 图标迁移不再靠猜
前端
__zRainy__4 小时前
Node.js Web 框架选型指南:从 Express 到 Hono 的全景对比
前端·node.js·express·koa·nestjs·egg·fastify
西瓜有点饿4 小时前
PostCSS 和 UnoCSS 的作用和区别
前端·postcss
伟大的兔神5 小时前
我做了一个本地优先的 AI 图片工作台:Loomora v1.0.0 正式发布
前端·javascript·vue.js
90后的晨仔6 小时前
uni-app项目 Vue3 状态管理 Pinia 完全指南:从概念到实战的深度解析
前端
80s7776 小时前
住宅代理解析:动态住宅IP与静态住宅IP如何选型?
网络·网络协议·tcp/ip
90后的晨仔6 小时前
uni-app 在 iOS 平台跳转页面时移除底部安全区域的完整技术指南
前端
用户938515635076 小时前
React + JWT 登录鉴权底层原理与工程化实践
前端·react.js
zww89491116 小时前
家政派单系统开发实战:架构设计与派单算法指南
前端·系统架构
fangzhanpeng1687 小时前
(前端)2.js变量作用域样例
开发语言·前端·javascript