localhost
GET
Elysia默认提供了一个最小的上下文,允许我们使用状态、装饰、派生和解析根据我们的具体需求扩展上下文。
Elysia允许我们为各种用例扩展上下文,例如:
我们可以通过使用以下API来扩展Elysia的上下文,以自定义上下文:
只有在以下情况下,您才应该扩展上下文:
否则,我们建议将值或函数单独定义,而不是扩展上下文。
TIP
建议将与请求和响应相关的属性或经常使用的函数分配给Context,以实现关注点分离。
状态是一个全局可变对象或状态,在Elysia应用程序中共享。
一旦调用 state,值将添加到 store 属性 一次在调用时,并且可以在处理程序中使用。
import { Elysia } from 'elysia'
new Elysia()
.state('version', 1)
.get('/a', ({ store: { version } }) => version)
.get('/b', ({ store }) => store)
.get('/c', () => '仍然可以')
.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会自动将状态值注册到商店中,而无需显式类型或额外的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 直接在调用时为 Context 分配一个额外的属性。
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'
})
从 Context 中现有属性中检索值并分配新属性。
在请求发生时 派生 在转换生命周期中分配,允许我们 "派生" (从现有属性创建新属性)。
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,但确保类型完整性。
解析允许我们将新属性分配到上下文中。
解析在 beforeHandle 生命周期或 after validation 阶段被调用,允许我们安全地 解析 请求属性。
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)
由于解析和派生基于 transform 和 beforeHandle 生命周期,我们可以从解析和派生中返回错误。如果从 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 为向Context分配属性提供了类似API模式,如下所示:
其中 derive 只能与 remap 一起使用,因为它依赖于现有值。
我们可以使用 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会将返回的对象视为新属性,从而移除对象中缺失的任何属性。
为提供更流畅的体验,一些插件可能有很多属性值,这使得逐个重映射变得繁琐。
附加 函数由 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
这使我们可以轻松地批量重映射插件的属性,防止插件名称冲突。
默认情况下,附加 将自动处理运行时、类型级别的代码,将属性重映射为命名约定的驼峰式命名。
在某些情况下,我们还可以重映射插件的 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)