跳到主要内容
JavaScript Node.js 大前端
SuperAgent HTTP 请求库快速上手指南 SuperAgent 是一个轻量级、渐进式的 HTTP 请求库,支持链式调用 API。其核心原理、在浏览器和 Node.js 环境下的安装配置、基础用法(GET/POST 等)、高级功能(请求头、超时设置)、异常处理机制以及常见错误解决方案。内容涵盖 API 客户端封装、性能优化策略及最佳实践,旨在帮助开发者高效构建前后端 HTTP 请求逻辑,避免跨域、并发控制等常见问题。
Elasticer 发布于 2026/4/6 更新于 2026/5/22 26 浏览SuperAgent HTTP 请求库快速上手指南
引言
SuperAgent 是一个轻量级、渐进式的 HTTP 请求库,由 TJ Holowaychuk 开发。它提供了链式调用的 API,让发送 HTTP 请求变得简单高效。
学习目标
掌握 SuperAgent 的基本使用方法
理解 SuperAgent 的核心原理和工作机制
学会在浏览器和 Node.js 环境中使用 SuperAgent
避免常见的坑和错误
掌握高级用法和最佳实践
1. SuperAgent 核心原理解析
1.1 什么是 SuperAgent?
SuperAgent 是一个轻量级、渐进式的 HTTP 请求库。它提供了链式调用的 API,让发送 HTTP 请求变得像说话一样简单。
+
| 浏览器环境 || Node.js 环境 || 统一 API 层 |
| (XMLHttpRequest)|| (HTTP 模块) || (SuperAgent) |
+
\ / /
\ / /
\ / /
\ / /
\ / /
\ / /
\/ /
+
| 统一接口 |
|<
| (链式调用) |
+
1.2 SuperAgent 的核心设计理念
SuperAgent 采用链式调用的设计模式,每个方法都返回 Request 对象实例,这样就可以像搭积木一样组合各种操作:
request.post ('/api/users' )
.set ('Content-Type' , 'application/json' )
.send ({name : 'User' , age : 18 })
.end ((err, res ) => {
if (err) throw err;
console .log (res.body );
});
2. 环境搭建与安装配置
2.1 浏览器环境安装
npm install superagent
yarn add superagent
<script src="https://cdn.jsdelivr.net/npm/superagent" ></script>
2.2 Node.js 环境安装
2.3 基本引入方式
import request from 'superagent' ;
const request = require ('superagent' );
3. 基础用法详解
3.1 GET 请求
request.get ('/api/users' ).end ((err, res ) => {
if (err) {
console .error ('请求出错了:' , err);
return ;
}
console .log ('响应数据:' , res.body );
});
request.get ('/api/users' )
.query ({page : 1 , limit : 10 })
.query ({sort : 'name' })
.end ((err, res ) => {
});
request.get ('/api/users' )
.then (res => {
console .log (res.body );
})
.catch (err => {
console .error (err);
});
3.2 POST 请求
request.post ('/api/users' )
.send ({name : 'User' , email : '[email protected] ' })
.set ('Content-Type' , 'application/json' )
.end ((err, res ) => {
});
request.post ('/api/users' )
.type ('form' )
.send ({name : 'User' , email : '[email protected] ' })
.end ((err, res ) => {
});
request.post ('/api/upload' )
.attach ('avatar' , '/path/to/avatar.jpg' )
.field ('name' , 'User' )
.end ((err, res ) => {
});
3.3 其他 HTTP 方法
request.put ('/api/users/123' ).send ({name : 'New User' }).end (callback);
request.del ('/api/users/123' ).end (callback);
request.patch ('/api/users/123' ).send ({age : 19 }).end (callback);
4. 高级功能与配置
4.1 请求头设置
request.get ('/api/users' )
.set ('Authorization' , 'Bearer token123' )
.set ('X-Custom-Header' , 'custom-value' )
.end (callback);
request.get ('/api/users' )
.set ({'Authorization' : 'Bearer token123' , 'Content-Type' : 'application/json' , 'X-API-Key' : 'your-api-key' })
.end (callback);
4.2 查询参数处理
request.get ('/api/search' )
.query ({q : 'javascript' })
.query ({page : 1 })
.query ({limit : 20 })
.end (callback);
request.get ('/api/search' )
.query ({q : 'javascript' , page : 1 , limit : 20 })
.end (callback);
request.get ('/api/search' )
.query ('q=javascript&page=1&limit=20' )
.end (callback);
4.3 超时设置
request.get ('/api/users' )
.timeout (5000 )
.end ((err, res ) => {
if (err && err.timeout ) {
console .log ('请求超时了!' );
}
});
request.get ('/api/users' )
.timeout ({response : 5000 , deadline : 60000 })
.end (callback);
5. 异常处理与错误捕获
5.1 错误类型分类 错误类型 描述 示例场景 err.timeout请求超时 网络慢或服务器无响应 err.crossDomain跨域错误 违反同源策略 err.abort请求被中止 主动调用 req.abort() err.response服务器返回错误状态码 404, 500 等
5.2 错误处理最佳实践 request.get ('/api/users' ).end ((err, res ) => {
if (err) {
if (err.timeout ) {
console .log ('请求超时,请检查网络连接' );
} else if (err.crossDomain ) {
console .log ('跨域请求被阻止' );
} else if (err.abort ) {
console .log ('请求被取消' );
} else {
console .log ('其他错误:' , err.message );
}
return ;
}
if (res.status === 200 ) {
console .log ('请求成功:' , res.body );
} else {
console .log ('服务器返回错误状态:' , res.status );
}
});
6. 流程图解析 SuperAgent 工作机制 +
| 开始请求构建 |
+
| v
+
| 设置请求方法 |
| (GET / POST/ PUT 等)|
+
| v
+
| 设置请求 URL |
+
| v
+
| 添加请求头 |
+
| v
+
| 设置请求体数据 |
+
| v
+
| 设置查询参数 |
+
| v
+
| 发送网络请求 |
+
| v
+
| 等待服务器响应 |
+
| v
+
| 处理响应数据 |
+
| v
+
| 执行回调函数 |
+
7. 常见错误与解决方案
7.1 忘记调用 .end() 问题 :写了半天请求,忘了调用 .end(),结果没有返回。
request.get ('/api/users' );
request.get ('/api/users' ).end ((err, res ) => {
});
7.2 混淆 .send() 和 .field() 问题 :上传文件时用错了方法,导致文件变成了字符串。
request.post ('/api/upload' ).send ('file' , fileInput.files [0 ]);
request.post ('/api/upload' )
.field ('name' , 'User' )
.attach ('file' , fileInput.files [0 ])
.end (callback);
7.3 跨域请求不设置 CORS
request.get ('http://api.example.com/users' )
.withCredentials ()
.end (callback);
7.4 Promise 和 Callback 混用
request.get ('/api/users' )
.end ((err, res ) => {})
.then (res => {});
request.get ('/api/users' ).end (callback);
request.get ('/api/users' ).then (res => {}).catch (err => {});
7.5 请求头设置 Content-Type 错误
request.post ('/api/users' )
.set ('Content-Type' , 'application/json' )
.send ({name : 'User' });
request.post ('/api/users' )
.type ('form' )
.send ({name : 'User' });
7.6 忽略 HTTPS 证书验证
request.get ('https://self-signed.badssl.com/' )
.ca (null )
.end (callback);
process.env .NODE_TLS_REJECT_UNAUTHORIZED = '0' ;
7.7 文件上传路径错误
const fileInput = document .getElementById ('fileInput' );
request.post ('/api/upload' )
.attach ('file' , fileInput.files [0 ])
.end (callback);
request.post ('/api/upload' )
.attach ('file' , '/path/to/file.txt' )
.end (callback);
7.8 查询参数编码问题
request.get ('/api/search' )
.query ({q : '用户的博客' })
.end (callback);
request.get ('/api/search' )
.query ({q : encodeURIComponent ('用户的博客' )})
.end (callback);
7.9 并发请求处理不当
const urls = ['/api/data1' , '/api/data2' ];
const promises = urls.map (url => request.get (url));
Promise .all (promises).then (results => {
});
async function limitedRequest (urls, limit = 5 ) {
const results = [];
for (let i = 0 ; i < urls.length ; i += limit) {
const batch = urls.slice (i, i + limit);
const batchPromises = batch.map (url => request.get (url));
const batchResults = await Promise .all (batchPromises);
results.push (...batchResults);
}
return results;
}
7.10 忽略错误处理
request.get ('/api/users' ).end ((err, res ) => {
console .log (res.body );
});
request.get ('/api/users' ).end ((err, res ) => {
if (err) {
console .error ('请求失败:' , err);
return ;
}
console .log (res.body );
});
8. SuperAgent 方法速查表
8.1 HTTP 方法相关 方法 用途 示例 request.get(url)GET 请求 request.get('/api/users')request.post(url)POST 请求 request.post('/api/users')request.put(url)PUT 请求 request.put('/api/users/1')request.patch(url)PATCH 请求 request.patch('/api/users/1')request.delete(url)DELETE 请求 request.delete('/api/users/1')request.del(url)DELETE 请求别名 request.del('/api/users/1')request.head(url)HEAD 请求 request.head('/api/users')request.options(url)OPTIONS 请求 request.options('/api/users')
8.2 请求配置相关 方法 用途 示例 .set(field, val)设置请求头 .set('Content-Type', 'application/json').set(obj)批量设置请求头 .set({ 'Content-Type': 'application/json' }).query(val)添加查询参数 .query({ page: 1, limit: 10 }).send(data)设置请求体数据 .send({ name: 'User' }).type(type)设置 Content-Type .type('json').accept(type)设置 Accept 头 .accept('json').timeout(ms)设置超时时间 .timeout(5000).auth(user, pass)设置基本认证 .auth('user', 'pass').withCredentials()携带 cookies .withCredentials().retry(count)设置重试次数 .retry(3)
8.3 文件上传相关 方法 用途 示例 .attach(field, file, filename)附加文件 .attach('avatar', file, 'photo.jpg').field(name, val)添加表单字段 .field('name', 'User').field(obj)批量添加表单字段 .field({ name: 'User', age: 18 })
9. 实战案例:构建完整的 API 客户端
9.1 基础 API 客户端封装 class ApiClient {
constructor (baseUrl ) {
this .baseUrl = baseUrl;
}
request (method, url, data = null , options = {} ) {
const req = request[method.toLowerCase ()](`${this .baseUrl} ${url} ` );
if (options.headers ) { req.set (options.headers ); }
if (options.query ) { req.query (options.query ); }
if (data) { req.send (data); }
if (options.timeout ) { req.timeout (options.timeout ); }
return new Promise ((resolve, reject ) => {
req.end ((err, res ) => {
if (err) {
reject (err);
} else {
resolve (res);
}
});
});
}
get (url, options = {} ) {
return this .request ('GET' , url, null , options);
}
post (url, data, options = {} ) {
return this .request ('POST' , url, data, options);
}
put (url, data, options = {} ) {
return this .request ('PUT' , url, data, options);
}
delete (url, options = {} ) {
return this .request ('DELETE' , url, null , options);
}
}
const api = new ApiClient ('https://api.example.com' );
api.get ('/users' , {query : {page : 1 , limit : 10 }, timeout : 5000 })
.then (res => {
console .log ('用户列表:' , res.body );
})
.catch (err => {
console .error ('获取用户列表失败:' , err);
});
api.post ('/users' , {name : 'User' , email : '[email protected] ' }, {headers : {'Authorization' : 'Bearer token123' }})
.then (res => {
console .log ('创建用户成功:' , res.body );
})
.catch (err => {
console .error ('创建用户失败:' , err);
});
9.2 带拦截器的高级客户端 class AdvancedApiClient {
constructor (baseUrl ) {
this .baseUrl = baseUrl;
this .requestInterceptors = [];
this .responseInterceptors = [];
}
useRequestInterceptor (interceptor ) {
this .requestInterceptors .push (interceptor);
}
useResponseInterceptor (interceptor ) {
this .responseInterceptors .push (interceptor);
}
async runRequestInterceptors (config ) {
let interceptedConfig = {...config};
for (const interceptor of this .requestInterceptors ) {
interceptedConfig = await interceptor (interceptedConfig);
}
return interceptedConfig;
}
async runResponseInterceptors (response ) {
let interceptedResponse = response;
for (const interceptor of this .responseInterceptors ) {
interceptedResponse = await interceptor (interceptedResponse);
}
return interceptedResponse;
}
async request (method, url, data = null , options = {} ) {
let config = { method, url : `${this .baseUrl} ${url} ` , data, options };
config = await this .runRequestInterceptors (config);
const req = request[config.method .toLowerCase ()](config.url );
if (config.options .headers ) { req.set (config.options .headers ); }
if (config.options .query ) { req.query (config.options .query ); }
if (config.data ) { req.send (config.data ); }
if (config.options .timeout ) { req.timeout (config.options .timeout ); }
try {
const res = await new Promise ((resolve, reject ) => {
req.end ((err, response ) => {
if (err) reject (err);
else resolve (response);
});
});
const finalResponse = await this .runResponseInterceptors (res);
return finalResponse;
} catch (error) {
throw error;
}
}
get (url, options = {} ) { return this .request ('GET' , url, null , options); }
post (url, data, options = {} ) { return this .request ('POST' , url, data, options); }
put (url, data, options = {} ) { return this .request ('PUT' , url, data, options); }
delete (url, options = {} ) { return this .request ('DELETE' , url, null , options); }
}
const advancedApi = new AdvancedApiClient ('https://api.example.com' );
advancedApi.useRequestInterceptor (async (config) => {
const token = localStorage .getItem ('authToken' );
if (token) {
config.options .headers = {...config.options .headers , 'Authorization' : `Bearer ${token} ` };
}
return config;
});
advancedApi.useResponseInterceptor (async (response) => {
if (response.status >= 400 ) {
throw new Error (`请求失败:${response.status} ${response.text} ` );
}
return response;
});
advancedApi.get ('/users' )
.then (res => {
console .log ('用户列表:' , res.body );
})
.catch (err => {
console .error ('请求出错:' , err.message );
});
10. 性能优化与最佳实践
10.1 请求缓存策略 class CachedApiClient {
constructor ( ) {
this .cache = new Map ();
}
async getCached (url, options = {} ) {
const cacheKey = `${url} ?${JSON .stringify(options.query || {})} ` ;
const cacheTimeout = options.cacheTimeout || 300000 ;
if (this .cache .has (cacheKey)) {
const cached = this .cache .get (cacheKey);
if (Date .now () - cached.timestamp < cacheTimeout) {
return cached.data ;
}
}
const res = await request.get (url).query (options.query || {});
const data = res.body ;
this .cache .set (cacheKey, { data, timestamp : Date .now () });
return data;
}
clearCache (url ) {
if (url) {
for (const key of this .cache .keys ()) {
if (key.startsWith (url)) {
this .cache .delete (key);
}
}
} else {
this .cache .clear ();
}
}
}
const cachedApi = new CachedApiClient ();
cachedApi.getCached ('/api/users' , {query : {page : 1 }}).then (data => console .log ('第一次:' , data));
setTimeout (() => {
cachedApi.getCached ('/api/users' , {query : {page : 1 }}).then (data => console .log ('使用缓存:' , data));
}, 1000 );
10.2 请求队列管理 class RequestQueue {
constructor (concurrency = 3 ) {
this .concurrency = concurrency;
this .running = 0 ;
this .queue = [];
}
add (requestFn ) {
return new Promise ((resolve, reject ) => {
this .queue .push ({ requestFn, resolve, reject });
this .process ();
});
}
async process ( ) {
if (this .running >= this .concurrency || this .queue .length === 0 ) {
return ;
}
this .running ++;
const { requestFn, resolve, reject } = this .queue .shift ();
try {
const result = await requestFn ();
resolve (result);
} catch (error) {
reject (error);
} finally {
this .running --;
this .process ();
}
}
}
const queue = new RequestQueue (2 );
const urls = ['/api/data1' , '/api/data2' , '/api/data3' , '/api/data4' , '/api/data5' ];
Promise .all (
urls.map (url => queue.add (() => request.get (url).then (res => res.body )))
).then (results => {
console .log ('所有请求完成:' , results);
});
10.3 最佳实践总结 实践项 说明 示例代码 🔧 统一错误处理 集中处理所有请求错误 创建全局错误处理拦截器 ⚡ 合理使用缓存 对于不常变化的数据使用缓存 用户信息、配置数据等 🚦 控制并发数量 避免同时发送过多请求 使用请求队列限制并发 📝 规范请求日志 记录请求和响应信息 便于调试和监控 🔒 安全处理认证 正确处理认证信息 使用拦截器自动添加 token 📈 监控请求性能 记录请求耗时等指标 用于性能优化 🔄 合理重试机制 对于可重试的错误自动重试 网络波动等情况 🧹 及时清理资源 避免内存泄漏 取消未完成的请求 🎯 类型安全 使用 TypeScript 提供类型支持 提高代码可靠性 📚 文档化接口 为 API 客户端编写文档 便于团队协作
总结 SuperAgent 是一个优秀的 HTTP 客户端库,链式调用的设计让代码简洁优雅,功能强大且兼容性强,既能在浏览器中使用,也能在 Node.js 中运行。建议开发者遵循规范,妥善处理错误与性能优化,充分利用其特性提升开发效率。
相关免费在线工具 Keycode 信息 查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
Escape 与 Native 编解码 JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
JavaScript / HTML 格式化 使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
JavaScript 压缩与混淆 Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online