哎,说到这个,我可得跟你掏心窝子讲一讲。刚入行那会儿,我面对浏览器控制台里那些红色的Network请求,也是懵圈了好一阵子。GET、POST、PUT、DELETE,这四个家伙看着差不多,用起来却各有各的小脾气。后来学会了用Postman这个神器,才真正体会到什么叫“工欲善其事,必先利其器”。今天我就把这四年踩过的坑、咽过的苦,还有那些偷偷用的技巧,全倒给你。

先别急着动手,咱们得先把这事儿捋顺了。AJAX(Asynchronous JavaScript And XML)这玩意儿,说白了就是网页“悄悄话”的技术。你点一个按钮,不用刷新整个页面,后台就能偷偷去服务器取数据,然后局部刷新页面。这就像你去餐厅吃饭,不用每次都喊服务员过来重新点一遍全单,只是让服务员悄悄记一下你要加的那盘菜。

GET和POST,这对冤家到底谁先谁后?

很多人分不清GET和POST,我也曾经把它们的混用搞得API一团糟。咱们先说GET,这可是个“老实人”。

GET请求,顾名思义,就是去获取数据。它的特点是把所有参数都放在URL里,就像你把要买的东西写在购物清单上,直接递给收银员。因为它参数外露,所以有几个硬性规矩:

  1. 参数长度有限制:URL有长度上限(不同浏览器不一样,一般2KB左右),所以GET不能传大段文本。
  2. 会被缓存:浏览器会记住你GET过的请求,除非你加缓存控制头。
  3. 看起来透明:参数就在地址栏里,谁都能看到。

举个真实的例子,假设你在做一个查询用户列表的功能:

fetch('https://api.example.com/users?status=active&page=1&limit=10')
  .then(response => response.json())
  .then(data => console.log(data));

注意看,statuspagelimit这些参数全在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));

看到没?usernameemailpassword这些敏感信息全在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请求

  1. 新建一个Request,选“GET”。
  2. 在URL栏输入:https://api.example.com/users?status=active
  3. 点Send。
  4. 看下面的Response,能看到JSON数据、状态码、耗时。

Tip:Postman有个很方便的“Params”标签页,专门用来填查询参数,不用手动拼URL。

测试POST请求

  1. 选“POST”,输入URL。
  2. 切换到“Body”标签。
  3. 选“raw”,格式选“JSON”。
  4. 粘贴你的JSON数据:
{
  "username": "李四",
  "email": "lisi@example.com"
}
  1. 点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头:

  1. 发一个请求,比如PUT。
  2. 看Response Headers,确认有Access-Control-Allow-Origin等。
  3. 如果没有,让后端加上。

小结

今天咱们聊了AJAX的四种请求方法,它们的区别和适用场景;学会了用Postman测试各种请求,还封装了带超时重试的函数;最后搞定了最让人头疼的跨域问题。

记住几个关键点:

  • GET拿数据,POST提交数据,PUT全量更新,DELETE删除。
  • Postman是测试神器,善用环境变量和断言。
  • 超时用AbortControlleraxios,重试用指数退避。
  • 跨域让后端加CORS头,前端开发用代理。

这些知识够你应付大部分接口开发场景了。要是还有疑问,多动手测,Postman里点点、代码里跑跑,比看十遍文档都管用。加油!