Context 上下文系统
Context 提供请求级别的全局状态管理,让你能在中间件之间优雅地共享数据。
🤔 为什么需要 Context?
想象这个场景:你有 3 个中间件,第 1 个获取了用户信息,第 3 个需要用这个信息。传统做法?
typescript
// ❌ 方案 1:全局变量(危险!多个请求会互相污染)
let currentUser = null
middleware1(() => {
currentUser = getUser()
})
middleware3(() => {
console.log(currentUser) // 可能是别人的用户!
})
// ❌ 方案 2:层层传递(恶心!)
middleware1((input, next) => {
const user = getUser()
return next({ ...input, user }) // 传递下去
})Context 的解决方案:
typescript
import { createContext } from 'farrow-pipeline'
// 创建一个用户上下文
const UserContext = createContext<User | null>(null)
// 第 1 个中间件:设置用户
pipeline.use((req, next) => {
const user = authenticate(req)
UserContext.set(user) // 🎯 设置!
return next(req)
})
// 第 3 个中间件:获取用户
pipeline.use((req) => {
const user = UserContext.get() // 🎯 获取!
return { user, data: processRequest(req) }
})✨ 魔法原理:每次
pipeline.run()都会创建独立的容器,不同请求互不干扰。并发安全!
Context 基本操作
创建 Context
typescript
import { createContext } from 'farrow-pipeline'
// 创建带默认值的 Context
const UserContext = createContext<User | null>(null)
const CounterContext = createContext<number>(0)
const ConfigContext = createContext({ debug: false })
// 泛型参数指定类型
const LoggerContext = createContext<{
log: (msg: string) => void
error: (msg: string) => void
}>({
log: console.log,
error: console.error
})获取值
typescript
// 获取当前值(可能是默认值)
const user = UserContext.get()
// 获取并使用
pipeline.use((input) => {
const user = UserContext.get()
if (user) {
console.log(`当前用户: ${user.name}`)
}
return input
})设置值
typescript
// 设置新值
UserContext.set(newUser)
// 在中间件中设置
pipeline.use((input, next) => {
const user = authenticate(input)
UserContext.set(user)
return next(input)
})断言非空
typescript
// 断言值存在(如果是 null/undefined 会抛错)
const user = UserContext.assert()
// 使用场景
pipeline.use((input) => {
// 假设前面的中间件已经设置了用户
const user = UserContext.assert() // 如果为 null,抛出异常
return processForUser(user, input)
})🎯 实战例子
例子 1: 多上下文协作
typescript
import { createContext, createPipeline } from 'farrow-pipeline'
type User = { id: string; name: string }
type Logger = { log: (msg: string) => void }
// 创建多个上下文
const UserContext = createContext<User | null>(null)
const RequestIdContext = createContext<string>('')
const LoggerContext = createContext<Logger>({ log: console.log })
const pipeline = createPipeline<Request, Response>()
// 中间件 1:初始化上下文
pipeline.use((req, next) => {
UserContext.set(authenticate(req))
RequestIdContext.set(generateId())
LoggerContext.set(createLogger())
return next(req)
})
// 中间件 2:使用上下文
pipeline.use((req) => {
const user = UserContext.get()
const requestId = RequestIdContext.get()
const logger = LoggerContext.get()
logger.log(`[${requestId}] 用户 ${user?.name} 发起请求`)
return { user, requestId, data: '...' }
})例子 2: 请求追踪
typescript
const TraceContext = createContext<{
traceId: string
startTime: number
steps: string[]
}>({
traceId: '',
startTime: 0,
steps: []
})
const tracePipeline = createPipeline<Request, Response>()
// 初始化追踪
tracePipeline.use((req, next) => {
TraceContext.set({
traceId: generateTraceId(),
startTime: Date.now(),
steps: []
})
return next(req)
})
// 记录步骤
function addStep(name: string) {
const trace = TraceContext.get()
trace.steps.push(`${name}: ${Date.now() - trace.startTime}ms`)
}
// 业务中间件
tracePipeline.use((req, next) => {
addStep('认证开始')
const user = authenticate(req)
addStep('认证完成')
return next(req)
})
tracePipeline.use((req) => {
addStep('处理开始')
const result = processRequest(req)
addStep('处理完成')
const trace = TraceContext.get()
return {
result,
trace: {
id: trace.traceId,
duration: Date.now() - trace.startTime,
steps: trace.steps
}
}
})例子 3: 数据库连接管理
typescript
interface Database {
query: (sql: string) => Promise<any>
close: () => Promise<void>
}
const DBContext = createContext<Database | null>(null)
const dbPipeline = createAsyncPipeline<Request, Response>()
// 打开数据库连接
dbPipeline.use(async (req, next) => {
const db = await openDatabase()
DBContext.set(db)
try {
return await next(req)
} finally {
await db.close() // 确保连接关闭
}
})
// 使用数据库
dbPipeline.use(async (req) => {
const db = DBContext.assert() // 断言数据库已连接
const users = await db.query('SELECT * FROM users')
return { users }
})🔒 上下文隔离
重要特性:每个 pipeline.run() 都有独立的容器!
typescript
const CounterContext = createContext(0)
const pipeline = createPipeline<string, string>()
.use((input, next) => {
const count = CounterContext.get()
CounterContext.set(count + 1)
return next(`${input}:${CounterContext.get()}`)
})
// 并发执行,每个都从 0 开始计数
await Promise.all([
pipeline.run('A'), // "A:1"
pipeline.run('B'), // "B:1"(不是 "B:2"!)
pipeline.run('C') // "C:1"(不是 "C:3"!)
])隔离演示
typescript
const UserContext = createContext<string>('匿名')
const pipeline = createPipeline<string, string>()
pipeline.use((input, next) => {
UserContext.set(input)
return next(input)
})
pipeline.use((input, next) => {
// 模拟异步操作
setTimeout(() => {
console.log(`用户: ${UserContext.get()}`)
}, 100)
return next(input)
})
pipeline.use((input) => {
return `处理完成: ${input}`
})
// 快速连续调用
pipeline.run('Alice')
pipeline.run('Bob')
pipeline.run('Charlie')
// 输出(顺序可能不同,但值是隔离的):
// 用户: Alice
// 用户: Bob
// 用户: Charlie高级用法
自定义 Context Hook
typescript
// 创建自定义 Hook
function useUser() {
const user = UserContext.get()
if (!user) {
throw new Error('用户未登录')
}
return user
}
// 使用
pipeline.use((req) => {
const user = useUser() // 自动断言用户存在
return processForUser(user, req)
})Context 组合
typescript
interface AppContext {
user: User | null
db: Database
logger: Logger
}
// 创建组合上下文
const AppContexts = {
user: createContext<User | null>(null),
db: createContext<Database | null>(null),
logger: createContext<Logger>(console)
}
// 初始化所有上下文
function initializeContext(user: User, db: Database, logger: Logger) {
AppContexts.user.set(user)
AppContexts.db.set(db)
AppContexts.logger.set(logger)
}
// 使用
pipeline.use(async (req, next) => {
const user = await authenticate(req)
const db = await getDatabase()
const logger = createLogger()
initializeContext(user, db, logger)
return next(req)
})嵌套 Context
typescript
const OuterContext = createContext<string>('外层')
const InnerContext = createContext<string>('内层')
const outerPipeline = createPipeline<string, string>()
.use((input, next) => {
OuterContext.set('外层值')
return next(input)
})
const innerPipeline = createPipeline<string, string>()
.use((input, next) => {
InnerContext.set('内层值')
console.log('外层:', OuterContext.get()) // 可以访问外层
console.log('内层:', InnerContext.get())
return next(input)
})
// 嵌套调用
outerPipeline.use((input, next) => {
const runInner = usePipeline(innerPipeline)
return runInner(input)
})最佳实践
✅ 适合用 Context
- 🟢 跨中间件共享的请求级数据(用户、请求 ID)
- 🟢 需要隔离的状态
- 🟢 数据库连接、日志器等资源
typescript
// ✅ 好:适合用 Context
const UserContext = createContext<User | null>(null)
const RequestIdContext = createContext<string>('')
const DBContext = createContext<Database | null>(null)❌ 不适合用 Context
- 🔴 全局配置(用模块变量)
- 🔴 可以通过参数传递的数据
- 🔴 不需要隔离的状态
typescript
// ❌ 不必要:用模块变量就行
const ConfigContext = createContext({ apiUrl: 'https://api.example.com' })
// 改成这样:
const API_URL = 'https://api.example.com'命名规范
typescript
// ✅ 好:以 Context 结尾
const UserContext = createContext<User | null>(null)
const LoggerContext = createContext<Logger>(console)
// ❌ 不好:名称不清晰
const User = createContext<User | null>(null)
const log = createContext<Logger>(console)类型安全
typescript
// ✅ 好:明确指定类型
const UserContext = createContext<User | null>(null)
// ❌ 不好:依赖类型推导
const UserContext = createContext(null) // 类型是 null