0.2.1 • Published 5 months ago
@aqcq/express-reply v0.2.1
@aqcq/express-reply
Express Reply middleware,为 Express Response 对象添加统一的响应方法和自动错误捕获功能。
特色功能
✨ 自动错误捕获 - 无需手动调用 next(error),直接 throw 即可
🚀 统一响应格式 - 标准化成功和错误响应结构
📝 完整 TypeScript 支持 - 类型提示和自动补全
🔧 灵活配置 - 支持预定义和自定义错误类型
安装
npm install @aqcq/express-reply
# 或
yarn add @aqcq/express-reply核心功能
🎯 自动错误捕获(推荐方式)
使用 catchError 包装器,让你可以直接 throw 错误,无需调用 next():
import express from 'express';
import { errorMiddleware, ResponseError, catchError } from '@aqcq/express-reply';
const app = express();
// 异步路由 - 自动捕获错误
app.get('/api/user/:id', catchError(async (req, res) => {
const { id } = req.params;
if (id === '404') {
// 直接抛出错误,无需 next()!
throw new ResponseError('VALIDATION_ERROR');
}
// 模拟异步数据库操作
const user = await getUserFromDB(id);
res.reply(user);
}));
// 同步路由 - 自动捕获错误
app.get('/api/sync', catchError((req, res) => {
// 同步代码中也可以直接抛出错误
throw new ResponseError('HTTP_ERROR');
}));
// 错误处理中间件(必须放在最后)
app.use(errorMiddleware());
app.listen(3000);🔧 针对性错误捕获
如果你明确知道路由是同步还是异步的,可以使用更具体的包装器:
import { asyncHandler, syncHandler } from '@aqcq/express-reply';
// 专门用于异步路由
app.get('/api/async', asyncHandler(async (req, res) => {
await someAsyncOperation();
throw new ResponseError('REQUEST_AI_ERROR');
}));
// 专门用于同步路由
app.get('/api/sync', syncHandler((req, res) => {
throw new ResponseError('RATE_LIMIT_ERROR');
}));📋 传统方式(向后兼容)
如果不使用错误捕获包装器,仍需手动调用 next():
app.get('/api/traditional', async (req, res, next) => {
try {
throw new ResponseError('VALIDATION_ERROR');
} catch (error) {
next(error); // 传统方式需要手动调用
}
});✅ 统一响应格式
使用内置的响应方法来标准化你的API响应:
import express from 'express';
import { replyMiddleware, catchError } from '@aqcq/express-reply';
const app = express();
// 注册 reply 中间件
app.use(replyMiddleware);
app.get('/api/success', catchError(async (req, res) => {
// 成功响应 - 统一格式
res.reply({
message: 'Hello World',
data: { id: 1, name: '用户名' }
});
}));
app.get('/api/fail', catchError((req, res) => {
// 方式1: 直接使用响应方法
res.replyFail('VALIDATION_ERROR');
// 方式2: 抛出错误(推荐)
// throw new ResponseError('VALIDATION_ERROR');
}));
app.listen(3000);TypeScript 支持
本包提供完整的 TypeScript 类型支持。安装后,你的 Express Response 对象会自动扩展以下方法:
interface Response {
reply: (data: any) => void;
replyFail: (name: ErrorName) => void;
}在 TypeScript 项目中使用时,你会获得完整的类型提示和自动补全:
import express from 'express';
import { replyMiddleware, ErrorName } from '@aqcq/express-reply';
const app = express();
app.use(replyMiddleware);
app.get('/api/users', (req, res) => {
// TypeScript 会为 res.reply 和 res.replyFail 提供类型提示
res.reply({ users: [] });
res.replyFail('USER_LIMIT_ERROR'); // 错误名称会有自动补全
});API 参考
错误捕获包装器
catchError(handler) - 推荐使用
通用错误捕获包装器,自动处理同步和异步错误:
import { catchError } from '@aqcq/express-reply';
app.get('/api/route', catchError(async (req, res) => {
// 可以是同步或异步函数
throw new ResponseError('VALIDATION_ERROR');
}));asyncHandler(handler)
专门用于异步路由的错误捕获:
import { asyncHandler } from '@aqcq/express-reply';
app.get('/api/async', asyncHandler(async (req, res) => {
await someAsyncOperation();
throw new ResponseError('REQUEST_AI_ERROR');
}));syncHandler(handler)
专门用于同步路由的错误捕获:
import { syncHandler } from '@aqcq/express-reply';
app.get('/api/sync', syncHandler((req, res) => {
throw new ResponseError('HTTP_ERROR');
}));响应方法
res.reply(data)
发送成功响应。
data: 要返回的数据(任意类型)
响应格式:
{
"code": 0,
"message": "success",
"timestamp": 1234567890,
"data": "你传入的数据"
}res.replyFail(errorName, code?)
发送错误响应。
errorName: 错误名称(预定义类型或自定义字符串)code: 可选的错误码,默认为 5000
响应格式:
{
"code": 2001,
"message": "parameter validation error",
"success": false
}错误类
new ResponseError(message, code?)
创建一个可以被错误中间件捕获的错误实例。
message: 错误名称(预定义类型)或自定义错误消息code: 可选的错误码,默认为 500
// 使用预定义错误类型
throw new ResponseError('VALIDATION_ERROR');
// 使用自定义错误消息和错误码
throw new ResponseError('自定义错误消息', 4001);支持的错误类型及错误码
[
{
code: 1001,
name: 'HTTP_ERROR',
message: 'internal unknown error',
_apiDesc: '内部未知错误',
},
{
code: 2001,
name: 'VALIDATION_ERROR',
message: 'parameter validation error',
_apiDesc: '参数校验错误',
},
{
code: 4002,
name: 'REQUEST_AI_ERROR',
message: 'requestAI error',
},
{
code: 4003,
name: 'USER_LIMIT_ERROR',
message:
'The limit for current model has been reached. Please wait until the next period or upgrade your plan to get more.',
},
{
code: 4003,
name: 'NOT_BASE_BOT_USAGE_LIMIT_ERROR',
message:
'The monthly queries for GPT-4, which this bot is based on, has been exhausted. Please use another bot.',
},
{
code: 4004,
name: 'USAGE_LIMIT_FOR_FREE_ERROR',
message:
'Free users can only use the standard model ( 10 queries per day ). Please try again tomorrow or upgrade your plan to get more.',
},
{
code: 4005,
name: 'BASE_BOT_NOT_FOUND_ERRO',
message:
'The base bot for your current bot has been deleted. Please reconfigure another base robot.',
},
{
code: 4006,
name: 'ONLY_USE_GPT3DOT5_ERROR',
message:
'Your complimentary queries has been exhausted, you can now only use GPT-3.5 (10 queries per day).',
},
{
code: 4008,
name: 'REQUEST_AI_WITH_NETWORK_AUTH_ERROR',
message: 'Unauthorized',
},
{
code: 4009,
name: 'ONLY_USE_BASIC_BOT_ERROR',
message:
'The advanced drawing model is not available for free users. Upgrade your plan to get more advanced features.',
},
{
code: 4010,
name: 'BOT_NETWORK_ERROR',
message: 'Internet access is not available for free users.',
},
{
code: 4011,
name: 'ONLY_PRO_USER_CAN_USE_ERRO',
message: 'insufficient User Permissions',
_apiDesc: 'insufficient User Permissions',
},
{
code: 4012,
name: 'AI_USE_PRO_ERROR',
message: 'The advanced drawing model is not available for Air Plan.Please upgrade your plan.',
},
{
code: 4013,
name: 'MODEL_NOT_FOUND_ERROR',
message: 'Model not found',
_apiDesc: 'Model not found',
},
{
code: 4014,
name: 'MODEL_UNAVAILABLE_ERROR',
message: 'The model you are using is not available. Please try another model.',
},
{
code: 4015,
name: 'MODEL_UNAVAILABLE_ERROR',
message: 'Model unavailable',
_apiDesc: 'Model unavailable',
},
{
code: 5001,
name: 'CREATE_ACTION_ERROR',
message: 'Creation failure',
_apiDesc: 'Creation failure',
},
{
code: 5040,
name: 'RESPONSE_MODEL_NOT_FOUND_ERRO',
message: 'model not found',
_apiDesc: '模型不存在',
},
{
code: 5041,
name: 'RESPONSE_GEN_TITLE_ERROR',
message: 'generate title failed',
_apiDesc: '生成标题失败',
},
{
code: 5042,
name: 'RESPONSE_QUERY_ERROR',
message: 'query failed',
_apiDesc: '查询失败',
},
{
code: 5043,
name: 'RESPONSE_BOT_OPERATION_ERROR',
message: 'operation failed',
_apiDesc: 'bot操作失败',
},
{
code: 5044,
name: 'RESPONSE_BOT_EXISTS_ERROR',
message: 'Bot already exists',
_apiDesc: 'bot已存在',
},
{
code: 6001,
name: 'RATE_LIMIT_ERROR',
message: 'Request rate limit',
_apiDesc: 'Creation failure',
},
]完整的错误类型列表请参考源码中的 ERROR_MESSAGES。
最佳实践
✅ 推荐做法
import { replyMiddleware, errorMiddleware, catchError, ResponseError } from '@aqcq/express-reply';
const app = express();
// 1. 注册 reply 中间件
app.use(replyMiddleware);
// 2. 使用 catchError 包装所有路由
app.get('/api/users', catchError(async (req, res) => {
// 3. 直接抛出错误,无需 next()
if (!req.user) {
throw new ResponseError('VALIDATION_ERROR');
}
// 4. 使用统一的响应格式
res.reply({ users: await getUsers() });
}));
// 5. 错误处理中间件放在最后
app.use(errorMiddleware());❌ 避免的做法
// 不推荐:混合使用传统方式和新方式
app.get('/api/bad', async (req, res, next) => {
try {
throw new ResponseError('ERROR');
} catch (error) {
next(error); // 多余的 next() 调用
}
});
// 不推荐:忘记使用错误捕获包装器
app.get('/api/bad2', async (req, res, next) => {
throw new ResponseError('ERROR'); // 异步错误不会被捕获
});注意事项
中间件顺序很重要:
replyMiddleware要在业务路由之前注册errorMiddleware要在所有路由之后注册
错误捕获:
- 使用
catchError包装器可以自动捕获所有错误 - 不使用包装器时,异步错误需要手动调用
next(error)
- 使用
响应格式:
res.reply()和res.replyFail()会自动设置适当的 HTTP 状态码- 调用后响应立即发送,无需手动调用其他响应方法
TypeScript 支持:
- 包含完整的类型定义,提供最佳的开发体验
- 错误名称会有自动补全功能
License
MIT