返回首页

说一下GET和POST的区别

问题解析

GET和POST是HTTP协议中最常用的两种请求方法,面试官通过这个问题考察候选人对HTTP协议基础的理解深度,以及能否区分表面差异和本质区别。

核心概念

HTTP方法概述

HTTP(HyperText Transfer Protocol)定义了多种请求方法,表示对资源的操作类型:

方法 描述 幂等性 安全性
GET 获取资源
POST 提交数据
PUT 更新资源
DELETE 删除资源
PATCH 部分更新
HEAD 获取头部
OPTIONS 查询支持的方法

幂等性(Idempotent):多次执行结果相同 安全性(Safe):不修改服务器状态

GET方法

GET方法用于从服务器获取资源,是最常用的HTTP方法。

核心特点:

  • 用于请求指定资源
  • 参数附加在URL中
  • 可以被缓存
  • 可以收藏为书签
  • 长度受限于URL长度

POST方法

POST方法用于向服务器提交数据,通常会导致服务器状态的改变。

核心特点:

  • 用于提交数据到服务器
  • 参数放在请求体中
  • 默认不被缓存
  • 不可收藏为书签
  • 理论上无长度限制

详细解答

GET与POST的区别对比

特性 GET POST
主要用途 获取资源 提交数据
参数位置 URL查询参数 请求体(Body)
数据可见性 参数在URL中可见 参数在Body中,相对隐蔽
数据长度限制 受URL长度限制(约2KB-8KB) 理论上无限制
编码方式 仅支持URL编码 支持多种编码(JSON、Form等)
缓存 可以被浏览器缓存 默认不被缓存
历史记录 参数保留在浏览器历史 参数不保留在历史记录
回退行为 无害,可重复执行 可能重复提交数据
书签 可以收藏为书签 不可收藏为书签
幂等性 幂等 非幂等
安全性 安全(不修改状态) 不安全(修改状态)

详细差异分析

1. 语义和用途

GET - 获取资源:

GET /users?id=123 HTTP/1.1
Host: api.example.com
适用场景:
- 搜索查询
- 分页请求
- 筛选过滤
- 获取详情

POST - 提交数据:

POST /users HTTP/1.1
Host: api.example.com
Content-Type: application/json

{
  "name": "张三",
  "email": "zhangsan@example.com"
}
适用场景:
- 表单提交
- 文件上传
- 创建资源
- 复杂查询(GraphQL)

2. 参数传递方式

GET参数在URL中:

GET /search?q=keyword&category=tech&page=1 HTTP/1.1
Host: www.example.com
URL结构:
https://example.com/path?key1=value1&key2=value2
         ↑      ↑    ↑        ↑
       协议   域名  路径    查询参数

POST参数在Body中:

POST /api/users HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded

name=张三&age=25&city=北京
POST /api/users HTTP/1.1
Host: example.com
Content-Type: application/json

{
  "name": "张三",
  "age": 25,
  "city": "北京"
}
POST /api/upload HTTP/1.1
Host: example.com
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary

------WebKitFormBoundary
Content-Disposition: form-data; name="file"; filename="photo.jpg"
Content-Type: image/jpeg

[二进制文件内容]
------WebKitFormBoundary--

3. 数据长度限制

GET长度限制:

不同浏览器的URL长度限制:
- IE:2083字符
- Chrome:约32KB
- Firefox:约64KB
- Safari:约64KB

服务器限制:
- Apache:8192字符
- Nginx:默认8KB(可配置)
- Tomcat:8192字符

建议:GET请求URL长度保持在2KB以内

POST长度限制:

理论上:HTTP协议无限制
实际上:受限于服务器配置
- PHP:post_max_size(默认8MB)
- Nginx:client_max_body_size(默认1MB)
- Apache:LimitRequestBody
- Node.js:需要中间件处理

4. 安全性考量

GET的安全问题:

1. 参数暴露在URL中
   - 浏览器历史记录
   - 服务器访问日志
   -  referrer头中泄露

2. 容易被CSRF攻击
   - <img src="http://bank.com/transfer?to=hacker&amount=10000">

3. 不适合传输敏感信息
   - 密码、Token等不应放在URL中

POST的安全优势:

1. 参数不在URL中
   - 不会出现在浏览器历史
   - 不会出现在访问日志
   - 但仍在请求体中,需配合HTTPS

2. 需要额外的CSRF防护
   - 使用CSRF Token
   - SameSite Cookie属性
   - 验证Origin/Referer

3. 仍需HTTPS加密
   - POST不加密,只是相对隐蔽
   - 敏感数据必须使用HTTPS

重要提醒:

GET和POST在安全性上没有本质区别!
- 都不加密(除非使用HTTPS)
- 都可以被拦截
- 安全性取决于HTTPS,而非方法本身

5. 缓存行为

GET缓存:

# 请求
GET /api/data HTTP/1.1

# 响应
HTTP/1.1 200 OK
Cache-Control: max-age=3600
ETag: "abc123"

# 后续请求(浏览器缓存)
GET /api/data HTTP/1.1
If-None-Match: "abc123"

# 304响应
HTTP/1.1 304 Not Modified

POST缓存:

# 默认不缓存
POST /api/data HTTP/1.1

# 显式允许缓存(HTTP/1.1 RFC 7231)
POST /api/search HTTP/1.1
Cache-Control: max-age=3600
Content-Location: /api/search/result-123

6. 回退行为差异

GET回退:

用户操作:点击后退按钮
结果:重新请求页面,无副作用
原因:GET是幂等的,重复执行结果相同

POST回退:

用户操作:点击后退按钮
结果:浏览器警告"是否重新提交表单"
原因:POST非幂等,重复执行可能产生副作用

解决方案:
1. PRG模式(Post/Redirect/Get)
2. 表单提交后重定向到GET页面
3. 使用Token防止重复提交

PRG模式示例:

# 处理POST请求
@app.route('/submit', methods=['POST'])
def submit():
    # 处理表单数据
    save_data(request.form)
    # 重定向到GET页面
    return redirect('/success')

@app.route('/success', methods=['GET'])
def success():
    return "提交成功"

本质区别:RFC规范定义

根据RFC 7231,GET和POST的本质区别在于语义

GET的语义:

安全的(Safe):请求不会导致服务器状态改变
幂等的(Idempotent):多次请求结果相同
可缓存的(Cacheable):响应可以被缓存

POST的语义:

不安全的:可能导致服务器状态改变
非幂等的:多次请求可能产生不同结果
不可缓存:默认情况下响应不可缓存

技术实现细节

POST发送数据包的数量

传统说法:

GET:发送1个数据包(Header)
POST:发送2个数据包(Header + Body

实际情况:

取决于浏览器和服务器实现:

1. 大多数浏览器:
   - POST的Header和Body一起发送
   - 仅1个数据包

2. 部分旧版浏览器(如旧版Firefox):
   - 先发送Header(带Expect: 100-continue)
   - 等待服务器100 Continue响应
   - 再发送Body
   - 共2个数据包

3. 使用curl --expect100-timeout:
   curl -H "Expect: 100-continue" http://example.com

注意:

数据包数量差异对性能影响微乎其微
不应作为选择GET或POST的依据

底层都是TCP连接

GET和POST在传输层没有本质区别:

应用层:HTTP GET/POST请求
传输层:TCP连接(三次握手)
网络层:IP数据包
数据链路层:以太网帧
物理层:比特流

无论是GET还是POST:
- 都建立TCP连接
- 都通过TCP传输
- 都可以使用HTTP/1.1的keep-alive复用连接
- HTTP/2中都是帧(Frame)传输

深入理解

RESTful API设计中的使用

RESTful架构中,HTTP方法对应CRUD操作:

GET    /users       # 读取用户列表(Read)
GET    /users/123   # 读取指定用户(Read)
POST   /users       # 创建用户(Create)
PUT    /users/123   # 更新用户(Update)
DELETE /users/123   # 删除用户(Delete)
PATCH  /users/123   # 部分更新(Partial Update)

GET vs POST在REST中的区别:

GET /users?active=true   # 查询活跃用户(无副作用)
POST /users/deactivate   # 批量停用用户(有副作用)

即使功能相似,也应根据是否有副作用选择方法

实际开发中的选择建议

使用GET的场景:

1. 获取资源
   GET /api/products
   GET /api/products/123

2. 搜索查询
   GET /api/search?q=keyword

3. 分页筛选
   GET /api/users?page=1&size=20&sort=name

4. 过滤条件
   GET /api/orders?status=pending&date=2024-01

使用POST的场景:

1. 创建资源
   POST /api/users
   { "name": "张三", "email": "zs@example.com" }

2. 提交表单
   POST /api/login
   { "username": "admin", "password": "***" }

3. 文件上传
   POST /api/upload
   Content-Type: multipart/form-data

4. 复杂查询(查询参数过多或敏感)
   POST /api/search
   { "filters": [...], "sort": {...}, "page": 1 }

5. 非幂等操作
   POST /api/transfer
   { "from": "A", "to": "B", "amount": 100 }

常见误区澄清

误区1:POST比GET安全

错误!两者都不加密
- 都可通过抓包工具查看
- 都需要HTTPS保护
- POST参数只是不在URL中,但仍在请求中明文传输

误区2:GET不能传输敏感数据

部分正确:
- GET参数会留在浏览器历史、服务器日志
- 不适合传输密码、Token
- 但业务数据(如搜索关键词)可以用GET

误区3:POST数据量更大

部分正确:
- GET受URL长度限制
- POST理论上无限制
- 但实际都受服务器配置限制

误区4:GET只能URL编码,POST可以多种编码

错误!
- GET参数在URL中,只能用URL编码
- POST的Body可以用任何编码
- 这是参数位置决定的,不是方法本身限制

最佳实践

API设计规范

# GET请求规范
GET /api/v1/users:
  parameters:
    - name: page
      in: query
      type: integer
    - name: size
      in: query
      type: integer
    - name: sort
      in: query
      type: string
  responses:
    200:
      description: 用户列表

# POST请求规范
POST /api/v1/users:
  consumes:
    - application/json
  parameters:
    - name: body
      in: body
      schema:
        type: object
        properties:
          name:
            type: string
          email:
            type: string
  responses:
    201:
      description: 创建成功

安全最佳实践

# 1. 敏感操作使用POST + CSRF防护
@app.route('/transfer', methods=['POST'])
@csrf_protect
def transfer():
    pass

# 2. 使用HTTPS
# 配置HSTS头
response.headers['Strict-Transport-Security'] = 'max-age=31536000'

# 3. 防止参数污染
# GET参数校验
if len(request.args) > 10:
    return error("参数过多")

# POST Body大小限制
if request.content_length > 1024 * 1024:  # 1MB
    return error("请求体过大")

# 4. 幂等性设计
# POST请求使用幂等Token
@app.route('/create-order', methods=['POST'])
def create_order():
    idempotency_key = request.headers.get('Idempotency-Key')
    if cache.exists(idempotency_key):
        return cache.get(idempotency_key)
    # 执行业务逻辑

缓存策略

# GET请求缓存
GET /api/static-data HTTP/1.1

HTTP/1.1 200 OK
Cache-Control: public, max-age=3600
ETag: "abc123"
Last-Modified: Wed, 21 Oct 2023 07:28:00 GMT

# POST请求(特定情况下)缓存
POST /api/search HTTP/1.1
Content-Location: /api/search/query-hash-abc123
Cache-Control: max-age=300

# 后续GET请求
GET /api/search/query-hash-abc123 HTTP/1.1

面试要点

  1. 核心区别:GET用于获取,POST用于提交;GET参数在URL,POST参数在Body
  2. 幂等性:GET是幂等的,POST不是
  3. 缓存:GET可缓存,POST默认不缓存
  4. 长度限制:GET受URL长度限制,POST理论上无限制
  5. 安全性:两者都不安全,都需要HTTPS
  6. 本质:都是TCP连接,区别在HTTP语义层

常见面试追问:

  • GET和POST的本质区别是什么?
  • POST一定比GET安全吗?
  • 什么时候应该用POST而不是GET?
  • 什么是幂等性?为什么GET是幂等的?
  • POST发送几个数据包?