扩展上下文

Elysia 提供了一个上下文,它配备了小工具,帮助您入门。

您可以使用以下方式扩展 Elysia 的上下文:

  1. 装饰(Decorate)
  2. 状态(State)
  3. 解析(Resolve)
  4. 派生(Derive)

Decorate

单例,且不可变的属性,所有请求共享。

typescript
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'
    })

装饰后的值将在上下文中作为只读属性可用,详见 装饰(Decorate)

State

一个可变的引用,所有请求共享。

typescript
import { Elysia } from 'elysia'

new Elysia()
	.state('count', 0)
	.get('/', ({ store }) => {
		store.count++

		return store.count
	})

状态将在每个请求共享的 context.store 中可用,详见 状态(State)

Resolve / Derive

装饰(Decorate) 的值注册为单例。

解析(Resolve)派生(Derive) 允许您为每个请求抽象一个上下文值。

typescript
import { Elysia } from 'elysia'

new Elysia()
	.derive(({ headers: { authorization } }) => ({
		authorization
	}))
	.get('/', ({ authorization }) => authorization)

任何返回的值都会在上下文中可用,除了状态(status),它会直接发送给客户端,并中止后续处理器。

两个 解析(resolve)派生(derive) 的语法相似,但使用场景不同。

在底层,两者是一个语法糖 (具有类型安全) 的生命周期:

由于 解析(resolve) 是基于 转换(transform) 的,这意味着数据尚未验证,也未进行强制转换/转换。如果你需要验证过的数据,建议使用 解析(resolve)

作用域

状态(State)装饰(Decorate) 是跨所有请求和实例共享的。
解析(Resolve)派生(Derive) 是每个请求的,且有封装作用域 (因为它们基于生命周期事件)

如果您想使用来自插件的解析/派生值,您需要声明一个 作用域(Scope)

typescript
import { Elysia } from 'elysia'

const plugin = new Elysia()
	.derive(
		{ as: 'scoped' }, // [!代码 ++]
		({ headers: { authorization } }) => ({
			authorization
		})
	)

new Elysia()
	.use(plugin)
	.get('/', ({ authorization }) => authorization)
	.listen(3000)

练习

让我们尝试扩展 Elysia 的上下文。

  1. Extract "query.age"

    Let's extract a "query.age" as "age". If it doesn't existed, return 401.

  2. Use "age"

    In a main handler of GET "/profile", return a value of "age" from the context

Show answer

我们可以使用 解析(resolve) 从查询中提取年龄。

typescript
import { Elysia, t } from 'elysia'

class Logger {
	log(info: string) {
		console.log(info)
	}
}

new Elysia()
	.decorate('logger', new Logger())
	.onRequest(({ request, logger }) => {
		logger.log(`请求地址 ${request.url}`)
	})
	.guard({
		query: t.Optional(
			t.Object({
				age: t.Number({ min: 15 })
			})
		)
	})
	.resolve(({ query: { age }, status }) => {
		if(!age) return status(401)

		return { age }
	})
	.get('/profile', ({ age }) => age)
	.listen(3000)
  • index.ts