GET/POST 的区别:从“为什么登录请求不能用 GET”说起

上周我在审查一个新人提交的登录接口代码时,发现他用了 GET 请求:

js 复制代码
// ❌ 危险!不要这样写登录接口
fetch(`/api/login?username=${username}&password=${password}`, {
  method: 'GET'
})

我问他:"如果用户在公共电脑上登录,别人能通过浏览器历史记录看到他的密码吗?"

他愣住了------这正是 GETPOST 最核心的区别。今天我就结合真实业务场景,带你彻底搞懂 HTTP 方法的本质差异。


一、问题场景:登录接口的安全隐患

我们有个后台管理系统,登录模块最初是这样设计的:

js 复制代码
// 前端代码
function login(username, password) {
  return fetch(`/login?user=${encodeURIComponent(username)}&pass=${encodeURIComponent(password)}`, {
    method: 'GET'
  })
}

结果运维报警:Nginx 日志里全是明文密码!

因为 GET 请求的参数会出现在:

  • URL 中
  • 浏览器地址栏
  • 服务器访问日志
  • 代理服务器日志
  • Referer 头

任何能看到这些地方的人,都能看到用户的密码。


二、解决方案:改用 POST 提交敏感数据

我们重构为 POST 请求:

js 复制代码
// ✅ 安全做法
function login(username, password) {
  return fetch('/api/login', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ username, password })
  })
}

现在密码不会出现在 URL 和日志中,安全性大幅提升。


三、原理剖析:从表面到网络层的五层差异

1. 第一层:数据传输位置(最核心区别)

方法 数据位置 是否可见
GET URL 查询参数(?key=value ✅ 明文可见
POST 请求体(Request Body) ❌ 不直接可见

我们来画一张 HTTP 请求结构对比图

ET /api/data?name=Alice&age=25 HTTP/1.1

Host: example.com

User-Agent: Chrome

→ 参数在第一行,随请求头一起发送

POST /api/login HTTP/1.1

Host: example.com

Content-Type: application/json

Content-Length: 38

{"username":"Alice","password":"123"}

↑ 参数在空行之后,作为请求体独立存在

2. 第二层:缓存与历史记录

方法 能否被缓存 是否保存在浏览器历史
GET ✅ 可以(除非禁用) ✅ 是
POST ❌ 通常不缓存 ✅ 但参数不可见
js 复制代码
// GET 可以被浏览器缓存,提升性能
fetch('/api/config', { method: 'GET' }) // 可能读取缓存

// POST 每次都会发请求
fetch('/api/submit', { method: 'POST' }) // 总是发送新请求

3. 第三层:数据大小限制

方法 数据大小限制 原因
GET 约 2KB - 8KB 受 URL 长度限制(浏览器和服务器限制)
POST 几 MB 甚至更大 请求体无理论限制(受服务器配置影响)
js 复制代码
// ❌ GET 无法发送大文件
fetch(`/upload?file=${hugeBase64Data}`) // 可能失败

// ✅ POST 可以发送大文件
fetch('/upload', {
  method: 'POST',
  body: fileData // 可达几十 MB
})

4. 第四层:幂等性与安全性

方法 幂等性 安全性
GET ✅ 幂等 ✅ 安全(只读)
POST ❌ 非幂等 ❌ 不安全(可能修改数据)
PUT ✅ 幂等 ❌ 不安全
DELETE ✅ 幂等 ❌ 不安全
PATCH ❌ 非幂等 ❌ 不安全

📌 术语解释

  • 幂等 :多次执行结果相同(如 GET /user/1 调 10 次结果一样)
  • 安全:不会修改服务器状态

5. 第五层:典型使用场景

方法 使用场景 示例
GET 获取数据、搜索、查询 /api/users, /search?q=keyword
POST 创建资源、提交表单、登录 /api/users, /login
PUT 完全替换资源 PUT /api/users/1(更新整个用户对象)
DELETE 删除资源 DELETE /api/users/1
PATCH 部分更新资源 PATCH /api/users/1(只改邮箱)

四、对比主流 HTTP 方法

方法 数据位置 幂等 安全 典型用途
GET URL 参数 查询、获取
POST 请求体 创建、提交
PUT 请求体 替换
DELETE 通常无 删除
PATCH 请求体 部分更新
HEAD 获取元信息(如检查资源是否存在)
OPTIONS 预检请求(CORS)

五、实战避坑指南

❌ 错误用法:用 GET 提交敏感信息

js 复制代码
// 千万不要这样做!
fetch(`/transfer?to=Bob&amount=1000&token=${token}`, { method: 'GET' })

✅ 正确做法:敏感操作用 POST/PUT

js 复制代码
fetch('/api/transfer', {
  method: 'POST',
  body: JSON.stringify({ to: 'Bob', amount: 1000 }),
  headers: { 'Authorization': `Bearer ${token}` }
})

❌ 错误用法:用 POST 实现查询接口

js 复制代码
// 虽然能用,但违反 REST 原则
fetch('/search', {
  method: 'POST',
  body: JSON.stringify({ q: 'react' })
})

✅ 正确做法:查询用 GET,即使参数复杂

js 复制代码
// 方案1:用 GET + 查询参数
fetch(`/search?q=react&tags=hooks,context&sort=desc`)

// 方案2:如果参数太复杂,考虑用 POST 但命名要明确
fetch('/api/advanced-search', {
  method: 'POST',
  body: complexQuery
})

六、举一反三:三个变体场景实现思路

  1. 需要上传文件并携带元数据

    使用 POSTPUTContent-Type: multipart/form-data,文件和字段一起发送。

  2. 实现"撤销删除"功能

    删除用 DELETE,但服务器端改为标记删除(is_deleted=true),前端用 GET /items?deleted=true 查看已删项。

  3. 批量操作接口设计

    • 批量创建:POST /api/users(数组 body)
    • 批量删除:POST /api/users/delete(ID 数组)或 DELETE /api/users(带 body)
    • 批量更新:PATCH /api/users/batch(操作列表)
js 复制代码
// 批量删除示例
fetch('/api/users/batch-delete', {
  method: 'POST',
  body: JSON.stringify({ ids: [1, 2, 3] })
})

小结

GETPOST 的区别,本质是 "安全查询" vs "数据操作" 的哲学差异:

  • GET 是"读卡器"------只能读,不能写,数据暴露在外
  • POST 是"投递箱"------内容密封,专门用于提交

查数据,用 GET;
改数据,用 POST;
传敏感,绝不用 GET;
想幂等,选 PUT/DELETE。

当你在设计 API 时,先问自己:

  • 这个操作会修改数据吗?→ 不能用 GET
  • 数据是否敏感?→ 不能用 GET
  • 是否可能重复提交?→ 考虑幂等性(PUT vs POST)
  • 是创建还是更新?→ POST vs PUT/PATCH
相关推荐
fatsheep洋1 分钟前
XSS-DOM-1
java·前端·xss
MX_93591 分钟前
使用Nginx部署前端项目
运维·前端·nginx
斯~内克2 分钟前
前端CSS重绘与重排深度解析:从原理到优化实战
前端·css
srrsheng7 分钟前
电商前端Nginx访问日志收集分析实战
运维·前端·nginx
飏旎9 分钟前
对于前端闭包的详细理解
前端·状态模式
拾光拾趣录11 分钟前
前端的单元测试
前端·单元测试
风清云淡_A1 小时前
【Flutter3.8x】flutter从入门到实战基础教程(四):自定义实现一个自增的StatefulWidget组件
前端·flutter
curdcv_po1 小时前
🔥 Three.js 一个项目用到:轨道控制器、动画与GUI交互
前端
琹箐1 小时前
CSS font-weight:500不生效
前端·css
代码的余温1 小时前
React核心:组件化与虚拟DOM揭秘
前端·react.js·前端框架