嘿,我是Agnes。说到AJAX里的这四个“大佬”——GET、POST、PUT、DELETE,很多初学者甚至工作几年的开发同学,都容易把它们混为一谈,或者用得不规范。今天咱们不聊枯燥的RFC文档,我就用最通俗的大白话,结合真实的开发坑点,帮你把这事儿彻底理清楚。
先搞懂一个核心概念:HTTP动词的本质
你得先明白,GET、POST、PUT、DELETE不仅仅是AJAX的方法,它们是RESTful风格里对资源操作的四种基本动作。
你可以把服务器想象成一个巨大的图书馆,而每个资源(比如一篇文章、一个用户)就是书架上的一本书。
- GET:我去借书看,看完还回去,不改动书的内容。
- POST:我写了一新篇章,要添加到书里(创建新资源)。
- PUT:我把书里错别字改正确,或者重写某一章(完整更新资源)。
- DELETE:我把这本书从书架上扔进碎纸机(删除资源)。
搞懂这个比喻,后面的区别你就明白一半了。
GET请求:只读,别想太多
特点
- 幂等性:查10次和查1次,结果完全一样。
- 安全性:不会修改服务器数据。
- 参数:通过URL问号(
?)传递,比如api/users?page=1。 - 缓存:浏览器会对GET请求结果进行缓存,再次访问同URL时速度更快。
- 长度限制:受限于浏览器和服务器的URL长度,一般几千字符以内。
典型场景
- 搜索商品列表
- 获取用户信息
- 分页加载数据
- 查询订单状态
代码示例
// 使用 fetch API
fetch('/api/products?category=books&sort=price_asc')
.then(response => response.json())
.then(data => {
console.log('商品列表:', data);
renderProductList(data);
})
.catch(error => {
console.error('获取商品失败:', error);
});
// 使用 jQuery(老项目常见)
$.ajax({
url: '/api/products',
method: 'GET',
data: { category: 'books', sort: 'price_asc' },
success: function(data) {
console.log('商品列表:', data);
}
});
常见错误排查
错误1:用GET提交敏感数据
// ❌ 错误!密码不能放在URL里
fetch('/api/login?username=admin&password=123456')
原因:URL会被记录在浏览器历史、服务器日志、代理缓存中,极度不安全。
错误2:GET请求参数编码问题
// ❌ 错误!中文参数未编码
fetch('/api/search?key=你好')
// ✅ 正确!使用 encodeURIComponent
const key = encodeURIComponent('你好');
fetch(`/api/search?key=${key}`)
错误3:把GET当作POST用(提交表单)
// ❌ 错误!表单提交修改数据用GET
// ✅ 正确!使用POST
POST请求:创建,别省力气
特点
- 非幂等:多次POST可能创建多个资源(比如多次下单)。
- 数据在请求体:参数放在body里,不暴露在URL中。
- 无长度限制:理论上可以发送大量数据。
- 不被缓存:每次都是新请求。
典型场景
- 用户注册
- 提交评论
- 上传文件
- 创建订单
代码示例
// 提交用户注册信息
const userData = {
username: 'zhangsan',
password: 'securePass123!',
email: 'zhangsan@example.com'
};
fetch('/api/users/register', {
method: 'POST',
headers: {
'Content-Type': 'application/json', // 重要!告诉服务器发的是JSON
'Authorization': 'Bearer token123'
},
body: JSON.stringify(userData) // 对象转字符串
})
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.then(data => {
console.log('注册成功:', data);
// 跳转首页
window.location.href = '/dashboard';
})
.catch(error => {
console.error('注册失败:', error);
// 显示错误提示
showError(error.message);
});
常见错误排查
错误1:忘记设置Content-Type
// ❌ 错误!服务器收到的是undefined或乱码
fetch('/api/users', {
method: 'POST',
body: JSON.stringify({ name: 'test' })
// 缺少 headers
})
// ✅ 正确!必须设置
fetch('/api/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ name: 'test' })
})
错误2:直接发送对象而不是字符串
// ❌ 错误!body必须是字符串
fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: { name: 'test' } // 直接传对象,会被自动转为"[object Object]"
})
// ✅ 正确!用JSON.stringify()
body: JSON.stringify({ name: 'test' })
错误3:使用表单数据却忘了编码
// ❌ 错误!中文乱码
const formData = new FormData();
formData.append('name', '张三');
// ✅ 正确!FormData会自动处理编码
const formData = new FormData();
formData.append('name', '张三');
formData.append('file', fileInput.files[0]); // 上传文件用FormData
fetch('/api/upload', {
method: 'POST',
body: formData // 不需要设置Content-Type,浏览器会自动设置
})
PUT请求:更新,覆盖式修改
特点
- 幂等性:多次PUT同一资源,结果相同(最后一次生效)。
- 完整替换:通常需要发送完整的资源数据,服务器会用新数据完全覆盖旧数据。
- 数据在请求体:同POST。
典型场景
- 修改用户资料(头像、昵称、邮箱)
- 更新商品信息
- 同步配置项
代码示例
// 更新用户资料
const userUpdateData = {
id: 1001,
username: 'zhangsan_new',
email: 'zhangsan_new@example.com',
avatar: 'https://example.com/avatar.jpg',
bio: '这是一个新的个人简介'
};
fetch('/api/users/1001', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token123'
},
body: JSON.stringify(userUpdateData)
})
.then(response => {
if (response.status === 200) {
console.log('更新成功');
// 刷新页面数据
loadUserProfile();
} else {
console.error('更新失败');
}
})
.catch(error => {
console.error('网络错误:', error);
});
常见错误排查
错误1:用PUT做部分更新
// ❌ 错误!PUT应该发送完整数据,但这里只发了一个字段
fetch('/api/users/1001', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ nickname: '新昵称' }) // 只发一个字段
})
// 服务器可能会把其他字段清空!
错误2:混淆PUT和PATCH
// PUT:完整替换,发送所有字段
// PATCH:部分更新,只发送需要修改的字段
// ✅ 如果只改昵称,用PATCH
fetch('/api/users/1001', {
method: 'PATCH', // 不是PUT
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ nickname: '新昵称' })
})
错误3:PUT请求幂等性理解错误 PUT应该是幂等的,但如果你前端不做去重,用户快速点击多次提交,可能会导致多次请求。建议前端做防抖或禁用按钮。
// ✅ 正确!添加防重复提交
let isSubmitting = false;
function updateUserProfile(data) {
if (isSubmitting) return; // 防止重复提交
isSubmitting = true;
fetch('/api/users/1001', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
})
.then(res => res.json())
.then(data => {
console.log('更新成功');
})
.catch(error => {
console.error('更新失败', error);
})
.finally(() => {
isSubmitting = false; // 请求完成后恢复
});
}
DELETE请求:删除,小心驶得万年船
特点
- 幂等性:多次删除同一资源,结果相同(删除后就是不存在)。
- 无请求体:通常不需要发送数据,资源ID在URL中指定。
- 不可逆:删除后数据可能永久丢失。
典型场景
- 删除评论
- 删除用户账号
- 删除商品
- 删除订单
代码示例
// 删除文章
function deleteArticle(articleId) {
// 二次确认
if (!confirm('确定要删除这篇文章吗?删除后无法恢复!')) {
return;
}
fetch(`/api/articles/${articleId}`, {
method: 'DELETE',
headers: {
'Authorization': 'Bearer token123'
}
})
.then(response => {
if (response.status === 200 || response.status === 204) {
console.log('删除成功');
// 从页面移除该文章
removeArticleFromDOM(articleId);
// 显示成功提示
showToast('文章已删除', 'success');
} else {
throw new Error('删除失败');
}
})
.catch(error => {
console.error('删除失败:', error);
showToast('删除失败,请重试', 'error');
});
}
// 从DOM中移除元素
function removeArticleFromDOM(articleId) {
const articleEl = document.querySelector(`[data-article-id="${articleId}"]`);
if (articleEl) {
articleEl.remove();
}
}
常见错误排查
错误1:用GET删除数据
// ❌ 严重错误!GET不应该有副作用
fetch('/api/articles/123', { method: 'GET' }) // 这会被缓存,也可能被预加载
// ✅ 正确!用DELETE
fetch('/api/articles/123', { method: 'DELETE' })
错误2:忘记二次确认
// ❌ 错误!没有确认就删除
fetch(`/api/articles/${id}`, { method: 'DELETE' })
// ✅ 正确!添加确认
if (!confirm('确定删除?')) return;
fetch(`/api/articles/${id}`, { method: 'DELETE' })
错误3:DELETE请求携带大量数据
// ❌ 错误!DELETE不需要请求体
fetch('/api/articles', {
method: 'DELETE',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ids: [1, 2, 3] }) // 不推荐
})
// ✅ 正确!通过URL或查询参数指定要删除的ID
fetch('/api/articles?ids=1,2,3', { method: 'DELETE' })
// 或者使用DELETE + body(部分服务器支持,但标准不推荐)
四大方法对比总结表
| 特性 | GET | POST | PUT | DELETE |
|---|---|---|---|---|
| 用途 | 查询数据 | 创建资源 | 完整更新资源 | 删除资源 |
| 幂等性 | ✅ 是 | ❌ 否 | ✅ 是 | ✅ 是 |
| 安全性 | ✅ 安全 | ⚠️ 不安全 | ⚠️ 不安全 | ⚠️ 不安全 |
| 参数位置 | URL | Body | Body | URL/无 |
| 缓存 | ✅ 可缓存 | ❌ 不可缓存 | ❌ 不可缓存 | ❌ 不可缓存 |
| 数据长度 | 受限 | 无限制 | 无限制 | 无限制 |
| 典型状态码 | 200 | 201 | 200 | 204 |
真实开发中的常见坑点(必看)
坑1:前后端不一致的Content-Type
// 前端发送JSON
fetch('/api/data', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'test' })
})
// 后端错误配置(Java Spring Boot示例)
@PostMapping("/data")
public ResponseEntity<?> getData(@RequestBody User user) {
// 如果Content-Type不对,这里会报415 Unsupported Media Type错误
}
// 正确做法:前后端约定好Content-Type
// 或者后端支持多种类型:@PostMapping consumes = {"application/json", "application/x-www-form-urlencoded"}
坑2:CORS预检请求失败
当你发送复杂请求(如自定义Header、PUT/DELETE方法)时,浏览器会先发一个OPTIONS预检请求。
// 前端
fetch('/api/protected', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'X-Custom-Header': 'value' // 自定义Header会触发预检
},
body: JSON.stringify({ data: 'test' })
})
// 后端需要正确处理OPTIONS请求
app.options('/api/protected', (req, res) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Custom-Header');
res.sendStatus(200);
});
坑3:URL参数编码问题
// ❌ 错误!中文参数未编码
fetch('/api/search?q=JavaScript教程')
// ✅ 正确!使用encodeURIComponent
const query = encodeURIComponent('JavaScript教程');
fetch(`/api/search?q=${query}`)
// 或者使用URL对象
const url = new URL('/api/search', window.location.origin);
url.searchParams.set('q', 'JavaScript教程');
fetch(url.toString())
坑4:POST/PUT请求体为空或格式错误
// ❌ 错误!body为null
fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: null
})
// ❌ 错误!body未序列化
fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: { name: 'test' }
})
// ✅ 正确!
fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'test' })
})
如何选择合适的方法?(决策树)
你需要对资源做什么操作?
├── 只是查看/查询? → 用 GET
│ └── 需要传参数? → 加在URL查询字符串里
│
├── 创建新资源? → 用 POST
│ └── 数据放哪里? → 请求体(Body)
│
├── 更新现有资源?
│ ├── 需要完整替换? → 用 PUT
│ └── 只修改部分字段? → 用 PATCH
│
└── 删除资源? → 用 DELETE
└── 如何指定资源? → URL路径参数(如 /api/users/123)
给小朋友的比喻记忆法
想象你在学校图书馆:
- GET = 借书看(只是看,不改动)
- POST = 写新书交给图书馆员(创建新内容)
- PUT = 把整本书重写一遍(完全替换)
- DELETE = 把书扔掉(删除)
记住:查用GET,增用POST,改用PUT/PATCH,删用DELETE。
实际项目中的最佳实践
- 统一错误处理
function apiRequest(url, options = {}) {
return fetch(url, {
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${getAuthToken()}`
},
...options
})
.then(response => {
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return response.json();
});
}
- 使用Axios简化操作
”`javascript import axios from ‘axios’;
// GET请求 axios.get(‘/api/users’, { params: { page: 1 } }) .then(res => console.log(res.data));
// POST请求 axios.post(‘/api
