哎,说到这个,我可得跟你掏心窝子讲一讲。刚入行那会儿,我面对浏览器控制台里那些红色的Network请求,也是懵圈了好一阵子。GET、POST、PUT、DELETE,这四个家伙看着差不多,用起来却各有各的小脾气。后来学会了用Postman这个神器,才真正体会到什么叫“工欲善其事,必先利其器”。今天我就把这四年踩过的坑、咽过的苦,还有那些偷偷用的技巧,全倒给你。
先别急着动手,咱们得先把这事儿捋顺了。AJAX(Asynchronous JavaScript And XML)这玩意儿,说白了就是网页“悄悄话”的技术。你点一个按钮,不用刷新整个页面,后台就能偷偷去服务器取数据,然后局部刷新页面。这就像你去餐厅吃饭,不用每次都喊服务员过来重新点一遍全单,只是让服务员悄悄记一下你要加的那盘菜。
GET和POST,这对冤家到底谁先谁后?
很多人分不清GET和POST,我也曾经把它们的混用搞得API一团糟。咱们先说GET,这可是个“老实人”。
GET请求,顾名思义,就是去获取数据。它的特点是把所有参数都放在URL里,就像你把要买的东西写在购物清单上,直接递给收银员。因为它参数外露,所以有几个硬性规矩:
- 参数长度有限制:URL有长度上限(不同浏览器不一样,一般2KB左右),所以GET不能传大段文本。
- 会被缓存:浏览器会记住你GET过的请求,除非你加缓存控制头。
- 看起来透明:参数就在地址栏里,谁都能看到。
举个真实的例子,假设你在做一个查询用户列表的功能:
fetch('https://api.example.com/users?status=active&page=1&limit=10')
.then(response => response.json())
.then(data => console.log(data));
注意看,status、page、limit这些参数全在URL里。这就是典型的GET。
然后说说POST,这可是个“闷葫芦”,什么都藏在身体里(请求体)不说。
POST用于提交数据,比如注册用户、提交订单。它的参数放在请求体(Body)里,URL只管地址,不管内容。
fetch('https://api.example.com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
username: '张三',
email: 'zhangsan@example.com',
password: '123456'
})
})
.then(response => response.json())
.then(data => console.log(data));
看到没?username、email、password这些敏感信息全在body里,URL干干净净。
为什么一定要区分? 因为安全!密码能放URL里吗?绝对不能!放URL里会被浏览器历史记录、服务器日志、甚至网络中间人看到。所以涉及敏感数据、大文本、文件上传,一律用POST。
PUT和DELETE,更新与删除的艺术
这两个方法经常被搞混。其实它们的区别很简单:
- PUT:全量更新。你告诉服务器“把资源1换成这个完整的版本”。如果服务器收到PUT请求,它通常期望你提供完整的资源数据。
- DELETE:删除资源。告诉服务器“删掉这个资源”。
举个例子,修改一个用户的信息:
// PUT:全量更新,提供完整数据
fetch('https://api.example.com/users/123', {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
id: 123,
username: '张三',
email: 'zhangsan_new@example.com',
password: 'newpassword'
})
});
// DELETE:删除用户
fetch('https://api.example.com/users/123', {
method: 'DELETE'
});
注意看,PUT请求体里有完整的用户信息,包括ID。而DELETE连body都没有,因为删一个东西不需要告诉服务器“我要删什么内容”,只需要告诉它“删哪个ID”。
不过现实中,有些API设计者喜欢用PATCH代替PUT,PATCH是部分更新,只传需要修改的字段。比如只改邮箱:
fetch('https://api.example.com/users/123', {
method: 'PATCH',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
email: 'zhangsan_new@example.com'
})
});
这三种更新方式的区别,就像改作文:PUT是全文重写,PATCH是只改几个错别字,DELETE是直接把作文撕了。
Postman:你的接口测试利器
讲完理论,咱们上家伙。Postman是我用过最好用的接口测试工具,没有之一。它不用写代码,点点鼠标就能发请求,还能保存、自动化、生成文档。
第一步:安装与基本界面
下载Postman(有桌面版和网页版),安装后你会看到左边是文件夹/集合,中间是写请求的地方,下面是响应区域。
测试GET请求
- 新建一个Request,选“GET”。
- 在URL栏输入:
https://api.example.com/users?status=active - 点Send。
- 看下面的Response,能看到JSON数据、状态码、耗时。
Tip:Postman有个很方便的“Params”标签页,专门用来填查询参数,不用手动拼URL。
测试POST请求
- 选“POST”,输入URL。
- 切换到“Body”标签。
- 选“raw”,格式选“JSON”。
- 粘贴你的JSON数据:
{
"username": "李四",
"email": "lisi@example.com"
}
- 点Send。
注意:一定要选JSON,不然服务器可能解析不出来。Postman会自动加Content-Type: application/json头,你也可以在“Headers”标签里手动加。
测试PUT和DELETE
操作跟POST差不多,只是换一下方法。PUT记得传完整数据,DELETE通常不需要Body。
Postman的高级功能
环境变量:如果你有多套环境(测试、生产),可以用环境变量。比如设一个base_url,请求时写{{base_url}}/users,切换环境一键改地址。
断言测试:在“Tests”标签写JavaScript,自动验证响应。比如:
pm.test("Status code is 200", () => {
pm.response.to.have.status(200);
});
pm.test("Response has username", () => {
const json = pm.response.json();
pm.expect(json.username).to.eql("李四");
});
每次Send都会自动跑这些测试,成功绿色,失败红色。
集合运行器:把多个请求放进一个集合,用Runner批量执行,还能设置延迟、迭代次数。
导出集合:方便团队共享,或者导入到CI/CD流水线。
超时重试:别让请求干等
接口超时是个很烦人的事。网络抖动、服务器慢,请求卡在那儿,用户干瞪眼。咱们得学会优雅处理。
原生fetch的超时控制
原生fetch没有内置超时,但可以用AbortController实现:
function fetchWithTimeout(url, options = {}, timeout = 5000) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
return fetch(url, {
...options,
signal: controller.signal
}).finally(() => clearTimeout(timeoutId));
}
用法:
fetchWithTimeout('https://api.example.com/slow-endpoint', {
method: 'GET'
}, 3000) // 3秒超时
.then(res => res.json())
.then(data => console.log(data))
.catch(err => {
if (err.name === 'AbortError') {
console.log('请求超时了');
} else {
console.error('其他错误:', err);
}
});
带重试的封装
光超时不行,有时候需要重试。比如网络抖动,第一次失败了,等一会儿再试。
async function fetchWithRetry(url, options = {}, retries = 3, delay = 1000) {
for (let i = 0; i < retries; i++) {
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 5000);
const response = await fetch(url, {
...options,
signal: controller.signal
}).finally(() => clearTimeout(timeoutId));
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
if (error.name === 'AbortError') {
console.warn(`请求超时,第${i + 1}次重试...`);
} else if (i === retries - 1) {
console.error('重试次数用尽,放弃', error);
throw error;
} else {
console.warn(`请求失败,第${i + 1}次重试...`, error);
}
// 等待一段时间后重试,指数退避
await new Promise(resolve => setTimeout(resolve, delay * Math.pow(2, i)));
}
}
}
这段代码的关键是指数退避:第一次等1秒,第二次等2秒,第三次等4秒。这样不会一下子把服务器压垮。
Axios:更省心的选择
如果你不想自己封装,axios库原生支持超时和重试:
import axios from 'axios';
const instance = axios.create({
timeout: 5000,
retry: 3,
retryDelay: 1000
});
// 自定义拦截器实现重试
instance.interceptors.response.use(
response => response,
async error => {
const { config } = error;
if (!config.__retryCount) {
config.__retryCount = 0;
}
if (config.__retryCount < config.retry) {
config.__retryCount++;
return new Promise(resolve => {
setTimeout(() => {
resolve(instance(config));
}, config.retryDelay);
});
}
return Promise.reject(error);
}
);
用的时候就很简单:
instance.get('/users').then(res => console.log(res.data));
跨域问题:浏览器的“安全墙”
跨域(CORS)是前端最头疼的问题之一。简单说,浏览器规定:你的网页(比如http://localhost:3000)不能直接访问另一个域名的接口(比如https://api.example.com),除非服务器明确说“我允许你访问”。
为什么会跨域?
同源策略:协议、域名、端口,三个都一样才算同源。只要有一个不同,就是跨域。
常见报错:
Access to fetch at 'https://api.example.com/users' from origin 'http://localhost:3000'
has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
解决方案一:后端设置响应头(最正宗)
服务器返回的响应里加这些头:
Access-Control-Allow-Origin: * // 允许所有来源,或者指定域名如 http://localhost:3000
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
以Node.js + Express为例:
const express = require('express');
const app = express();
// 全局中间件
app.use((req, res, next) => {
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');
// 预检请求直接返回
if (req.method === 'OPTIONS') {
return res.sendStatus(200);
}
next();
});
app.get('/users', (req, res) => {
res.json([{ id: 1, username: '张三' }]);
});
app.listen(3000);
注意:浏览器发复杂请求(比如带自定义头、PUT/DELETE)时,会先发一个OPTIONS预检请求,服务器必须响应200,否则正式请求会被拦截。所以上面代码里专门处理了OPTIONS。
解决方案二:前端代理(开发环境最常用)
如果后端改不了,或者你不想动后端,可以在前端项目里配个代理。比如用Vite:
// vite.config.js
export default {
server: {
proxy: {
'/api': {
target: 'https://api.example.com',
changeOrigin: true,
rewrite: path => path.replace(/^\/api/, '')
}
}
}
}
然后前端请求写相对路径:
fetch('/api/users') // 会被代理到 https://api.example.com/users
这样浏览器以为请求的是同源,就不会有跨域问题。生产环境建议还是让后端配CORS头,或者用Nginx反向代理。
解决方案三:JSONP(古老但有效)
JSONP利用<script>标签不受同源限制的特性。但只能用于GET,而且需要后端支持。现在很少用了,知道有这回事就行。
function jsonp(url, callback) {
const script = document.createElement('script');
const param = 'callback=' + callback;
script.src = url + '?' + param;
document.body.appendChild(script);
}
// 后端会返回:callbackName({...data...})
jsonp('https://api.example.com/users?callback=handleUsers', 'handleUsers');
window.handleUsers = (data) => {
console.log(data);
};
实战:用Postman模拟跨域场景
Postman里测试接口,其实不受浏览器同源策略限制。但你想验证CORS配置是否正确,得用真实浏览器。
不过,你可以在Postman里检查响应头里有没有正确的CORS头:
- 发一个请求,比如PUT。
- 看Response Headers,确认有
Access-Control-Allow-Origin等。 - 如果没有,让后端加上。
小结
今天咱们聊了AJAX的四种请求方法,它们的区别和适用场景;学会了用Postman测试各种请求,还封装了带超时重试的函数;最后搞定了最让人头疼的跨域问题。
记住几个关键点:
- GET拿数据,POST提交数据,PUT全量更新,DELETE删除。
- Postman是测试神器,善用环境变量和断言。
- 超时用
AbortController或axios,重试用指数退避。 - 跨域让后端加CORS头,前端开发用代理。
这些知识够你应付大部分接口开发场景了。要是还有疑问,多动手测,Postman里点点、代码里跑跑,比看十遍文档都管用。加油!
