青戈AI小栈
会员限时8折
网络与 HTTP 基础

一次访问发生了什么:请求与响应

5,155 字约 13 分钟读完146 次阅读更新于 2026/10/20

登录后收藏

阅读设置

字号
行距
代码

到第 27 节为止,任务管理程序只在本地读写 tasks.json:任务来自你自己的文件,也只保存在你的文件里。要让任务来自远端——比如一台服务器上维护的任务列表——先得看清一次「上网」到底发生了什么。

本节不写代码。用一条命令把本机变成服务器,再用浏览器和命令行各访问一次,从两边看到同一个请求。

本节结束时,你能说出一次访问里客户端发了哪四样东西、服务器回了哪三样,并能从服务器日志里认出自己刚发出的请求。

本篇的命令统一写 py,macOS / Linux 换成 python3。

一次访问就是一次对话

打开一个网页,背后是一次请求加一次响应:

  • 客户端是发出请求的一方。浏览器是客户端,你在后面几节写的 Python 程序也是客户端。
  • 服务器是回应请求的一方。它在某台电脑上等着,收到请求就回一条响应。

请求固定带四样东西:

部分 例子 说明
方法 GET 想做什么:GET 取数据,POST 提交数据
地址 http://127.0.0.1:8000/tasks.json 找谁
请求头 User-Agent: curl/7.55.1 附带的说明,一行一条
请求体 提交的数据 可以没有;GET 通常没有

响应固定带三样东西:状态码(这次成没成)、响应头(附带的说明,一行一条)、响应体(要的数据)。后面每一节都围着这几样转。

把地址拆开

http://127.0.0.1:8000/tasks.json 从左到右是五段:

段 值 含义
协议 http 用什么规矩说话;加了加密的是 https
主机 127.0.0.1 服务器的地址;这个地址固定表示本机
端口 8000 同一台机器上,不同服务用不同端口
路径 /tasks.json 要服务器上的哪个东西
查询 没有 以 ? 开头,附带的筛选条件,第 4 节讲

每台电脑都认识 127.0.0.1,它指向自己。所以你可以在这台电脑上跑一个服务器,同时用浏览器访问它,请求和响应都不出本机。

把本机变成服务器

Python 自带一个简单的文件服务器。先进入学习目录,再启动它:

powershell
cd python-foundations
py -m http.server 8000 --bind 127.0.0.1

-m http.server 是让 Python 运行自带的 http.server 模块,8000 是端口,--bind 127.0.0.1 让它只接受本机的访问。运行后它不会退出,而是停在那里等请求:

输出
Serving HTTP on 127.0.0.1 port 8000 (http://127.0.0.1:8000/) ...

这个命令窗口现在是服务器视角:每收到一次请求,它就打印一行日志。收到请求之前,窗口里只有上面这一行。

服务器提供的是启动时所在目录的文件。所以第一步的 cd python-foundations 不是可选的——起错目录,浏览器里就会看到别的文件夹。

从浏览器访问

在浏览器地址栏输入:

输出
http://127.0.0.1:8000/tasks.json

浏览器显示 tasks.json 的内容。第 27 节生成过这个文件;学习目录里没有的话,新建一个,内容如下:

json
[
  {
    "id": 1,
    "title": "学习 Python",
    "done": false
  }
]

回到跑服务器的命令窗口,多了一行:

输出
127.0.0.1 - - [07/Oct/2026 21:09:12] "GET /tasks.json HTTP/1.1" 200 -

这行日志从左到右是:发出请求的地址、时间、请求行(方法 GET、路径 /tasks.json、协议版本 HTTP/1.1)、状态码 200。浏览器每次刷新、每次重新打开这个地址,都是一次新的请求,日志里就多一行。

换一个不存在的路径

把地址改成 http://127.0.0.1:8000/tasks.json.bak,浏览器显示「404 Not Found」。服务器窗口里这次是两行:

输出
127.0.0.1 - - [07/Oct/2026 21:09:17] code 404, message File not found
127.0.0.1 - - [07/Oct/2026 21:09:17] "GET /tasks.json.bak HTTP/1.1" 404 -

请求本身是发出去的,服务器也回话了,只是回的是「你要的东西我没有」——用状态码 404 表示。地址栏输错、文件名写错、服务器起在了别的目录,看到的都是它。

状态码第一位就够用

状态码是三位数,看第一位就知道大方向:

开头 含义 例子
2xx 成功 200 正常返回
3xx 东西在别处 301 目录少了结尾的斜杠,转过去
4xx 请求这边有问题 404 路径不存在;401 没带密钥(第 7 节)
5xx 服务器那边出错 500 服务器自己的代码报错了

从命令行看完整的响应

浏览器只给你看响应体。想看状态码和响应头,用 curl——Windows、macOS、Linux 都自带这个命令。另开一个终端窗口(不要关掉服务器那个):

powershell
curl -i http://127.0.0.1:8000/tasks.json
输出
HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/3.14.4
Date: Wed, 07 Oct 2026 13:09:12 GMT
Content-type: application/json
Content-Length: 73
Last-Modified: Wed, 07 Oct 2026 13:09:09 GMT

[
  {
    "id": 1,
    "title": "学习 Python",
    "done": false
  }
]
  • 第一行是协议版本、状态码和一句说明。
  • 空行之前是响应头:Content-Type 说明响应体是什么类型(这里是 JSON),Content-Length 是响应体的字节数,Server 留言用了哪个程序——Python/3.14.4 是你电脑上的 Python 版本。
  • 空行之后是响应体,也就是浏览器里看到的那段。
  • 服务器回的是 HTTP/1.0——Python 这个自带服务器只实现了这个版本,字段的位置和含义都一样,不影响阅读。

把 -i 换成 -v,还能看到自己发出的请求头,> 开头是发出去的,< 开头是收到的:

输出
> GET /tasks.json HTTP/1.1
> Host: 127.0.0.1:8000
> User-Agent: curl/7.55.1
> Accept: */*

< HTTP/1.0 200 OK
< Content-type: application/json

User-Agent 会写上是哪个客户端在发请求,版本号以你机器上的 curl 为准,每个人看到的不一样。

完整示例

  1. 在 python-foundations 目录启动服务器:py -m http.server 8000 --bind 127.0.0.1。
  2. 浏览器打开 http://127.0.0.1:8000/tasks.json,确认看到了任务列表。
  3. 另开一个终端,执行 curl -i http://127.0.0.1:8000/tasks.json。
  4. 回到服务器窗口,日志里应该有对应的行。

预期结果

第 4 步的日志(时间和顺序按你的实际操作):

输出
Serving HTTP on 127.0.0.1 port 8000 (http://127.0.0.1:8000/) ...
127.0.0.1 - - [07/Oct/2026 21:09:12] "GET /tasks.json HTTP/1.1" 200 -
127.0.0.1 - - [07/Oct/2026 21:09:12] "GET /tasks.json HTTP/1.1" 200 -

两行对应两次访问:一次来自浏览器,一次来自 curl。服务器不看是谁发的,只看请求。

服务器窗口是「输出」不是「输入」:里面出现的行都不要复制到终端里执行。要停止服务器,按 Ctrl + C:

输出
Keyboard interrupt received, exiting.

常见错误

浏览器显示「无法访问此网站」或「拒绝了我们的连接请求」:服务器没在运行,或者已经被你关掉。看那个窗口里有没有 Serving HTTP on ...,没有就重新执行启动命令。

浏览器显示的是一个文件列表,但里面没有自己的文件:服务器起在了别的目录。http.server 提供的是启动时所在目录,按 Ctrl + C 停掉,cd python-foundations 之后重新启动。

启动后怎么访问都没有新日志:多半是之前那个服务器窗口还在跑。macOS / Linux 上重复占用端口会直接报 Address already in use,起不来;Windows 上更隐蔽——第二个服务器照样显示 Serving HTTP on ...,但请求全被先前那个接走了,新窗口一行日志也没有。关掉多余的窗口,或换一个端口(例如 8001,访问地址里也要跟着改)。

can't open file ... http.server:命令里的 -m 漏了,py http.server 被当成「运行一个叫 http.server 的文件」。-m 后面跟的是模块名。

小结

  • 一次访问是一次请求加一次响应。请求带方法、地址、请求头、请求体;响应带状态码、响应头、响应体。
  • URL 由协议、主机、端口、路径、查询组成。127.0.0.1 是本机,8000 是端口。
  • py -m http.server 8000 --bind 127.0.0.1 把当前目录变成服务器,那个窗口的日志就是服务器视角。
  • 状态码看第一位:2 成功、3 换地方、4 请求这边有问题、5 服务器那边出错。
  • curl -i 地址 一次看到状态码、响应头和响应体;-v 还能看到自己发出的请求头。

练习

  1. 排查:浏览器打开 http://127.0.0.1:8000/tasks.json 显示 404,而服务器窗口里多了一行 "GET /tasks.json HTTP/1.1" 404 -。问题出在哪一段上?写出你会敲的命令,把文件找回来。

  2. 排查:页面明明打开了,服务器窗口里却一行新日志都没有。说出最可能的原因,以及你的验证方法。

  3. 面试常问:只凭一次访问的响应头,怎么判断对方用的是不是 Python 自带的 http.server?另外,200、301、404、500 分别出现在什么情况?其中三个你能在本机复现,写出复现用的地址。

验收:第 1 题判断为「请求到了服务器,但它提供的目录里没有这个文件」,修复是先确认文件在哪个目录、再从这个目录重启服务器;第 2 题判断为「接走请求的不是你启动的那个进程」(端口被别的程序占用,或开了第二个实例),验证方法是换一个端口重启并再次访问,看日志是否出现;第 3 题依据 Server: 头,四个状态码各能说出一种情况。

查看提示

第 1 题:日志里有这一行,说明请求已经到了服务器;服务器只是没找到文件。用 dir(macOS / Linux 用 ls)看看当前目录里有没有 tasks.json,再想想这个服务器是从哪个目录启动的。第 2 题:日志只由「真正收到请求的那个进程」打印。第 3 题:curl -i 看第一行和响应头;301 可以用目录地址少写斜杠复现。

查看参考答案

第 1 题:日志里有 "GET /tasks.json HTTP/1.1" 404 -,说明请求送到了服务器,服务器在自己提供的目录里没找到这个文件。位置只可能错在两处:文件不在那里,或者服务器不是从那个目录启动的。先找文件:

powershell
cd python-foundations
dir tasks.json

找到了就说明是第二种情况:服务器是从别的目录启动的,按 Ctrl + C 停掉,cd 到 python-foundations 再启动,然后重新访问。

第 2 题:日志由「收到请求的那个进程」打印。你的请求被别的进程接走了——最常见的是 8000 端口上还跑着另一个程序,或者你又启动了一个服务器。这在 macOS / Linux 上会直接报 Address already in use,Windows 上更隐蔽:第二个服务器照样显示 Serving HTTP on ...,但一个请求也收不到。

验证方法:换一个端口重启,再访问一次。

powershell
py -m http.server 8001 --bind 127.0.0.1

浏览器打开 http://127.0.0.1:8001/tasks.json,日志立刻出现新行,就说明之前那次访问被别的进程接走了;顺手关掉多余的窗口。

第 3 题:看响应头里的 Server:

输出
Server: SimpleHTTP/0.6 Python/3.14.4

SimpleHTTP 是 Python 自带 http.server 的实现名。四个状态码:

状态码 什么情况 本机复现
200 请求成功,响应体就是你要的东西 http://127.0.0.1:8000/tasks.json
301 东西在别处,客户端要换个地址再来 先 mkdir sub,再访问 http://127.0.0.1:8000/sub(末尾不写斜杠)
404 服务器上找不到这个路径 http://127.0.0.1:8000/tasks.json.bak
500 服务器自己的代码出错 本机这个文件服务器不会 500;第 5 节的实验服务里 GET /flaky 会

排查时的共同起点是「先看状态码属于哪一类」:2xx 是成功,3xx 换地方,4xx 请求这边有问题,5xx 服务器那边有问题。它决定你接下来该看自己的代码、看地址,还是去看对方的服务。

有帮助
登录后提问 / 纠错

提问与反馈

仅你和管理员可见。我的反馈

设置你的昵称

在英雄榜和讨论中,使用你自己的名字。

2–12 个字符0 / 12