SuperAgent 使用指南:轻量级 AJAX 请求库详解

SuperAgent 使用指南:轻量级 AJAX 请求库详解

【免费下载链接】superagent Ajax for Node.js and browsers (JS HTTP client). Maintained for @forwardemail, @ladjs, @spamscanner, @breejs, @cabinjs, and @lassjs. 【免费下载链接】superagent 项目地址: https://gitcode.com/gh_mirrors/su/superagent

概述

SuperAgent 是一个轻量级、渐进式的客户端 HTTP 请求库,同时也是一个 Node.js 模块,提供相同的 API。它支持众多高级 HTTP 客户端特性,是处理 AJAX 请求的理想选择。该库由 Forward Email 和 Lad 项目维护,具有优雅的流式 API 和丰富的功能集。

核心特性

特性描述支持平台
流式 API链式调用,代码简洁易读Node.js & Browser
自动 JSON 解析自动处理 JSON 请求和响应Node.js & Browser
表单数据处理支持 multipart/form-data 和 x-www-form-urlencodedNode.js & Browser
超时控制支持响应超时、截止时间和上传超时Node.js & Browser
重试机制内置智能重试逻辑Node.js & Browser
插件系统易于扩展的插件架构Node.js & Browser
跨域请求支持 CORS 和凭据传输Browser
HTTP/2 支持原生 HTTP/2 协议支持Node.js

安装与配置

Node.js 环境

# 使用 npm
npm install superagent

# 使用 yarn
yarn add superagent

浏览器环境

<!-- 使用 jsDelivr CDN -->
<script src="https://cdn.jsdelivr.net/npm/superagent"></script>

<!-- 或者使用 unpkg -->
<script src="https://unpkg.com/superagent"></script>

基础用法

发起 GET 请求

const superagent = require('superagent');

// 回调函数方式
superagent
  .get('/api/users')
  .query({ page: 1, limit: 10 })
  .end((err, res) => {
    if (err) {
      console.error('请求失败:', err);
      return;
    }
    console.log('响应数据:', res.body);
  });

// Promise 方式
superagent
  .get('/api/users')
  .then(res => {
    console.log('响应数据:', res.body);
  })
  .catch(err => {
    console.error('请求失败:', err);
  });

// async/await 方式
async function fetchUsers() {
  try {
    const res = await superagent.get('/api/users');
    console.log('响应数据:', res.body);
  } catch (err) {
    console.error('请求失败:', err);
  }
}

发起 POST 请求

// 发送 JSON 数据
superagent
  .post('/api/users')
  .send({ name: 'John', email: 'john@example.com' })
  .set('Content-Type', 'application/json')
  .set('Authorization', 'Bearer token123')
  .then(res => {
    console.log('创建成功:', res.body);
  });

// 发送表单数据
superagent
  .post('/api/login')
  .type('form')
  .send({ username: 'admin', password: 'secret' })
  .then(res => {
    console.log('登录成功:', res.body);
  });

高级功能

超时控制

superagent
  .get('/api/slow-endpoint')
  .timeout({
    response: 5000,    // 响应超时 5 秒
    deadline: 30000,   // 总超时 30 秒
    upload: 10000      // 上传超时 10 秒
  })
  .then(res => {
    console.log('请求成功');
  })
  .catch(err => {
    if (err.timeout) {
      console.error('请求超时');
    } else {
      console.error('其他错误:', err);
    }
  });

重试机制

superagent
  .get('/api/unstable-endpoint')
  .retry(3, (err, res) => {
    // 自定义重试逻辑
    return err && err.code === 'ECONNRESET';
  })
  .then(res => {
    console.log('最终成功');
  });

文件上传

const fs = require('fs');

// 上传单个文件
superagent
  .post('/api/upload')
  .attach('avatar', 'path/to/avatar.jpg')
  .field('username', 'john_doe')
  .then(res => {
    console.log('上传成功');
  });

// 上传多个文件
superagent
  .post('/api/upload-multiple')
  .attach('images', 'image1.jpg')
  .attach('images', 'image2.jpg')
  .attach('documents', 'document.pdf')
  .then(res => {
    console.log('多文件上传成功');
  });

响应处理

响应对象结构

superagent
  .get('/api/data')
  .then(res => {
    console.log('状态码:', res.status);
    console.log('状态消息:', res.statusText);
    console.log('响应头:', res.headers);
    console.log('响应体:', res.body);
    console.log('文本内容:', res.text);
    console.log('响应类型:', res.type);
    console.log('字符集:', res.charset);
    console.log('响应长度:', res.length);
  });

自定义响应解析器

// 自定义 XML 解析器
superagent
  .get('/api/xml-data')
  .parse((res, fn) => {
    res.text = '';
    res.setEncoding('utf8');
    res.on('data', chunk => {
      res.text += chunk;
    });
    res.on('end', () => {
      try {
        // 简单的 XML 解析
        const parsed = parseXml(res.text);
        fn(null, parsed);
      } catch (err) {
        fn(err);
      }
    });
  })
  .then(data => {
    console.log('解析后的 XML 数据:', data);
  });

错误处理

统一错误处理模式

class ApiClient {
  constructor(baseURL) {
    this.baseURL = baseURL;
  }

  async request(method, path, data = null, options = {}) {
    try {
      let request = superagent[method.toLowerCase()](`${this.baseURL}${path}`);
      
      // 设置请求头
      if (options.headers) {
        request = request.set(options.headers);
      }
      
      // 设置查询参数
      if (options.query) {
        request = request.query(options.query);
      }
      
      // 发送数据
      if (data) {
        request = request.send(data);
      }
      
      // 设置超时
      if (options.timeout) {
        request = request.timeout(options.timeout);
      }
      
      const response = await request;
      return response.body;
      
    } catch (error) {
      this.handleError(error);
      throw error;
    }
  }

  handleError(error) {
    if (error.response) {
      // 服务器响应错误
      console.error('服务器错误:', {
        status: error.response.status,
        data: error.response.body,
        headers: error.response.headers
      });
    } else if (error.request) {
      // 请求未发出
      console.error('网络错误:', error.message);
    } else if (error.timeout) {
      // 请求超时
      console.error('请求超时');
    } else {
      // 其他错误
      console.error('未知错误:', error.message);
    }
  }
}

插件系统

使用现有插件

const superagent = require('superagent');
const nocache = require('superagent-no-cache');
const prefix = require('superagent-prefix')('/api/v1');

// 使用多个插件
superagent
  .get('/users')
  .use(prefix)          // 添加 API 前缀
  .use(nocache)         // 禁用缓存
  .query({ active: true })
  .then(res => {
    console.log(res.body);
  });

创建自定义插件

// 认证插件
function authPlugin(token) {
  return function(request) {
    request.set('Authorization', `Bearer ${token}`);
  };
}

// 日志插件
function loggerPlugin() {
  return function(request) {
    const startTime = Date.now();
    
    request.on('response', (res) => {
      const duration = Date.now() - startTime;
      console.log(`${request.method} ${request.url} - ${res.status} (${duration}ms)`);
    });
    
    request.on('error', (err) => {
      console.error(`${request.method} ${request.url} - ERROR: ${err.message}`);
    });
  };
}

// 使用自定义插件
superagent
  .get('/api/protected')
  .use(authPlugin('user-token-123'))
  .use(loggerPlugin())
  .then(res => {
    console.log('受保护资源:', res.body);
  });

性能优化

请求池管理

class RequestPool {
  constructor(maxConcurrent = 5) {
    this.maxConcurrent = maxConcurrent;
    this.queue = [];
    this.active = 0;
  }

  async add(requestFn) {
    return new Promise((resolve, reject) => {
      this.queue.push({ requestFn, resolve, reject });
      this.processQueue();
    });
  }

  processQueue() {
    while (this.queue.length > 0 && this.active < this.maxConcurrent) {
      this.active++;
      const { requestFn, resolve, reject } = this.queue.shift();
      
      requestFn()
        .then(resolve)
        .catch(reject)
        .finally(() => {
          this.active--;
          this.processQueue();
        });
    }
  }
}

// 使用请求池
const pool = new RequestPool(3);

async function makeConcurrentRequests(urls) {
  const results = await Promise.all(
    urls.map(url => 
      pool.add(() => superagent.get(url))
    )
  );
  return results.map(res => res.body);
}

缓存策略

const cache = new Map();

function cachedRequest(url, options = {}) {
  const cacheKey = JSON.stringify({ url, options });
  const cached = cache.get(cacheKey);
  
  if (cached && Date.now() - cached.timestamp < (options.ttl || 60000)) {
    return Promise.resolve(cached.data);
  }
  
  return superagent
    .get(url)
    .query(options.query || {})
    .set(options.headers || {})
    .then(res => {
      const data = res.body;
      cache.set(cacheKey, {
        data,
        timestamp: Date.now()
      });
      return data;
    });
}

最佳实践

1. 统一的 API 客户端

class ApiService {
  constructor(config) {
    this.config = config;
    this.interceptors = [];
  }

  addInterceptor(interceptor) {
    this.interceptors.push(interceptor);
  }

  async request(method, endpoint, data = null) {
    let request = superagent[method](`${this.config.baseURL}${endpoint}`);
    
    // 应用拦截器
    for (const interceptor of this.interceptors) {
      request = interceptor(request);
    }
    
    // 设置默认头
    request = request
      .set('Content-Type', 'application/json')
      .set('Accept', 'application/json');
    
    if (data) {
      request = request.send(data);
    }
    
    try {
      const response = await request;
      return response.body;
    } catch (error) {
      throw this.normalizeError(error);
    }
  }

  normalizeError(error) {
    if (error.response) {
      return new ApiError(
        error.response.body?.message || 'API Error',
        error.response.status,
        error.response.body
      );
    }
    return error;
  }
}

class ApiError extends Error {
  constructor(message, status, data) {
    super(message);
    this.status = status;
    this.data = data;
  }
}

2. 类型安全的请求

// 使用 JSDoc 提供类型提示
/**
 * @typedef {Object} User
 * @property {number} id
 * @property {string} name
 * @property {string} email
 */

/**
 * 获取用户列表
 * @param {number} page - 页码
 * @param {number} limit - 每页数量
 * @returns {Promise<User[]>}
 */
async function getUsers(page = 1, limit = 10) {
  const res = await superagent
    .get('/api/users')
    .query({ page, limit });
  
  return res.body;
}

/**
 * 创建新用户
 * @param {Object} userData - 用户数据
 * @param {string} userData.name - 用户名
 * @param {string} userData.email - 邮箱
 * @returns {Promise<User>}
 */
async function createUser(userData) {
  const res = await superagent
    .post('/api/users')
    .send(userData);
  
  return res.body;
}

常见问题解决方案

1. 处理大文件下载

async function downloadLargeFile(url, filePath) {
  return new Promise((resolve, reject) => {
    const writeStream = fs.createWriteStream(filePath);
    
    superagent
      .get(url)
      .on('response', (res) => {
        if (res.status !== 200) {
          reject(new Error(`下载失败: ${res.status}`));
          return;
        }
      })
      .on('error', reject)
      .pipe(writeStream)
      .on('finish', resolve)
      .on('error', reject);
  });
}

2. 处理分块上传

async function chunkedUpload(filePath, url, chunkSize = 1024 * 1024) {
  const fileStats = fs.statSync(filePath);
  const totalChunks = Math.ceil(fileStats.size / chunkSize);
  
  for (let chunkIndex = 0; chunkIndex < totalChunks; chunkIndex++) {
    const start = chunkIndex * chunkSize;
    const end = Math.min(start + chunkSize, fileStats.size);
    
    const chunkStream = fs.createReadStream(filePath, { start, end });
    
    await superagent
      .post(url)
      .attach('chunk', chunkStream)
      .field('chunkIndex', chunkIndex)
      .field('totalChunks', totalChunks)
      .field('originalName', path.basename(filePath));
  }
  
  // 通知服务器完成上传
  await superagent
    .post(`${url}/complete`)
    .send({
      originalName: path.basename(filePath),
      totalChunks: totalChunks
    });
}

3. 处理 API 限流

class RateLimitedClient {
  constructor(requestsPerSecond = 5) {
    this.interval = 1000 / requestsPerSecond;
    this.lastRequest = 0;
    this.queue = [];
  }

  async request(...args) {
    return new Promise((resolve, reject) => {
      this.queue.push({ args, resolve, reject });
      this.processQueue();
    });
  }

  processQueue() {
    if (this.queue.length === 0) return;
    
    const now = Date.now();
    const timeSinceLastRequest = now - this.lastRequest;
    
    if (timeSinceLastRequest >= this.interval) {
      this.executeRequest();
    } else {
      setTimeout(() => this.executeRequest(), this.interval - timeSinceLastRequest);
    }
  }

  executeRequest() {
    if (this.queue.length === 0) return;
    
    const { args, resolve, reject } = this.queue.shift();
    this.lastRequest = Date.now();
    
    superagent(...args)
      .then(resolve)
      .catch(reject)
      .finally(() => {
        this.processQueue();
      });
  }
}

总结

SuperAgent 作为一个轻量级但功能强大的 HTTP 客户端库,提供了优雅的 API 设计和丰富的功能特性。通过本文的详细介绍,您应该能够:

  1. 快速上手:掌握基础请求的发起和处理
  2. 高级应用:使用超时控制、重试机制等高级功能
  3. 错误处理:构建健壮的错误处理体系
  4. 性能优化:实现请求池、缓存等性能优化策略
  5. 最佳实践:遵循统一的代码组织和类型安全原则

无论您是构建简单的前端应用还是复杂的后端服务,SuperAgent 都能为您提供可靠、高效的 HTTP 请求处理能力。其插件系统和扩展性使得它能够适应各种复杂的业务场景,是现代 Web 开发中不可或缺的工具之一。

【免费下载链接】superagent Ajax for Node.js and browsers (JS HTTP client). Maintained for @forwardemail, @ladjs, @spamscanner, @breejs, @cabinjs, and @lassjs. 【免费下载链接】superagent 项目地址: https://gitcode.com/gh_mirrors/su/superagent

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值