Web 开发基础:HTTP 与 REST
Web 开发基础HTTP 与 REST目录1. 引言2. 知识要点3. HTTP 协议基础4. RESTful API 设计原则5. 用 Flask 写 REST 接口6. 完整代码示例7. 逐行讲解8. 运行与验证9. AtomCode 实战10. 常见错误与排查11. 小结与下一篇预告1. 引言为什么学这个Web 开发的核心就是客户端浏览器与服务器之间的通信。这种通信遵循一个协议——HTTPHyperText Transfer Protocol超文本传输协议。无论你用什么框架Flask、Django、Spring Boot、Express最终都是客户端发一个HTTP 请求Request服务器回一个HTTP 响应Response不理解 HTTP 和 REST你就无法真正理解 Web 开发。解决了什么问题❌ 不知道 GET 和 POST 的区别 → ✅ 彻底搞懂❌ 看到 404、500 报错就慌 → ✅ 知道每个状态码的含义❌ 写的 API 接口不规范 → ✅ 掌握 RESTful 设计规范❌ 不会用 Flask 写 API → ✅ 手写一个 REST API2. 知识要点编号知识点说明1HTTP 请求方法GET / POST / PUT / DELETE2HTTP 状态码200 / 201 / 400 / 404 / 5003HTTP 请求/响应结构请求行、头部、体4RESTful 设计原则资源、URL、动词5Flask 路由app.route() 装饰器6Flask JSON 响应jsonify()7curl 调试命令行测试 API3. 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/usersHTTP 版本HTTP/1.1或HTTP/23.3 HTTP 请求方法动词方法语义有无 Body幂等安全GET获取资源无✅✅POST创建资源有❌❌PUT更新/替换资源有✅❌DELETE删除资源无✅❌PATCH部分更新资源有❌❌关键概念解释幂等同一个请求执行多次结果一样。比如 DELETE 一次和 DELETE 十次资源最终都是已删除状态。安全不会改变服务器状态。GET 只是看不会改。3.4 HTTP 状态码状态码是服务器对请求的一句话总结分为 5 类状态码含义典型场景1xx信息很少见200OK 成功GET 请求成功201Created 已创建POST 创建成功204No Content 无内容DELETE 成功无返回体301Moved Permanently 永久重定向网站换了域名400Bad Request 请求错误参数校验失败401Unauthorized 未认证需要登录403Forbidden 禁止访问权限不足404Not Found 不存在路径写错了405Method Not Allowed 方法不允许GET 写了 POST 请求500Internal Server Error 服务器错误代码抛异常502Bad Gateway 网关错误Nginx 代理问题记住口诀1 消息、2 成功、3 重定向、4 客户端错、5 服务器错4. RESTful API 设计原则4.1 什么是 RESTRESTRepresentational State Transfer表现层状态转换是一种 API 设计风格不是标准而是一套设计规范。4.2 核心原则原则一面向资源把一切都看作资源Resource用 URL 表示资源# ❌ 不好的设计——用动词命名/getUser /createUser /deleteUser# ✅ 好的设计——用名词命名资源/users# 用户集合/users/1# 单个用户/users/1/posts# 用户 1 的帖子原则二用 HTTP 方法表示操作操作传统方式RESTful 方式查询用户GET /getUser?id1GET /users/1创建用户POST /createUserPOST /users更新用户POST /updateUserPUT /users/1删除用户POST /deleteUserDELETE /users/1原则三无状态每个请求都包含服务器处理所需的所有信息服务器不保存客户端状态。4.3 留言板的 RESTful API方法路径说明状态码GET/api/messages获取全部留言200POST/api/messages创建留言201GET/api/messages/1获取 ID1 的留言200DELETE/api/messages/1删除 ID1 的留言2045. 用 Flask 写 REST 接口5.1 Flask 路由基础Flask 使用app.route()装饰器来定义路由fromflaskimportFlask,jsonify appFlask(__name__)# 默认 GET 方法app.route(/api/hello)defhello():returnjsonify({message:Hello, World!})# 指定 HTTP 方法app.route(/api/data,methods[GET,POST])defdata():ifrequest.methodGET:returnjsonify({data:这是 GET 响应})elifrequest.methodPOST:returnjsonify({data:这是 POST 响应})5.2 接收请求参数fromflaskimportrequest# 查询参数Query String: GET /api/users?name张三app.route(/api/users)defget_users():namerequest.args.get(name,默认值)returnjsonify({name:name})# JSON 请求体: POST /api/users body: {name:张三}app.route(/api/users,methods[POST])defcreate_user():datarequest.get_json()namedata.get(name)returnjsonify({name:name}),2015.3 返回 JSON 响应fromflaskimportjsonify# 返回 JSON 状态码app.route(/api/success)defsuccess():returnjsonify({status:ok}),200app.route(/api/error)deferror():returnjsonify({error:True,message:出错了}),4006. 完整代码示例下面是一个完整的 Flask REST API 示例包含留言板的原型# demo_rest.py — 完整示例fromflaskimportFlask,jsonify,request appFlask(__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_id3# ---- GET /api/messages ----app.route(/api/messages,methods[GET])defget_messages():返回所有留言returnjsonify(messages)# ---- POST /api/messages ----app.route(/api/messages,methods[POST])defcreate_message():创建新留言datarequest.get_json()# 简单校验ifnotdataorusernamenotindataorcontentnotindata:returnjsonify({error:True,message:缺少必填字段}),400globalnext_id new_message{id:next_id,username:data[username],content:data[content],created_at:2026-07-11 00:00:00,}next_id1messages.append(new_message)returnjsonify(new_message),201# ---- GET /api/messages/id ----app.route(/api/messages/int:message_id,methods[GET])defget_message(message_id):获取单条留言formsginmessages:ifmsg[id]message_id:returnjsonify(msg)returnjsonify({error:True,message:留言不存在}),404# ---- DELETE /api/messages/id ----app.route(/api/messages/int:message_id,methods[DELETE])defdelete_message(message_id):删除留言globalmessagesformsginmessages:ifmsg[id]message_id:messages.remove(msg)returnjsonify({message:删除成功}),200returnjsonify({error:True,message:留言不存在}),404if__name____main__:app.run(host0.0.0.0,port5000,debugTrue)7. 逐行讲解第 1-4 行导入与初始化fromflaskimportFlask,jsonify,request appFlask(__name__)导入用途FlaskFlask 核心类jsonify将 Python 字典转为 JSON 响应request解析 HTTP 请求参数、Body 等app Flask(__name__)创建 Flask 应用实例__name__用于定位模板等资源。第 6-10 行模拟数据库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_id3这段代码做了什么用 Python 列表模拟数据库存储留言数据。next_id跟踪下一个可用 ID。这是为了方便演示实战中会改用 SQLite 数据库。第 14-17 行GET 接口app.route(/api/messages,methods[GET])defget_messages():returnjsonify(messages)app.route(/api/messages, methods[GET])定义路由只接受 GET 方法jsonify(messages)将列表转为 JSON 数组返回curl 测试curlhttp://localhost:5000/api/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}]第 20-36 行POST 接口app.route(/api/messages,methods[POST])defcreate_message():datarequest.get_json()ifnotdataorusernamenotindataorcontentnotindata:returnjsonify({error:True,message:缺少必填字段}),400globalnext_id new_message{id:next_id,username:data[username],content:data[content],created_at:2026-07-11 00:00:00,}next_id1messages.append(new_message)returnjsonify(new_message),201request.get_json()解析请求体中的 JSON 数据校验username和content字段是否存在return ..., 400返回 400 状态码表示客户端错误global next_id声明全局变量return jsonify(new_message), 201返回 201 表示资源创建成功curl 测试curl-XPOST http://localhost:5000/api/messages\-HContent-Type: application/json\-d{username: 王五, content: 新留言}预期输出{id:3,username:王五,content:新留言,created_at:2026-07-11 00:00:00}第 39-44 行GET 单条留言app.route(/api/messages/int:message_id,methods[GET])defget_message(message_id):formsginmessages:ifmsg[id]message_id:returnjsonify(msg)returnjsonify({error:True,message:留言不存在}),404int:message_id是 Flask 的 URL 变量int:表示转换为整数如果找不到对应 ID返回 404 状态码第 47-54 行DELETE 接口app.route(/api/messages/int:message_id,methods[DELETE])defdelete_message(message_id):globalmessagesformsginmessages:ifmsg[id]message_id:messages.remove(msg)returnjsonify({message:删除成功}),200returnjsonify({error:True,message:留言不存在}),404messages.remove(msg)从列表中删除元素删除成功返回 200不存在返回 4048. 运行与验证8.1 启动服务# 保存代码到文件catdemo_rest.pyEOF # ... 上面完整的代码 ... 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 CTRLC to quit * Restarting with stat * Debugger is active!8.2 用 curl 测试所有接口# 1. GET 全部留言echo 1. GET 全部留言 curl-shttp://localhost:5000/api/messages|python3-mjson.toolechoecho 2. POST 创建留言 # 2. POST 创建留言curl-s-XPOST http://localhost:5000/api/messages\-HContent-Type: application/json\-d{username: 测试, content: Hello from curl!}|python3-mjson.toolechoecho 3. GET 单条留言 # 3. GET 单条留言curl-shttp://localhost:5000/api/messages/1|python3-mjson.toolechoecho 4. GET 不存在的留言 # 4. 测试 404curl-shttp://localhost:5000/api/messages/999|python3-mjson.toolechoecho 5. DELETE 留言 # 5. DELETEcurl-s-XDELETE http://localhost:5000/api/messages/1|python3-mjson.tool预期输出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 状态码# 查看响应头确认状态码curl-s-o/dev/null-w%{http_code}http://localhost:5000/api/messages# 输出: 200curl-s-o/dev/null-w%{http_code}-XPOST http://localhost:5000/api/messages\-HContent-Type: application/json\-d{username: test, content: hello}# 输出: 201curl-s-o/dev/null-w%{http_code}http://localhost:5000/api/messages/999# 输出: 4049. 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或在虚拟环境外运行。解决# 确保激活了虚拟环境sourcevenv/bin/activate# 安装 Flaskpipinstallflask错误 2POST 请求取不到数据报错信息# 代码datarequest.get_json()print(data)# 输出: None原因请求头缺少Content-Type: application/json。解决确保 curl 请求包含curl-XPOST http://localhost:5000/api/messages\-HContent-Type: application/json\# ← 必须有这一行-d{username: test, content: hello}错误 3405 Method Not Allowed报错信息!DOCTYPE HTML PUBLIC ... title405 Method Not Allowed/title原因路由没有指定methods[POST]Flask 默认只接受 GET。解决在app.route()中明确指定方法app.route(/api/messages,methods[GET,POST])# 指定允许的方法错误 4端口被占用报错信息OSError: [Errno 98] Address already in use原因上次运行的 Flask 进程没有退出。解决# 方法一杀死进程pkill-fpython demo_rest.py# 方法二换端口app.run(host0.0.0.0,port5001)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 对后续学习很重要。下一篇我们将学习Webpack 的安装与配置开发环境 vs 生产环境Loaders 和 Babel文件 hash 和 clean-webpack-plugin准备好了吗继续前进