localhost
GET
title: 扩展上下文 - ElysiaJS head: - - meta - property: 'og:title' content: 扩展上下文 - ElysiaJS
- - meta
- name: 'description'
content: Elysia 默认提供一个最小化的上下文,允许我们使用 state、decorate、derive 和 resolve 根据特定需求扩展上下文。
- - meta
- property: 'og:description'
content: Elysia 默认提供一个最小化的上下文,允许我们使用 state、decorate、derive 和 resolve 根据特定需求扩展上下文。
Elysia 默认提供一个最小化的上下文,允许我们使用 state、decorate、derive 和 resolve 根据特定需求扩展上下文。
Elysia 允许我们为各种用例扩展上下文,例如:
我们可以使用以下 API 扩展 Elysia 的上下文来自定义上下文:
你应该仅在以下情况下扩展上下文:
否则,我们建议单独定义值或函数,而不是扩展上下文。
TIP
建议将与请求和响应相关的属性或频繁使用的函数分配给上下文,以实现关注点分离。
State 是在整个 Elysia 应用中共享的全局可变对象或状态。
一旦调用 state,值将在调用时添加到 store 属性一次,并可在处理程序中使用。
import { Elysia } from 'elysia'
new Elysia()
.state('version', 1)
.get('/a', ({ store: { version } }) => version)
.get('/b', ({ store }) => store)
.get('/c', () => 'still ok')
.listen(3000)GET
wrapper 值或类,请改用 decorate。import { Elysia } from 'elysia'
new Elysia()
// ❌ TypeError: counter 不存在于 store 中
.get('/error', ({ store }) => store.counter)Property 'counter' does not exist on type '{}'. .state('counter', 0)
// ✅ 因为我们之前分配了 counter,现在可以访问它
.get('/', ({ store }) => store.counter)GET
TIP
注意我们不能在分配之前使用状态值。
Elysia 会自动将状态值注册到 store 中,无需显式类型或额外的 TypeScript 泛型。
要修改状态,建议使用引用来修改,而不是使用实际值。
在 JavaScript 中访问属性时,如果我们从对象属性定义一个原始值作为新值,引用会丢失,该值将被视为新的独立值。
例如:
const store = {
counter: 0
}
store.counter++
console.log(store.counter) // ✅ 1我们可以使用 store.counter 来访问和修改属性。
但是,如果我们将 counter 定义为新值
const store = {
counter: 0
}
let counter = store.counter
counter++
console.log(store.counter) // ❌ 0
console.log(counter) // ✅ 1一旦原始值被重新定义为新变量,引用**"链接"**将丢失,导致意外行为。
这可以应用于 store,因为它也是一个全局可变对象。
import { Elysia } from 'elysia'
new Elysia()
.state('counter', 0)
// ✅ 使用引用,值是共享的
.get('/', ({ store }) => store.counter++)
// ❌ 在原始值上创建新变量,链接丢失
.get('/error', ({ store: { counter } }) => counter)GET
decorate 在调用时直接为上下文分配附加属性。
import { Elysia } from 'elysia'
class Logger {
log(value: string) {
console.log(value)
}
}
new Elysia()
.decorate('logger', new Logger())
// ✅ 从前一行定义
.get('/', ({ logger }) => {
logger.log('hi')
return 'hi'
})从上下文中的现有属性检索值并分配新属性。
Derive 在请求发生时在转换生命周期分配,允许我们"派生" (从现有属性创建新属性)。
import { Elysia } from 'elysia'
new Elysia()
.derive(({ headers }) => {
const auth = headers['authorization']
return {
bearer: auth?.startsWith('Bearer ') ? auth.slice(7) : null
}
})
.get('/', ({ bearer }) => bearer)GET
因为 derive 在新请求开始时分配一次,derive 可以访问请求属性,如 headers、query、body,而 store 和 decorate 不能。
类似于 derive 但确保类型完整性。
Resolve 允许我们为上下文分配新属性。
Resolve 在 beforeHandle 生命周期或验证之后调用,允许我们安全地解析请求属性。
import { Elysia, t } from 'elysia'
new Elysia()
.guard({
headers: t.Object({
bearer: t.String({
pattern: '^Bearer .+$'
})
})
})
.resolve(({ headers }) => {
return {
bearer: headers.bearer.slice(7)
}
})
.get('/', ({ bearer }) => bearer)由于 resolve 和 derive 基于 transform 和 beforeHandle 生命周期,我们可以从 resolve 和 derive 返回错误。如果错误从 derive 返回,Elysia 将提前退出并将错误作为响应返回。
import { Elysia } from 'elysia'
new Elysia()
.derive(({ headers, status }) => {
const auth = headers['authorization']
if(!auth) return status(400)
return {
bearer: auth?.startsWith('Bearer ') ? auth.slice(7) : null
}
})
.get('/', ({ bearer }) => bearer)state、decorate 为向上下文分配属性提供了类似的 API 模式,如下所示:
其中 derive 只能与重映射一起使用,因为它依赖于现有值。
我们可以使用 state 和 decorate 通过键值对模式分配值。
import { Elysia } from 'elysia'
class Logger {
log(value: string) {
console.log(value)
}
}
new Elysia()
.state('counter', 0)
.decorate('logger', new Logger())这种模式对于设置单个属性的可读性很有好处。
分配多个属性最好包含在一个对象中进行单次分配。
import { Elysia } from 'elysia'
new Elysia()
.decorate({
logger: new Logger(),
trace: new Trace(),
telemetry: new Telemetry()
})对象为设置多个值提供了更少重复的 API。
重映射是函数重新赋值。
允许我们从现有值创建新值,如重命名或删除属性。
通过提供一个函数,并返回一个全新的对象来重新赋值。
import { Elysia } from 'elysia'
new Elysia()
.state('counter', 0)
.state('version', 1)
.state(({ version, ...store }) => ({
...store,
elysiaVersion: 1
}))
// ✅ 从状态重映射创建
.get('/elysia-version', ({ store }) => store.elysiaVersion)
// ❌ 从状态重映射中排除
.get('/version', ({ store }) => store.version)Property 'version' does not exist on type '{ elysiaVersion: number; counter: number; }'.GET
使用状态重映射从现有值创建新初始值是个好主意。
但是,需要注意的是,Elysia 不提供此方法的响应性,因为重映射只分配初始值。
TIP
使用重映射时,Elysia 会将返回的对象视为新属性,删除对象中缺失的任何属性。
为了提供更流畅的体验,一些插件可能有很多属性值,逐一重映射可能会让人不知所措。
Affix 函数由 prefix 和 suffix 组成,允许我们重映射实例的所有属性。
import { Elysia } from 'elysia'
const setup = new Elysia({ name: 'setup' })
.decorate({
argon: 'a',
boron: 'b',
carbon: 'c'
})
const app = new Elysia()
.use(setup)
.prefix('decorator', 'setup')
.get('/', ({ setupCarbon, ...rest }) => setupCarbon)GET
允许我们轻松地批量重映射插件的属性,防止插件的名称冲突。
默认情况下,affix 将自动处理运行时和类型级别的代码,将属性重映射为驼峰命名法作为命名约定。
在某些情况下,我们也可以重映射插件的 all 属性:
import { Elysia } from 'elysia'
const setup = new Elysia({ name: 'setup' })
.decorate({
argon: 'a',
boron: 'b',
carbon: 'c'
})
const app = new Elysia()
.use(setup)
.prefix('all', 'setup')
.get('/', ({ setupCarbon, ...rest }) => setupCarbon)