添加响应中间件、redis链接、配置redis store中间件、跨域配置
一、响应中间件配置
1.1 HTTP 状态码标准化
文件位置: utils/httpStatus.js
javascript
/**
* HTTP状态码常量定义
* 统一管理所有HTTP状态码,确保系统一致性
*/
const HTTP_STATUS = {
// 2xx 成功系列
SUCCESS: 200, // 请求成功
CREATED: 201, // 资源创建成功
ACCEPTED: 202, // 请求已接受,处理中
NO_CONTENT: 204, // 请求成功,无返回内容
// 3xx 重定向系列
MOVED_PERMANENTLY: 301, // 永久重定向
SEE_OTHER: 303, // 临时重定向
NOT_MODIFIED: 304, // 资源未修改
// 4xx 客户端错误系列
BAD_REQUEST: 400, // 请求参数错误
UNAUTHORIZED: 401, // 未授权访问
FORBIDDEN: 403, // 禁止访问
NOT_FOUND: 404, // 资源未找到
METHOD_NOT_ALLOWED: 405, // 请求方法不允许
CONFLICT: 409, // 资源冲突
UNSUPPORTED_TYPE: 415, // 不支持的媒体类型
// 5xx 服务器错误系列
INTERNAL_SERVER_ERROR: 500, // 服务器内部错误
NOT_IMPLEMENTED: 501, // 功能未实现
BAD_GATEWAY: 502, // 网关错误
SERVICE_UNAVAILABLE: 503, // 服务不可用
GATEWAY_TIMEOUT: 504, // 网关超时
// 自定义业务状态码(6xx系列)
UNKNOWN_ERROR: 520, // 未知错误
DATABASE_ERROR: 521, // 数据库操作错误
WARNING: 601, // 业务警告
ERROR: 602 // 业务错误
}
module.exports = HTTP_STATUS
1.2 响应包装中间件
文件位置: middlewares/responseWrapper.js
javascript
/**
* 统一响应包装中间件
* 基于RuoYi框架响应格式标准,提供一致化的API响应结构
*/
const { API_ROOT } = require('../config')
const HTTP_STATUS = require('../utils/httpStatus')
function responseWrapper(req, res, next) {
// 仅对API根路径下的请求进行包装
if (!req.originalUrl?.startsWith(API_ROOT)) {
return next()
}
// 保存原始的json方法
const originalJson = res.json
/**
* 重写res.json方法,统一响应格式
* 支持三种格式:
* 1. 标准格式:{ code, data, msg }
* 2. 分页格式:{ code, rows, total, msg }
* 3. 已包装格式:直接返回
*/
res.json = function(payload = null) {
// 情况1:已经是完整响应格式,直接返回
if (payload && typeof payload === 'object' && 'code' in payload) {
return originalJson.call(this, payload)
}
// 情况2:分页响应格式
if (payload && typeof payload === 'object' && 'rows' in payload && 'total' in payload) {
const { rows, total, msg = '操作成功', ...rest } = payload
return originalJson.call(this, {
code: HTTP_STATUS.SUCCESS,
rows,
total,
msg,
...rest
})
}
// 情况3:普通数据响应,自动包装
return originalJson.call(this, {
code: HTTP_STATUS.SUCCESS,
data: payload,
msg: '操作成功'
})
}
/**
* 成功响应快捷方法
* @param {any} data - 响应数据
* @param {string} msg - 成功消息
* @returns {Object} 标准响应格式
*/
res.success = function(data = null, msg = '操作成功') {
return originalJson.call(this, {
code: HTTP_STATUS.SUCCESS,
data,
msg
})
}
/**
* 分页响应快捷方法
* @param {Array} rows - 数据列表
* @param {number} total - 总记录数
* @param {Object} rest - 其他扩展字段
* @param {string} msg - 成功消息
* @returns {Object} 分页响应格式
*/
res.page = function(rows, total, rest = {}, msg = '操作成功') {
return originalJson.call(this, {
code: HTTP_STATUS.SUCCESS,
rows,
total,
msg,
...rest
})
}
/**
* 错误响应快捷方法
* @param {string} msg - 错误消息
* @param {number} code - 错误状态码,默认为业务错误码
* @returns {Object} 错误响应格式
*/
res.error = function(msg = '操作失败', code = HTTP_STATUS.ERROR) {
// 设置HTTP状态码(映射到对应的HTTP状态码)
const httpStatusCode = code >= 600 ? 200 : code
this.status(httpStatusCode)
return originalJson.call(this, {
code,
msg
})
}
next()
}
module.exports = responseWrapper
1.3 响应格式示例
| 响应类型 | 示例格式 |
|---|---|
| 成功响应 | { "code": 200, "data": {...}, "msg": "操作成功" } |
| 分页响应 | { "code": 200, "rows": [...], "total": 100, ... } |
| 错误响应 | { "code": 602, "msg": "操作失败" } |
| HTTP错误 | { "code": 404, "msg": "用户不存在" } |
1.4 Express 应用集成
文件位置: app.js(简略版)
javascript
const createError = require('http-errors')
const express = require('express')
const { API_ROOT } = require('./config')
const responseWrapper = require('./middlewares/responseWrapper')
const routes = require('./routes')
const app = express()
// 基础配置
app.set('view engine', 'ejs')
app.use(express.json())
app.use(express.urlencoded({ extended: false }))
app.use(express.static('public'))
// 响应包装中间件
app.use(responseWrapper)
// 路由注册
app.use(API_ROOT, routes)
// 错误处理
app.use((req, res, next) => {
next(createError(404))
})
module.exports = app
二、Redis 连接与管理
2.1 配置文件
文件位置: config/index.js
javascript
/**
* 应用配置文件
*/
module.exports = {
// 数据库配置
DBHOST: '127.0.0.1',
DBPORT: 27017,
DBNAME: 'node-ruoyi',
// 会话安全配置
SESSION_SECRET: '31df9e38-03d3-4f97-b56c-a166a7420c8c',
// Redis 配置
REDIS_HOST: '127.0.0.1',
REDIS_PORT: 6379,
// API 配置
API_ROOT: '/api',
// 数据库自增配置
COUNTERS_COLLECTION: 'auto_increment_counters'
}
2.2 Redis 客户端封装
文件位置: redis/index.js
javascript
/**
* Redis 客户端管理类
* 提供连接池管理、自动重连、健康检查等功能
*/
const { createClient } = require('redis')
const { REDIS_HOST, REDIS_PORT, REDIS_PASSWORD } = require('../config')
class RedisClient {
constructor() {
this.client = null
this.isConnected = false
}
/**
* 建立 Redis 连接
*/
async connect() {
if (this.client && this.isConnected) {
return this.client
}
try {
// 创建 Redis 客户端
this.client = createClient({
url: `redis://${REDIS_HOST}:${REDIS_PORT}`,
password: process.env.REDIS_PASSWORD || undefined,
socket: {
reconnectStrategy: retries => {
if (retries > 10) {
console.error('Redis 连接失败,已达到最大重试次数')
return new Error('Redis 连接失败')
}
return Math.min(retries * 100, 3000) // 重试间隔
}
}
})
// 事件监听器
this.client.on('error', err => {
console.error('Redis 客户端错误:', err)
this.isConnected = false
})
this.client.on('connect', () => {
console.log('✅ Redis 连接成功')
this.isConnected = true
})
this.client.on('end', () => {
console.log('❌ Redis 连接关闭')
this.isConnected = false
})
// 建立连接
await this.client.connect()
return this.client
} catch (error) {
console.error('Redis 连接失败:', error)
throw error
}
}
/**
* 获取 Redis 客户端实例
*/
async getClient() {
if (!this.client || !this.isConnected) {
await this.connect()
}
return this.client
}
/**
* 断开连接
*/
async disconnect() {
if (this.client) {
await this.client.quit()
this.isConnected = false
}
}
/**
* 健康检查
*/
async ping() {
try {
const client = await this.getClient()
const result = await client.ping()
return result === 'PONG'
} catch (error) {
console.error('Redis ping 失败:', error)
return false
}
}
}
// 导出单例实例
module.exports = new RedisClient()
三、Session 管理与跨域配置
3.1 依赖安装
bash
# 安装必要的中间件
npm install connect-redis express-session cors --save
3.2 Express 应用完整配置
文件位置: app.js(完整版)
javascript
const createError = require('http-errors')
const express = require('express')
const path = require('path')
const cookieParser = require('cookie-parser')
const logger = require('morgan')
const session = require('express-session')
const cors = require('cors')
const { RedisStore } = require('connect-redis')
// 自定义模块
const { SESSION_SECRET, API_ROOT } = require('./config')
const responseWrapper = require('./middlewares/responseWrapper')
const redisClient = require('./redis')
const routes = require('./routes')
// 创建 Express 应用
const app = express()
// ==================== 中间件配置 ====================
// 1. 视图引擎配置
app.set('views', path.join(__dirname, 'views'))
app.set('view engine', 'ejs')
// 2. 开发日志
app.use(logger('dev'))
// 3. 请求体解析
app.use(express.json({ strict: false }))
app.use(express.urlencoded({ extended: true }))
// 4. Cookie 解析
app.use(cookieParser())
// 5. 静态文件服务
app.use(express.static(path.join(__dirname, 'public')))
// 6. 跨域配置
app.use(cors({
origin: true, // 允许所有来源(生产环境应限制)
credentials: true, // 允许携带凭证(Cookie)
exposedHeaders: ['set-cookie', 'download-filename']
}))
// ==================== Session 配置 ====================
/**
* 初始化 Redis Session 存储
*/
const initRedisSession = async () => {
const client = await redisClient.getClient()
const redisStore = new RedisStore({
client,
prefix: 'session:', // Redis 键前缀
ttl: 60 * 10, // 过期时间(秒)
disableTouch: false // 允许更新过期时间
})
return session({
store: redisStore,
secret: SESSION_SECRET,
name: 'sid', // Cookie 名称
resave: false, // 避免重复保存
saveUninitialized: false, // 不保存空 session
cookie: {
maxAge: 24 * 60 * 60 * 1000, // 24小时
httpOnly: true, // 防止 XSS 攻击
secure: process.env.NODE_ENV === 'production'
},
rolling: true, // 每次请求刷新过期时间
unset: 'destroy' // 删除时从存储移除
})
}
// ==================== 应用初始化 ====================
;(async () => {
try {
// 1. 初始化 Redis Session
const sessionMiddleware = await initRedisSession()
app.use(sessionMiddleware)
// 2. 响应包装中间件
app.use(responseWrapper)
// 3. 注册路由
app.use(API_ROOT, routes)
// 4. API 专用错误处理器
app.use(API_ROOT, (err, req, res, next) => {
console.log('API 错误日志:', err)
res.status(err.status || 500).json({
code: err.status || 500,
msg: err.message || '服务器内部错误'
})
})
// 5. 404 处理器
app.use((req, res, next) => {
next(createError(404))
})
// 6. 通用错误处理器
app.use((err, req, res, next) => {
res.locals.message = err.message
res.locals.error = req.app.get('env') === 'development' ? err : {}
res.status(err.status || 500)
res.render('error')
})
console.log('✅ 应用配置完成')
} catch (error) {
console.error('❌ 应用初始化失败:', error)
process.exit(1)
}
})()
module.exports = app
4.2 路由配置示例
javascript
// routes/user.js
const express = require('express')
const router = express.Router()
const userController = require('../controllers/userController')
const { verifyToken } = require('../jwt')
// 公开接口
router.get('/public', userController.getPublicData)
// 需要认证的接口
router.get('/profile', verifyToken(), userController.getProfile)
router.put('/profile', verifyToken(), userController.updateProfile)
// 分页查询
router.get('/list', verifyToken(), userController.getUsersPaginated)
module.exports = router
4.3 目录结构参考
bash
node-ruoyi/
├── config/
│ └── index.js # 配置文件
├── redis/
│ ├── index.js # Redis 客户端
│ ├── token.js # Token 管理
│ └── captcha.js # 验证码存储
├── middlewares/
│ ├── responseWrapper.js # 响应包装
│ ├── index.js # 中间件入口
│ └── validator/ # 验证中间件
├── utils/
│ └── httpStatus.js # HTTP 状态码
├── routes/ # 路由目录
├── controllers/ # 控制器目录
├── models/ # 数据模型
├── app.js # 应用主文件
└── package.json
📊 配置总结表
| 组件 | 配置文件 | 主要功能 | 依赖 |
|---|---|---|---|
| HTTP 响应 | responseWrapper.js |
统一响应格式、快捷方法 | - |
| 状态码 | httpStatus.js |
标准化状态码定义 | - |
| Redis | redis/index.js |
连接池、自动重连、健康检查 | redis |
| Session | app.js |
Redis 存储、安全配置 | express-session, connect-redis |
| 跨域 | app.js |
CORS 配置、凭证支持 | cors |
| 应用配置 | config/index.js |
集中配置管理 | - |
🛡️ 安全建议
-
生产环境配置
- 使用环境变量存储敏感信息
- 启用 HTTPS
- 配置合适的 CORS 白名单
-
Redis 安全
- 设置密码认证
- 限制网络访问
- 定期备份数据
-
Session 安全
- 定期更换
SESSION_SECRET - 设置合适的过期时间
- 启用
secure和httpOnly
- 定期更换