HTTP基础教程:请求方法、状态码、JSON、鉴权、超时、重试与流式返回
title: “HTTP基础教程:请求方法、状态码、JSON、鉴权、超时、重试与流式返回”
date: 2026-04-28
tags:
- HTTP
- Python
- API
- JSON
- FastAPI
- requests
description: “一篇面向初学者的 HTTP 基础博客教程,系统介绍请求方法、状态码、JSON、鉴权、超时、重试和流式返回,并结合 Python 示例说明实际开发中的常见用法。”
HTTP基础教程:请求方法、状态码、JSON、鉴权、超时、重试与流式返回
做后端、写爬虫、调大模型 API、接第三方平台,几乎绕不开 HTTP。
很多初学者一开始会直接背接口文档,能发请求就算会用,但一旦遇到下面这些问题就容易卡住:
- 为什么有的接口用
GET,有的用POST - 为什么明明请求发出去了,却返回
401、403、404、500 - 为什么有的接口参数放在 URL 上,有的放在 JSON 里
- 为什么调用第三方 API 经常要带 Token
- 为什么接口偶尔卡死,必须设置超时
- 为什么失败后有时应该重试,有时反而不能重试
- 为什么现在很多 AI 接口都支持“流式返回”
这篇文章的目标不是让你死记硬背名词,而是帮你建立一套够用的 HTTP 基础认知,尤其适合:
- Python 初学者
- 后端入门同学
- 爬虫和接口调用初学者
- 正在学 FastAPI、Flask、requests 的同学
一、HTTP到底是什么
HTTP 全称是HyperText Transfer Protocol,即超文本传输协议。
你可以把它理解成:
浏览器、客户端、服务端之间约定好的一套“怎么发请求、怎么回响应”的通信规则。
最常见的场景就是:
- 客户端发请求
- 服务端处理请求
- 服务端返回响应
比如你在浏览器输入一个网址,本质上就是浏览器向服务器发起了一次 HTTP 请求。
二、一次HTTP请求里都有什么
一个 HTTP 请求通常包含这些部分:
- 请求方法
- 请求路径
- 请求头
- 查询参数
- 请求体
一个 HTTP 响应通常包含这些部分:
- 状态码
- 响应头
- 响应体
看一个最简单的例子:
GET /users/123 HTTP/1.1 Host: api.example.com Accept: application/json Authorization: Bearer your_token_here服务端可能返回:
HTTP/1.1 200 OK Content-Type: application/json {"id":123,"name":"Alice"}这里最值得关注的是三件事:
- 你用什么方法请求:
GET - 服务端是否处理成功:
200 OK - 传输的数据格式是什么:
application/json
三、请求方法:GET、POST、PUT、PATCH、DELETE 到底怎么区分
请求方法决定了你“想对资源做什么”。
1. GET:获取资源
GET用来读取数据,不应该用于修改数据。
常见场景:
- 获取文章列表
- 查询用户信息
- 获取商品详情
例如:
GET /articles?page=1&page_size=10 HTTP/1.1特点:
- 参数通常放在 URL 查询参数里
- 理论上应该是“安全”的,不修改服务端数据
- 适合查询场景
2. POST:创建资源或提交数据
POST常用于创建新资源,或者提交一段需要服务端处理的数据。
例如:
POST /users HTTP/1.1 Content-Type: application/json {"name":"Alice","age":20}常见场景:
- 创建用户
- 登录接口
- 提交表单
- 调用需要复杂参数的推理接口
3. PUT:整体更新
PUT通常表示“用新的完整内容替换旧资源”。
例如:
PUT /users/123 HTTP/1.1 Content-Type: application/json {"name":"Alice","age":21}语义上更偏向“整体覆盖”。
4. PATCH:部分更新
PATCH用于局部更新资源的一部分字段。
例如:
PATCH /users/123 HTTP/1.1 Content-Type: application/json {"age":21}如果只是改某几个字段,PATCH往往比PUT更合适。
5. DELETE:删除资源
DELETE用于删除资源。
例如:
DELETE /users/123 HTTP/1.16. HEAD 和 OPTIONS
这两个方法初学阶段见得少,但也值得知道:
HEAD:和GET类似,但只要响应头,不要响应体OPTIONS:询问服务端支持哪些请求方法,浏览器跨域预检时常见
7. 一个最简单的记忆法
| 方法 | 含义 | 常见用途 |
|---|---|---|
GET | 查 | 获取资源 |
POST | 增 / 提交 | 创建资源、提交任务 |
PUT | 整体改 | 覆盖更新 |
PATCH | 局部改 | 部分字段更新 |
DELETE | 删 | 删除资源 |
四、状态码:看懂HTTP响应的第一步
状态码是服务端对本次请求处理结果的简短总结。
1. 2xx:成功
最常见的成功状态码有:
200 OK:请求成功201 Created:资源创建成功204 No Content:成功,但没有响应体
例如:
- 查询成功返回
200 - 创建新用户成功返回
201 - 删除成功但不返回内容可以用
204
2. 3xx:重定向
这一类状态码表示客户端需要进一步动作。
常见的有:
301 Moved Permanently:永久重定向302 Found:临时重定向
浏览器里比较常见,接口开发中一般接触少一点。
3. 4xx:客户端错误
这类状态码通常表示:
请求有问题,责任主要在客户端。
常见的有:
400 Bad Request:请求格式错误、参数不合法401 Unauthorized:未认证,通常没带 Token 或 Token 无效403 Forbidden:已认证,但没有权限404 Not Found:资源不存在405 Method Not Allowed:请求方法不对409 Conflict:资源冲突422 Unprocessable Entity:参数校验失败,FastAPI 中很常见429 Too Many Requests:请求太频繁,被限流
4. 5xx:服务端错误
这类状态码通常表示:
请求本身可能没问题,但服务端处理失败了。
常见的有:
500 Internal Server Error:服务端内部异常502 Bad Gateway:网关或上游服务异常503 Service Unavailable:服务暂时不可用504 Gateway Timeout:网关等待上游超时
5. 最常见的一组状态码,你最好熟到能脱口而出
| 状态码 | 含义 | 常见原因 |
|---|---|---|
200 | 成功 | 请求正常 |
201 | 创建成功 | 新资源已生成 |
400 | 请求错误 | 参数不对、格式错误 |
401 | 未认证 | Token 缺失或失效 |
403 | 无权限 | 账号没有访问权限 |
404 | 未找到 | 路径错了或资源不存在 |
429 | 频率过高 | 被限流 |
500 | 服务端异常 | 代码报错或依赖异常 |
502 | 网关错误 | 上游服务异常 |
504 | 网关超时 | 上游服务太慢 |
6. 一个开发习惯
看到接口报错时,不要先盯着“返回内容”,先看:
- 状态码是多少
- 请求方法对不对
- 路径对不对
- 鉴权头带没带
- 参数格式对不对
很多问题在这一步就能定位。
五、JSON:现代接口最常见的数据格式
现在大部分 Web API 都用 JSON 传输数据。
1. JSON是什么
JSON 全称是JavaScript Object Notation,但它并不只属于 JavaScript。
它是一种轻量、可读性强、跨语言通用的数据交换格式。
例如:
{"name":"Alice","age":20,"skills":["Python","SQL"],"is_active":true}2. JSON常见数据类型
JSON 里常见的值类型有:
- 字符串
- 数字
- 布尔值
- 数组
- 对象
null
3. 请求里怎么传JSON
如果你要向服务端发送 JSON,请求头一般需要带:
Content-Type: application/json请求体例如:
{"model":"gpt-4.1","messages":[{"role":"user","content":"你好"}]}4. 响应里怎么表示JSON
如果服务端返回的是 JSON,响应头一般会有:
Content-Type: application/json5. Python里如何发送JSON
使用requests最方便的方式是json=参数:
importrequests url="https://api.example.com/users"payload={"name":"Alice","age":20}response=requests.post(url,json=payload,timeout=10)print(response.status_code)print(response.json())这里的json=payload会自动帮你:
- 把 Python 字典转成 JSON 字符串
- 帮你设置
Content-Type: application/json
6.data=和json=的区别
这是一个高频面试点,也是高频踩坑点。
json=:发送 JSON 数据data=:通常发送表单或原始文本数据
对比:
requests.post(url,json={"a":1})requests.post(url,data={"a":1})它们发出去的内容通常并不一样,服务端的解析方式也不一样。
六、鉴权:为什么很多接口必须带Token
并不是所有接口都允许匿名访问。
比如这些场景都需要鉴权:
- 获取当前登录用户信息
- 调用付费 API
- 访问后台管理接口
- 操作私有资源
鉴权的本质是:
服务端需要确认“你是谁”,以及“你有没有权限做这件事”。
1. 最常见的几种鉴权方式
方式一:Bearer Token
这是现代 API 中最常见的方式之一。
请求头通常长这样:
Authorization: Bearer your_access_tokenPython 示例:
importrequests headers={"Authorization":"Bearer your_access_token"}response=requests.get("https://api.example.com/me",headers=headers,timeout=10,)print(response.status_code)print(response.text)方式二:Basic Auth
Basic Auth会把用户名和密码编码后放进请求头中。
Python 示例:
importrequests response=requests.get("https://api.example.com/private",auth=("username","password"),timeout=10,)print(response.status_code)方式三:API Key
有些平台会要求你把 Key 放在请求头或查询参数里。
例如:
X-API-Key: your_api_key或者:
GET /data?api_key=your_api_key HTTP/1.1通常更推荐放请求头,避免泄露在 URL 日志里。
2.401和403很容易混
可以这样记:
401:你还没通过身份认证403:你身份没问题,但你没有权限
例如:
- 没带 Token:常见
401 - 带了 Token,但访问管理员接口:常见
403
七、超时:为什么请求不能一直等下去
很多初学者写接口调用时,经常漏掉超时设置:
requests.get("https://api.example.com/data")这样写最大的问题是:
如果对方服务卡住了,你的程序可能会一直等下去。
这在下面这些场景里尤其危险:
- 爬虫批量抓取
- Web 服务调用第三方接口
- 定时任务
- 大模型接口聚合
1. 超时通常分什么
在很多 HTTP 客户端里,超时通常可以细分为:
- 连接超时:建立连接花太久
- 读取超时:连接已建立,但服务端返回太慢
requests支持元组写法:
importrequests response=requests.get("https://api.example.com/data",timeout=(3,10),)这里表示:
- 连接超时 3 秒
- 读取超时 10 秒
2. 一个更安全的习惯
无论是脚本、爬虫还是服务端代码,最好都显式设置超时。
例如:
response=requests.get(url,timeout=10)或者更细一点:
response=requests.get(url,timeout=(2,10))3. Web服务里为什么更要重视超时
如果你的服务 A 要调用服务 B,而服务 B 很慢:
- A 的请求线程会被占住
- 用户会一直等待
- 并发一多,整个服务吞吐就会下降
所以超时不只是“避免卡死”,更是系统稳定性的基础。
八、重试:失败后什么时候该再来一次
接口失败后,重试是很常见的策略,但不是所有失败都能重试。
1. 可以考虑重试的典型场景
- 网络抖动
- 临时超时
502、503、504- 短暂性的连接失败
- 被限流后服务端明确允许稍后重试
2. 不应该盲目重试的场景
- 参数错误,如
400 - 没权限,如
401、403 - 资源不存在,如
404 - 非幂等操作已经成功但客户端没确认,重复提交可能造成副作用
3. 什么叫幂等
幂等可以粗略理解为:
同一个请求执行一次和执行多次,结果一致。
例如:
GET查询通常是幂等的DELETE在很多设计里也可以视为幂等POST创建订单通常不是天然幂等的
这意味着:
- 查询接口失败了,重试一般风险较低
- 创建订单失败了,直接重试就要谨慎
4. 正确的重试姿势:指数退避
不要失败了立刻狂打一百次。
更合理的策略通常是:
- 最多重试 2 到 5 次
- 每次等待时间逐步增加
- 遇到确定性错误直接停止
5. 一个简单的Python重试示例
importtimeimportrequestsdeffetch_with_retry(url,max_retries=3):forattemptinrange(max_retries):try:response=requests.get(url,timeout=(3,10))response.raise_for_status()returnresponse.json()exceptrequests.RequestExceptionasexc:is_last=attempt==max_retries-1ifis_last:raisewait_time=2**attemptprint(f"request failed:{exc}, retry after{wait_time}s")time.sleep(wait_time)data=fetch_with_retry("https://api.example.com/data")print(data)6. 一个实用原则
重试是为了对抗“偶发性故障”,不是为了掩盖“确定性错误”。
如果请求结构错了、鉴权错了、路径错了,重试 100 次也没意义。
九、流式返回:为什么现在很多AI接口都一边生成一边返回
传统 HTTP 响应通常是:
- 客户端发请求
- 服务端处理完整个结果
- 一次性把完整内容返回
但在很多场景里,用户并不想等到全部完成才看到结果。
比如:
- 大模型逐字输出答案
- 实时日志推送
- 长任务分块返回进度
- 大文件下载
这时候就会用到流式返回。
1. 什么是流式返回
流式返回指的是:
服务端不是等所有数据都准备好再一次性返回,而是边生成边发送,客户端边接收边处理。
这会显著改善用户感知延迟。
2. 流式返回的价值
- 首字节更早到达
- 用户更早看到内容
- 长任务交互体验更好
- 更适合聊天、日志、实时反馈场景
3. Python客户端如何接收流式响应
用requests时,可以设置stream=True:
importrequests response=requests.get("https://api.example.com/stream",stream=True,timeout=(3,30),)forlineinresponse.iter_lines(decode_unicode=True):ifline:print(line)这里的关键点是:
stream=True:不要一次性把整个响应全部读完iter_lines():按行迭代流式内容
4. 服务端如何返回流式响应
以 FastAPI 为例,可以使用StreamingResponse:
importasynciofromfastapiimportFastAPIfromfastapi.responsesimportStreamingResponse app=FastAPI()asyncdeffake_llm_stream():chunks=["你好",",","这是","流式","返回","示例。"]forchunkinchunks:yieldchunkawaitasyncio.sleep(0.5)@app.get("/stream")asyncdefstream():returnStreamingResponse(fake_llm_stream(),media_type="text/plain")客户端访问/stream时,就能逐块收到响应,而不是等全部内容拼好。
5. SSE是什么
在流式返回的讨论里,你还经常会见到SSE。
SSE 全称是Server-Sent Events,它是一种基于 HTTP 的服务端单向推送机制,常用于:
- AI 聊天输出
- 消息通知
- 实时进度
它本质上还是 HTTP,只是响应格式和处理方式更适合持续推送文本事件。
十、把这些概念放到一个完整请求里看
下面是一个更接近真实项目的 Python 请求示例:
importtimeimportrequestsdefcall_api():url="https://api.example.com/chat"headers={"Authorization":"Bearer your_access_token","Content-Type":"application/json",}payload={"message":"你好","stream":False,}forattemptinrange(3):try:response=requests.post(url,headers=headers,json=payload,timeout=(3,15),)ifresponse.status_code==429:wait_time=2**attempt time.sleep(wait_time)continueresponse.raise_for_status()returnresponse.json()exceptrequests.RequestException:ifattempt==2:raisetime.sleep(2**attempt)result=call_api()print(result)这里你能同时看到几个 HTTP 基础点:
- 用的是
POST - 请求体是 JSON
- 带了 Bearer Token
- 设置了超时
- 对可恢复失败做了重试
- 最终解析 JSON 响应
十一、初学者最容易踩的坑
1. 忘记设置超时
这会导致程序在异常情况下长期卡住。
2. 把data=和json=搞混
表面都能发请求,但服务端收到的内容可能完全不是你想要的。
3. 遇到报错只看返回文本,不看状态码
状态码往往是定位问题的第一线索。
4. 看到失败就无脑重试
先判断是不是可恢复错误,再决定要不要重试。
5. 流式返回时还按普通响应处理
如果服务端本来是分块返回,你却用一次性读取,体验和处理逻辑都会变差。
6. 把敏感信息拼到URL里
例如把 Token、密码、API Key 放在查询参数中,容易出现在日志、浏览器历史和代理记录里。
十二、如果你在学Python,建议这样练HTTP
按下面顺序练习会比较扎实:
- 用
requests调一个公开GET接口 - 自己写一个
POST JSON请求 - 练习读取状态码和响应头
- 给请求加上超时和异常处理
- 练习 Bearer Token 鉴权
- 写一个简单重试函数
- 用 FastAPI 写一个普通接口和一个流式接口
如果这些都能独立写出来,你对 HTTP 的理解就已经不只是“会调接口”了。
十三、总结
HTTP 基础最重要的不是背概念,而是建立正确的请求和排错思维。
这篇文章最核心的内容可以压缩成下面几句话:
- 请求方法决定你想对资源做什么
- 状态码决定你应该先从哪里排查问题
- JSON 是现代 API 最常见的数据格式
- 鉴权决定你有没有资格访问接口
- 超时是稳定性的底线,不能省
- 重试只适合处理偶发性故障,不适合掩盖确定性错误
- 流式返回适合长任务、实时输出和 AI 场景
如果你后面要学:
- 爬虫
- FastAPI
- 调用大模型 API
- 微服务接口联调
那这些 HTTP 基础几乎都会反复用到。
把这一层打牢,后面很多内容都会顺很多。
