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
相关推荐
Lupino6 分钟前
被 React “玩弄”的 24 小时:为了修一个不存在的 Bug,我给大模型送了顿火锅钱
前端·react.js
米丘13 分钟前
了解 Javascript 模块化,更好地掌握 Vite 、Webpack、Rollup 等打包工具
前端
Heo14 分钟前
深入 React19 Diff 算法
前端·javascript·面试
滕青山15 分钟前
个人所得税计算器 在线工具核心JS实现
前端·javascript·vue.js
小怪点点16 分钟前
手写promise
前端·promise
国思RDIF框架25 分钟前
RDIFramework.NET Web 敏捷开发框架 V6.3 发布 (.NET8+、Framework 双引擎)
前端
Mintopia26 分钟前
如何在有限的时间里,活出几倍的人生
前端
炫饭第一名27 分钟前
速通Canvas指北🦮——变形、渐变与阴影篇
前端·javascript·程序员
Neptune128 分钟前
让我带你迅速吃透React组件通信:从入门到精通(上篇)
前端·javascript
阿懂在掘金28 分钟前
Vue 表单避坑(一):为什么 v-model 绑定对象属性会偷偷修改父组件数据?
前端·vue.js