先说明使用的库:
{
"ioredis": "^5.6.0",
"jsonwebtoken": "^9.0.2"
}
ioredis
报错显示为 TypeError: Cannot read properties of undefined (reading 'charCodeAt')
,是因为 NextJs 的中间件是在边缘运行 (Edge), 没有不能使用.env 获取环境变量


// utils/redis.ts
import Redis from 'ioredis'
class RedisClient {
private static instance: RedisClient
private redisClient: Redis
// 当前使用的数据库索引
private currentDb: number = 0
private constructor() {
// 初始化 Redis 客户端
this.redisClient = new Redis({
host: process.env.REDIS_HOST || '127.0.0.1',
port: parseInt(process.env.REDIS_PORT || '6379'),
password: process.env.REDIS_PASSWORD || '123456',
db: parseInt(process.env.REDIS_DB || '0'), // 默认使用配置文件中的 DB
connectTimeout: parseInt(
process.env.REDIS_CONNECT_TIMEOUT || '3000'
),
retryStrategy(times) {
// 当连接失败时的重试策略
return Math.min(times * 50, 2000)
}
})
// 监听连接错误
this.redisClient.on('error', (error) => {
console.error('Redis connection error:', error)
})
// 监听连接成功
this.redisClient.on('connect', () => {
console.log(`Connected to Redis (DB ${this.currentDb})`)
})
}
// 单例模式:确保只有一个 Redis 实例
public static getInstance(): RedisClient {
if (!RedisClient.instance) {
RedisClient.instance = new RedisClient()
}
return RedisClient.instance
}
// 切换数据库
public async selectDb(dbIndex: number): Promise<void> {
try {
await this.redisClient.select(dbIndex)
this.currentDb = dbIndex
console.log(`Switched to Redis DB ${dbIndex}`)
} catch (error) {
console.error('Error switching Redis DB:', error)
throw error
}
}
// 设置键值对(支持设置过期时间)
public async set(key: string, value: any, ttl?: number): Promise<void> {
try {
const serializedValue = this.serialize(value)
if (ttl) {
await this.redisClient.set(key, serializedValue, 'EX', ttl) // 设置过期时间(秒)
} else {
await this.redisClient.set(key, serializedValue)
}
} catch (error) {
console.error('Redis set error:', error)
throw error
}
}
// 获取键的值并反序列化
public async get<T = any>(key: string): Promise<T | null> {
try {
const value = await this.redisClient.get(key)
return this.deserialize(value)
} catch (error) {
console.error('Redis get error:', error)
throw error
}
}
// 删除键
public async del(key: string): Promise<void> {
try {
await this.redisClient.del(key)
} catch (error) {
console.error('Redis del error:', error)
throw error
}
}
// 检查键是否存在
public async exists(key: string): Promise<boolean> {
try {
const result = await this.redisClient.exists(key)
return result === 1
} catch (error) {
console.error('Redis exists error:', error)
throw error
}
}
// 设置哈希字段(支持复杂对象)
public async hset(key: string, field: string, value: any): Promise<void> {
try {
const serializedValue = this.serialize(value)
await this.redisClient.hset(key, field, serializedValue)
} catch (error) {
console.error('Redis hset error:', error)
throw error
}
}
// 获取哈希字段的值并反序列化
public async hget<T = any>(key: string, field: string): Promise<T | null> {
try {
const value = await this.redisClient.hget(key, field)
return this.deserialize(value)
} catch (error) {
console.error('Redis hget error:', error)
throw error
}
}
// 删除 Redis 客户端连接
public async quit(): Promise<void> {
try {
await this.redisClient.quit()
console.log('Redis client disconnected')
} catch (error) {
console.error('Redis quit error:', error)
throw error
}
}
// 将值序列化为 JSON 字符串
private serialize(value: any): string {
return JSON.stringify(value)
}
// 将 JSON 字符串反序列化为原始值
private deserialize(value: string | null): any {
if (!value) return null
try {
return JSON.parse(value)
} catch (error) {
console.error('Error deserializing JSON:', error)
return value // 返回原始字符串以防解析失败
}
}
}
export const RedisClientInstance = RedisClient.getInstance()
jsonwebtoken
报错显示为 Error: Invalid token: The edge runtime does not support Node.js 'crypto' module.
同样是由于边缘运行,没有 node 的环境,导致找不到 crypto


import type { JwtPayload, SignOptions } from 'jsonwebtoken'
import jwt from 'jsonwebtoken'
/**
* JWT 工具类
*/
class JwtService {
private secretKey: string
/**
* 构造函数
* @param secretKey - 用于签名和验证的密钥
*/
constructor(secretKey: string) {
this.secretKey = secretKey
}
/**
* 生成 JWT Token
* @param payload - 要加密的数据(可以是对象或字符串)
* @param options - 签名选项(可选)
* @returns 生成的 JWT Token
*/
public generateToken(
payload: string | object,
options?: SignOptions
): string {
try {
return jwt.sign(payload, this.secretKey, options)
} catch (error: any) {
throw new Error(`Failed to generate token: ${error.message}`)
}
}
/**
* 验证并解析 JWT Token
* @param token - 要验证的 JWT Token
* @returns 解析后的数据(payload)
* @throws 如果 Token 无效或过期,则抛出错误
*/
public verifyToken<T extends JwtPayload | string>(token: string): T {
try {
return jwt.verify(token, this.secretKey) as T
} catch (error: any) {
throw new Error(`Invalid token: ${error.message}`)
}
}
/**
* 检查 Token 是否已过期
* @param token - 要检查的 JWT Token
* @returns true 表示已过期,false 表示未过期
*/
public isTokenExpired(token: string): boolean {
try {
const decoded = jwt.decode(token, { complete: true })
if (!decoded || typeof decoded.payload === 'string') {
return true // 无法解析 Token
}
const { exp } = decoded.payload as JwtPayload
return !!exp && Date.now() >= exp * 1000
} catch (error) {
return true // 解析失败则视为已过期
}
}
}
const jwtService = new JwtService(process.env.JWT_SECRET_KEY!)
export default jwtService
解决方法
使用 api 请求来使用 redis 和 jwt 功能:

Comments 1 条评论
非常好文章,使我的问题解决