llm-chat/backend/middleware/errorHandler.js

274 lines
6.2 KiB
JavaScript
Raw Permalink Normal View History

const { Logger } = require('./logger');
/**
* 404错误处理中间件
*/
const notFoundHandler = (req, res, next) => {
const error = new Error(`找不到请求的资源: ${req.method} ${req.originalUrl}`);
error.status = 404;
error.code = 'NOT_FOUND';
Logger.warn('404错误', {
method: req.method,
url: req.originalUrl,
ip: req.ip,
userAgent: req.get('User-Agent')
});
next(error);
};
/**
* 全局错误处理中间件
*/
const globalErrorHandler = (err, req, res, next) => {
// 默认错误状态码
let statusCode = err.status || err.statusCode || 500;
let message = err.message || '服务器内部错误';
let code = err.code || 'INTERNAL_ERROR';
// 记录错误日志
Logger.error('全局错误处理', err, {
method: req.method,
url: req.originalUrl,
ip: req.ip,
userAgent: req.get('User-Agent'),
body: req.body,
params: req.params,
query: req.query
});
// 根据错误类型设置不同的响应
switch (err.name) {
case 'ValidationError':
statusCode = 400;
message = '请求参数验证失败';
code = 'VALIDATION_ERROR';
break;
case 'UnauthorizedError':
statusCode = 401;
message = '未授权访问';
code = 'UNAUTHORIZED';
break;
case 'JsonWebTokenError':
statusCode = 401;
message = 'Token无效';
code = 'INVALID_TOKEN';
break;
case 'TokenExpiredError':
statusCode = 401;
message = 'Token已过期';
code = 'TOKEN_EXPIRED';
break;
case 'CastError':
statusCode = 400;
message = '无效的ID格式';
code = 'INVALID_ID';
break;
case 'SyntaxError':
if (err.message.includes('JSON')) {
statusCode = 400;
message = 'JSON格式错误';
code = 'INVALID_JSON';
}
break;
}
// 数据库相关错误
if (err.message.includes('SQLITE_')) {
statusCode = 500;
message = '数据库操作失败';
code = 'DATABASE_ERROR';
// 具体的SQLite错误处理
if (err.message.includes('UNIQUE')) {
statusCode = 409;
message = '资源已存在';
code = 'RESOURCE_CONFLICT';
} else if (err.message.includes('FOREIGN KEY')) {
statusCode = 400;
message = '外键约束错误';
code = 'FOREIGN_KEY_ERROR';
}
}
// LLM API相关错误
if (err.message.includes('LLM') || err.message.includes('API')) {
if (err.message.includes('密钥')) {
statusCode = 503;
message = 'AI服务配置错误';
code = 'LLM_CONFIG_ERROR';
} else if (err.message.includes('网络') || err.message.includes('连接')) {
statusCode = 503;
message = 'AI服务连接失败';
code = 'LLM_CONNECTION_ERROR';
} else if (err.message.includes('频率')) {
statusCode = 429;
message = '请求过于频繁';
code = 'RATE_LIMIT_EXCEEDED';
}
}
// 构建错误响应
const errorResponse = {
success: false,
error: message,
code: code,
timestamp: new Date().toISOString(),
path: req.originalUrl,
method: req.method
};
// 开发环境包含更多错误信息
if (process.env.NODE_ENV === 'development') {
errorResponse.stack = err.stack;
errorResponse.details = {
name: err.name,
originalMessage: err.message
};
}
// 特殊处理:如果是流式请求,需要特殊处理
if (req.headers.accept && req.headers.accept.includes('text/plain')) {
res.status(statusCode).write(JSON.stringify({
type: 'error',
data: errorResponse
}) + '\n');
res.end();
return;
}
res.status(statusCode).json(errorResponse);
};
/**
* 异步错误包装器
*/
const asyncHandler = (fn) => {
return (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
};
/**
* 验证错误处理器
*/
const validationErrorHandler = (errors) => {
const formattedErrors = errors.map(error => ({
field: error.param,
message: error.msg,
value: error.value
}));
const validationError = new Error('请求参数验证失败');
validationError.status = 400;
validationError.code = 'VALIDATION_ERROR';
validationError.details = formattedErrors;
return validationError;
};
/**
* 业务逻辑错误类
*/
class BusinessError extends Error {
constructor(message, statusCode = 400, code = 'BUSINESS_ERROR') {
super(message);
this.name = 'BusinessError';
this.status = statusCode;
this.code = code;
}
}
/**
* 资源未找到错误类
*/
class NotFoundError extends Error {
constructor(resource = '资源') {
super(`${resource}不存在`);
this.name = 'NotFoundError';
this.status = 404;
this.code = 'NOT_FOUND';
}
}
/**
* 验证错误类
*/
class ValidationError extends Error {
constructor(message, field = null) {
super(message);
this.name = 'ValidationError';
this.status = 400;
this.code = 'VALIDATION_ERROR';
this.field = field;
}
}
/**
* 权限错误类
*/
class ForbiddenError extends Error {
constructor(message = '权限不足') {
super(message);
this.name = 'ForbiddenError';
this.status = 403;
this.code = 'FORBIDDEN';
}
}
/**
* 服务不可用错误类
*/
class ServiceUnavailableError extends Error {
constructor(service = '服务') {
super(`${service}暂时不可用`);
this.name = 'ServiceUnavailableError';
this.status = 503;
this.code = 'SERVICE_UNAVAILABLE';
}
}
/**
* 进程退出前的清理处理
*/
const gracefulShutdown = () => {
process.on('SIGTERM', () => {
Logger.info('收到SIGTERM信号开始优雅关闭...');
process.exit(0);
});
process.on('SIGINT', () => {
Logger.info('收到SIGINT信号开始优雅关闭...');
process.exit(0);
});
process.on('uncaughtException', (err) => {
Logger.error('未捕获的异常', err);
process.exit(1);
});
process.on('unhandledRejection', (reason, promise) => {
Logger.error('未处理的Promise拒绝', new Error(reason), { promise });
process.exit(1);
});
};
module.exports = {
notFoundHandler,
globalErrorHandler,
asyncHandler,
validationErrorHandler,
gracefulShutdown,
// 错误类
BusinessError,
NotFoundError,
ValidationError,
ForbiddenError,
ServiceUnavailableError
};