SuperAgent 使用指南:轻量级 AJAX 请求库详解
概述
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-urlencoded | Node.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 设计和丰富的功能特性。通过本文的详细介绍,您应该能够:
- 快速上手:掌握基础请求的发起和处理
- 高级应用:使用超时控制、重试机制等高级功能
- 错误处理:构建健壮的错误处理体系
- 性能优化:实现请求池、缓存等性能优化策略
- 最佳实践:遵循统一的代码组织和类型安全原则
无论您是构建简单的前端应用还是复杂的后端服务,SuperAgent 都能为您提供可靠、高效的 HTTP 请求处理能力。其插件系统和扩展性使得它能够适应各种复杂的业务场景,是现代 Web 开发中不可或缺的工具之一。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



