Skip to content

生命周期

¥Lifecycle

生命周期事件允许你在预定义点拦截重要事件,从而允许你根据需要自定义服务器的行为。

¥Lifecycle events allow you to intercept important events at predefined points, allowing you to customize the behavior of your server as needed.

Elysia 的生命周期如下图所示。

¥Elysia's lifecycle can be illustrated as the following.

Elysia Life Cycle Graph

点击图片放大

以下是 Elysia 中可用的请求生命周期事件:

¥Below are the request lifecycle events available in Elysia:

为什么

¥Why

假设我们想要返回一些 HTML。

¥Imagine we want to return some HTML.

我们需要将 "内容类型" 标头设置为 "text/html",以便浏览器呈现 HTML。

¥We need to set "Content-Type" headers as "text/html" for the browser to render HTML.

如果有大量处理程序(例如约 200 个端点),则显式指定响应为 HTML 可能会重复。

¥Explicitly specifying that the response is HTML could be repetitive if there are a lot of handlers, say ~200 endpoints.

这会导致大量重复代码,仅仅为了指定 "text/html" "内容类型"。

¥This can lead to a lot of duplicated code just to specify the "text/html" "Content-Type"

但是,如果我们发送响应后,能够检测到响应是 HTML 字符串,然后自动附加标头,会怎么样呢?

¥But what if after we send a response, we could detect that the response is an HTML string and then append the header automatically?

这就是生命周期的概念发挥作用的时候了。

¥That's when the concept of lifecycle comes into play.

钩子

¥Hook

我们将每个拦截生命周期事件的函数称为 "hook",因为该函数会挂接到生命周期事件中。

¥We refer to each function that intercepts the lifecycle event as "hook", as the function hooks into the lifecycle event.

钩子可分为两类:

¥Hooks can be categorized into 2 types:

  1. 本地钩子:在特定路由上执行
  2. 拦截器钩子:在每条路由上执行

提示

该钩子将接受与处理程序相同的上下文;你可以想象在特定位置添加路由处理程序。

¥The hook will accept the same Context as a handler; you can imagine adding a route handler but at a specific point.

本地钩子

¥Local Hook

在特定路由上执行本地钩子。

¥A local hook is executed on a specific route.

要使用本地钩子,你可以将钩子内联到路由处理程序中:

¥To use a local hook, you can inline hook into a route handler:

typescript
import { Elysia } from 'elysia'
import { isHtml } from '@elysiajs/html'

new Elysia()
    .get('/', () => '<h1>Hello World</h1>', {
        afterHandle({ response, set }) {
            if (isHtml(response))
                set.headers['Content-Type'] = 'text/html; charset=utf8'
        }
    })
    .get('/hi', () => '<h1>Hello World</h1>')
    .listen(3000)

响应应如下所示:

¥The response should be listed as follows:

路径内容类型
/text/html;charset=utf8
/hitext/plain;charset=utf8

拦截器钩子

¥Interceptor Hook

将钩子注册到当前实例的每个后续处理程序中。

¥Register hook into every handler of the current instance that came after.

要添加拦截器钩子,你可以使用 .on,后跟一个驼峰命名的生命周期事件:

¥To add an interceptor hook, you can use .on followed by a lifecycle event in camelCase:

typescript
import { Elysia } from 'elysia'
import { isHtml } from '@elysiajs/html'

new Elysia()
    .get('/none', () => '<h1>Hello World</h1>')
    .onAfterHandle(({ response, set }) => {
        if (isHtml(response))
            set.headers['Content-Type'] = 'text/html; charset=utf8'
    })
    .get('/', () => '<h1>Hello World</h1>')
    .get('/hi', () => '<h1>Hello World</h1>')
    .listen(3000)

响应应如下所示:

¥The response should be listed as follows:

路径内容类型
/text/html;charset=utf8
/hitext/html;charset=utf8
/nonetext/plain;charset=utf8

其他插件的事件也会应用于路由,因此代码的顺序非常重要。

¥Events from other plugins are also applied to the route, so the order of code is important.

提示

以上代码仅适用于当前实例,不适用于父实例。

¥The code above will only apply to the current instance, not applying to parent.

有关原因,请参阅 scope

¥See scope to find out why

代码顺序

¥Order of code

Elysia 生命周期代码的顺序非常重要。

¥The order of Elysia's lifecycle code is very important.

因为事件只有在注册后才会应用于路由。

¥Because an event will only apply to routes after it is registered.

如果你将 onError 放在插件之前,插件将不会继承 onError 事件。

¥If you put the onError before plugin, plugin will not inherit the onError event.

typescript
import { Elysia } from 'elysia'

new Elysia()
 	.onBeforeHandle(() => {
        console.log('1')
    })
	.get('/', () => 'hi')
    .onBeforeHandle(() => {
        console.log('2')
    })
    .listen(3000)

控制台应记录以下内容:

¥Console should log the following:

bash
1

请注意,它不会记录 2,因为该事件是在路由之后注册的,因此不会应用于路由。

¥Notice that it doesn't log 2, because the event is registered after the route so it is not applied to the route.

这也适用于插件。

¥This also applies to the plugin.

typescript
import { Elysia } from 'elysia'

new Elysia()
	.onBeforeHandle(() => {
		console.log('1')
	})
	.use(someRouter)
	.onBeforeHandle(() => {
		console.log('2')
	})
	.listen(3000)

在上面的代码中,由于事件是在插件之后注册的,因此只会记录 1 个事件。

¥In the code above, only 1 will be logged, because the event is registered after the plugin.

这是因为每个事件都将内联到路由处理程序中,以创建真正的封装范围和静态代码分析。

¥This is because each events will be inline into a route handler to create a true encapsulation scope and static code analysis.

唯一的例外是 onRequest,它在路由处理程序之前执行,因此无法将其内联并绑定到路由进程。

¥The only exception is onRequest which is executed before the route handler so it couldn't be inlined and tied to the routing process instead.

请求

¥Request

收到每个新请求后执行的第一个生命周期事件。

¥The first lifecycle event to get executed for every new request is received.

由于 onRequest 旨在仅提供最重要的上下文以减少开销,因此建议在以下场景中使用:

¥As onRequest is designed to provide only the most crucial context to reduce overhead, it is recommended to use in the following scenarios:

  • 缓存

  • 速率限制器 / IP/区域锁定

  • 分析

  • 提供自定义标头,例如。 CORS

示例

¥Example

以下是用于对特定 IP 地址实现速率限制的伪代码。

¥Below is a pseudocode to enforce rate-limits on a certain IP address.

typescript
import { Elysia } from 'elysia'

new Elysia()
    .use(rateLimiter)
    .onRequest(({ rateLimiter, ip, set, status }) => {
        if (rateLimiter.check(ip)) return status(420, 'Enhance your calm')
    })
    .get('/', () => 'hi')
    .listen(3000)

如果 onRequest 返回了一个值,它将被用作响应,并且其余的生命周期将被跳过。

¥If a value is returned from onRequest, it will be used as the response and the rest of the lifecycle will be skipped.

前置上下文

¥Pre Context

Context 的 onRequest 类型为 PreContext,它是 Context 的最小表示,具有以下属性:请求:Request

¥Context's onRequest is typed as PreContext, a minimal representation of Context with the attribute on the following: request: Request

  • 设置:Set

  • store

  • decorators

Context 不提供 derived 值,因为派生基于 onTransform 事件。

¥Context doesn't provide derived value because derive is based on onTransform event.

解析

¥Parse

Parse 相当于 Express 中的 body 解析器。

¥Parse is an equivalent of body parser in Express.

一个用于解析请求体(body)的函数,返回值将附加到 Context.body 中。如果没有附加到 Context.body,Elysia 将继续迭代 onParse 指定的其他解析器函数,直到请求体被赋值或所有解析器都执行完毕。

¥A function to parse body, the return value will be append to Context.body, if not, Elysia will continue iterating through additional parser functions assigned by onParse until either body is assigned or all parsers have been executed.

默认情况下,Elysia 将解析内容类型为以下类型的主体:

¥By default, Elysia will parse the body with content-type of:

  • text/plain

  • application/json

  • multipart/form-data

  • application/x-www-form-urlencoded

建议使用 onParse 事件来提供 Elysia 未提供的自定义 body 解析器。

¥It's recommended to use the onParse event to provide a custom body parser that Elysia doesn't provide.

示例

¥Example

以下是根据自定义标头检索值的示例代码。

¥Below is an example code to retrieve value based on custom headers.

typescript
import { Elysia } from 'elysia'

new Elysia().onParse(({ request, contentType }) => {
    if (contentType === 'application/custom-type') return request.text()
})

返回值将分配给 Context.body。如果没有,Elysia 将继续从 onParse 堆栈中迭代其他解析器函数,直到分配主体或执行完所有解析器为止。

¥The returned value will be assigned to Context.body. If not, Elysia will continue iterating through additional parser functions from onParse stack until either body is assigned or all parsers have been executed.

上下文

¥Context

onParse Context 扩展自 Context,并具有以下附加属性:

¥onParse Context is extends from Context with additional properties of the following:

  • contentType:请求的 Content-Type 标头

所有上下文都基于普通上下文,并且可以像路由处理程序中的普通上下文一样使用。

¥All of the context is based on normal context and can be used like normal context in route handler.

解析器

¥Parser

默认情况下,Elysia 将尝试提前确定主体解析函数,并选择最合适的函数以加快解析速度。

¥By default, Elysia will try to determine body parsing function ahead of time and pick the most suitable function to speed up the process.

Elysia 能够通过读取 body 来判断主体机能。

¥Elysia is able to determine that body function by reading body.

请看以下示例:

¥Take a look at this example:

typescript
import { Elysia, t } from 'elysia'

new Elysia().post('/', ({ body }) => body, {
    body: t.Object({
        username: t.String(),
        password: t.String()
    })
})

Elysia 读取了正文的 schema,发现其类型完全是一个对象,因此正文很可能是 JSON 格式。Elysia 随后会提前选择 JSON 正文解析器函数并尝试解析正文。

¥Elysia read the body schema and found that, the type is entirely an object, so it's likely that the body will be JSON. Elysia then picks the JSON body parser function ahead of time and tries to parse the body.

以下是 Elysia 用于选择 body 解析器类型的标准

¥Here's a criteria that Elysia uses to pick up type of body parser

  • application/json:主体类型为 t.Object

  • multipart/form-data:body 类型为 t.Object,并且与 t.File 深度相同。

  • application/x-www-form-urlencoded:主体类型为 t.URLEncoded

  • text/plain:其他原始类型

这允许 Elysia 提前优化主体解析器,并减少编译时的开销。

¥This allows Elysia to optimize body parser ahead of time, and reduce overhead in compile time.

显式解析器

¥Explicit Parser

但是,在某些情况下,如果 Elysia 无法选择正确的主体解析器函数,我们可以通过指定 type 明确告诉 Elysia 使用某个函数。

¥However, in some scenario if Elysia fails to pick the correct body parser function, we can explicitly tell Elysia to use a certain function by specifying type.

typescript
import { Elysia } from 'elysia'

new Elysia().post('/', ({ body }) => body, {
    // Short form of application/json
    parse: 'json'
})

这使我们能够控制 Elysia 的行为,以便在复杂场景中选择符合我们需求的 body 解析器函数。

¥This allows us to control Elysia behavior for picking body parser function to fit our needs in a complex scenario.

type 可能是以下之一:

¥type may be one of the following:

typescript
type ContentType = |
    // Shorthand for 'text/plain'
    | 'text'
    // Shorthand for 'application/json'
    | 'json'
    // Shorthand for 'multipart/form-data'
    | 'formdata'
    // Shorthand for 'application/x-www-form-urlencoded'
    | 'urlencoded'
    // Skip body parsing entirely
    | 'none'
    | 'text/plain'
    | 'application/json'
    | 'multipart/form-data'
    | 'application/x-www-form-urlencoded'

跳过正文解析

¥Skip Body Parsing

当你需要将第三方库与 HTTP 处理程序(例如 trpcorpc)集成时,它会抛出 Body is already used 异常。

¥When you need to integrate a third-party library with HTTP handler like trpc, orpc, and it throw Body is already used.

这是因为 Web 标准请求只能被解析一次。

¥This is because Web Standard Request can be parsed only once.

Elysia 和第三方库都有自己的 body 解析器,因此你可以通过指定 parse: 'none' 跳过 Elysia 端的 body 解析。

¥Both Elysia and the third-party library both has its own body parser, so you can skip body parsing on Elysia side by specifying parse: 'none'

typescript
import { Elysia } from 'elysia'

new Elysia()
	.post(
		'/',
		({ request }) => library.handle(request),
		{
			parse: 'none'
		}
	)

自定义解析器

¥Custom Parser

你可以使用 parser 注册自定义解析器:

¥You can provide register a custom parser with parser:

typescript
import { Elysia } from 'elysia'

new Elysia()
    .parser('custom', ({ request, contentType }) => {
        if (contentType === 'application/elysia') return request.text()
    })
    .post('/', ({ body }) => body, {
        parse: ['custom', 'json']
    })

转换

¥Transform

在验证过程之前立即执行,旨在改变上下文以符合验证或附加新值。

¥Executed just before Validation process, designed to mutate context to conform with the validation or appending new value.

建议在以下情况下使用 transform:

¥It's recommended to use transform for the following:

  • 修改现有上下文以符合验证要求。

  • derive 基于 onTransform,但支持提供类型。

示例

¥Example

以下是使用 transform 将参数转换为数值的示例。

¥Below is an example of using transform to mutate params to be numeric values.

typescript
import { Elysia, t } from 'elysia'

new Elysia()
    .get('/id/:id', ({ params: { id } }) => id, {
        params: t.Object({
            id: t.Number()
        }),
        transform({ params }) {
            const id = +params.id

            if (!Number.isNaN(id)) params.id = id
        }
    })
    .listen(3000)

派生

¥Derive

在验证之前直接将新值附加到上下文。它与 transform 存储在同一个堆栈中。

¥Append new value to context directly before validation. It's stored in the same stack as transform.

与 state 和 decorate 不同,derive 会在服务器启动前赋值。derive 在每个请求发生时分配一个属性。这使我们能够将一条信息提取到属性中。

¥Unlike state and decorate that assigned value before the server started. derive assigns a property when each request happens. This allows us to extract a piece of information into a property instead.

typescript
import { Elysia } from 'elysia'

new Elysia()
    .derive(({ headers }) => {
        const auth = headers['Authorization']

        return {
            bearer: auth?.startsWith('Bearer ') ? auth.slice(7) : null
        }
    })
    .get('/', ({ bearer }) => bearer)

因为 derive 在新请求启动后才会赋值,所以 derive 可以访问请求属性,例如 headers、query、body,而 store 和 decorate 则不能。

¥Because derive is assigned once a new request starts, derive can access Request properties like headers, query, body where store, and decorate can't.

与状态不同,并进行装饰。由 derive 分配的属性是唯一的,不会与其他请求共享。

¥Unlike state, and decorate. Properties which assigned by derive is unique and not shared with another request.

队列

¥Queue

derivetransform 存储在同一个队列中。

¥derive and transform are stored in the same queue.

typescript
import { Elysia } from 'elysia'

new Elysia()
    .onTransform(() => {
        console.log(1)
    })
    .derive(() => {
        console.log(2)

        return {}
    })

控制台应打印以下内容:

¥The console should log as the following:

bash
1
2

处理前

¥Before Handle

在验证之后、主路由处理程序之前执行。

¥Execute after validation and before the main route handler.

设计旨在在运行主处理程序之前提供自定义验证以满足特定需求。

¥Designed to provide a custom validation to provide a specific requirement before running the main handler.

如果返回了一个值,路由处理程序将被跳过。

¥If a value is returned, the route handler will be skipped.

建议在以下情况下使用 Before Handle:

¥It's recommended to use Before Handle in the following situations:

  • 限制访问检查:授权,用户登录

  • 自定义数据结构请求要求

示例

¥Example

以下是使用 before 句柄检查用户登录的示例。

¥Below is an example of using the before handle to check for user sign-in.

typescript
import { Elysia } from 'elysia'
import { validateSession } from './user'

new Elysia()
    .get('/', () => 'hi', {
        beforeHandle({ set, cookie: { session }, status }) {
            if (!validateSession(session.value)) return status(401)
        }
    })
    .listen(3000)

响应应如下所示:

¥The response should be listed as follows:

已登录响应
未经授权
你好

守护

¥Guard

当我们需要将相同的 before 句柄应用于多个路由时,可以使用 guard 将相同的 before 句柄应用于多个路由。

¥When we need to apply the same before handle to multiple routes, we can use guard to apply the same before handle to multiple routes.

typescript
import { Elysia } from 'elysia'
import { signUp, signIn, validateSession, isUserExists } from './user'

new Elysia()
    .guard(
        {
            beforeHandle({ set, cookie: { session }, status }) {
                if (!validateSession(session.value)) return status(401)
            }
        },
        (app) =>
            app
                .get('/user/:id', ({ body }) => signUp(body))
                .post('/profile', ({ body }) => signIn(body), {
                    beforeHandle: isUserExists
                })
    )
    .get('/', () => 'hi')
    .listen(3000)

解析

¥Resolve

验证后将新值附加到上下文。它与 beforeHandle 存储在同一个堆栈中。

¥Append new value to context after validation. It's stored in the same stack as beforeHandle.

解析语法与 derive 相同,以下是从授权插件中检索承载标头的示例。

¥Resolve syntax is identical to derive, below is an example of retrieving a bearer header from the Authorization plugin.

typescript
import { Elysia, t } from 'elysia'

new Elysia()
    .guard(
        {
            headers: t.Object({
                authorization: t.TemplateLiteral('Bearer ${string}')
            })
        },
        (app) =>
            app
                .resolve(({ headers: { authorization } }) => {
                    return {
                        bearer: authorization.split(' ')[1]
                    }
                })
                .get('/', ({ bearer }) => bearer)
    )
    .listen(3000)

使用 resolveonBeforeHandle 存储在同一个队列中。

¥Using resolve and onBeforeHandle is stored in the same queue.

typescript
import { Elysia } from 'elysia'

new Elysia()
    .onBeforeHandle(() => {
        console.log(1)
    })
    .resolve(() => {
        console.log(2)

        return {}
    })
    .onBeforeHandle(() => {
        console.log(3)
    })

控制台应打印以下内容:

¥The console should log as the following:

bash
1
2
3

与 derive 相同,由 resolve 分配的属性是唯一的,不会与其他请求共享。

¥Same as derive, properties which assigned by resolve is unique and not shared with another request.

守护解析

¥Guard resolve

由于 resolve 在本地钩子中不可用,建议使用 guard 封装 resolve 事件。

¥As resolve is not available in local hook, it's recommended to use guard to encapsulate the resolve event.

typescript
import { Elysia } from 'elysia'
import { isSignIn, findUserById } from './user'

new Elysia()
    .guard(
        {
            beforeHandle: isSignIn
        },
        (app) =>
            app
                .resolve(({ cookie: { session } }) => ({
                    userId: findUserById(session.value)
                }))
                .get('/profile', ({ userId }) => userId)
    )
    .listen(3000)

处理后

¥After Handle

在主处理程序之后执行,用于将前处理程序和路由处理程序的返回值映射到正确的响应中。

¥Execute after the main handler, for mapping a returned value of before handle and route handler into a proper response.

建议在以下情况下使用 After Handle:

¥It's recommended to use After Handle in the following situations:

  • 将请求转换为新值,例如压缩、事件流

  • 根据响应值添加自定义标头,例如。内容类型

示例

¥Example

以下是使用 after 句柄将 HTML 内容类型添加到响应标头的示例。

¥Below is an example of using the after handle to add HTML content type to response headers.

typescript
import { Elysia } from 'elysia'
import { isHtml } from '@elysiajs/html'

new Elysia()
    .get('/', () => '<h1>Hello World</h1>', {
        afterHandle({ response, set }) {
            if (isHtml(response))
                set.headers['content-type'] = 'text/html; charset=utf8'
        }
    })
    .get('/hi', () => '<h1>Hello World</h1>')
    .listen(3000)

响应应如下所示:

¥The response should be listed as follows:

路径内容类型
/text/html;charset=utf8
/hitext/plain;charset=utf8

返回值

¥Returned Value

如果返回值,After Handle 将使用返回值作为新的响应值,除非该值未定义。

¥If a value is returned After Handle will use a return value as a new response value unless the value is undefined

以上示例可以改写如下:

¥The above example could be rewritten as the following:

typescript
import { Elysia } from 'elysia'
import { isHtml } from '@elysiajs/html'

new Elysia()
    .get('/', () => '<h1>Hello World</h1>', {
        afterHandle({ response, set }) {
            if (isHtml(response)) {
                set.headers['content-type'] = 'text/html; charset=utf8'
                return new Response(response)
            }
        }
    })
    .get('/hi', () => '<h1>Hello World</h1>')
    .listen(3000)

与 beforeHandle 不同,afterHandle 返回值后,不会跳过 afterHandle 的迭代。

¥Unlike beforeHandle, after a value is returned from afterHandle, the iteration of afterHandle will NOT be skipped.

上下文

¥Context

onAfterHandle 上下文扩展自 Context,并添加了 response 属性,即返回给客户端的响应。

¥onAfterHandle context extends from Context with the additional property of response, which is the response to return to the client.

onAfterHandle 上下文基于普通上下文,可以像普通上下文一样在路由处理程序中使用。

¥The onAfterHandle context is based on the normal context and can be used like the normal context in route handlers.

映射响应

¥Map Response

在 "afterHandle" 之后立即执行,旨在提供自定义响应映射。

¥Executed just after "afterHandle", designed to provide custom response mapping.

建议在以下情况下使用 transform:

¥It's recommended to use transform for the following:

  • 压缩

  • 将值映射到 Web 标准响应中

示例

¥Example

以下是使用 mapResponse 提供响应压缩的示例。

¥Below is an example of using mapResponse to provide Response compression.

typescript
import { Elysia } from 'elysia'

const encoder = new TextEncoder()

new Elysia()
    .mapResponse(({ responseValue, set }) => {
        const isJson = typeof response === 'object'

        const text = isJson
            ? JSON.stringify(responseValue)
            : (responseValue?.toString() ?? '')

        set.headers['Content-Encoding'] = 'gzip'

        return new Response(Bun.gzipSync(encoder.encode(text)), {
            headers: {
                'Content-Type': `${
                    isJson ? 'application/json' : 'text/plain'
                }; charset=utf-8`
            }
        })
    })
    .get('/text', () => 'mapResponse')
    .get('/json', () => ({ map: 'response' }))
    .listen(3000)

类似 parse 和 beforeHandle,在返回值后,mapResponse 的下一次迭代将被跳过。

¥Like parse and beforeHandle, after a value is returned, the next iteration of mapResponse will be skipped.

Elysia 将自动处理 mapResponse 中 set.headers 的合并过程。我们无需担心手动将 set.headers 附加到 Response。

¥Elysia will handle the merging process of set.headers from mapResponse automatically. We don't need to worry about appending set.headers to Response manually.

错误处理(错误处理)

¥On Error (Error Handling)

专为错误处理而设计。当任何生命周期中抛出错误时,都会执行此函数。

¥Designed for error handling. It will be executed when an error is thrown in any lifecycle.

建议在以下情况下使用 on Error:

¥It's recommended to use on Error in the following situations:

  • 提供自定义错误信息

  • 故障安全处理、错误处理程序或重试请求

  • 日志记录和分析

示例

¥Example

Elysia 捕获处理程序中抛出的所有错误,对错误代码进行分类,然后将其通过管道传输到 onError 中间件。

¥Elysia catches all the errors thrown in the handler, classifies the error code, and pipes them to onError middleware.

typescript
import { Elysia } from 'elysia'

new Elysia()
    .onError(({ code, error }) => {
        return new Response(error.toString())
    })
    .get('/', () => {
        throw new Error('Server is during maintenance')

        return 'unreachable'
    })

通过 onError,我们可以捕获错误并将其转换为自定义错误消息。

¥With onError we can catch and transform the error into a custom error message.

提示

重要的是,必须在我们要应用它的处理程序之前调用 onError

¥It's important that onError must be called before the handler we want to apply it to.

自定义 404 消息

¥Custom 404 message

例如,返回自定义 404 消息:

¥For example, returning custom 404 messages:

typescript
import { Elysia, NotFoundError } from 'elysia'

new Elysia()
    .onError(({ code, status, set }) => {
        if (code === 'NOT_FOUND') return status(404, 'Not Found :(')
    })
    .post('/', () => {
        throw new NotFoundError()
    })
    .listen(3000)

上下文

¥Context

onError Context 扩展自 Context,并具有以下附加属性:

¥onError Context is extends from Context with additional properties of the following:

  • 错误:抛出的值

  • 代码:错误代码

错误代码

¥Error Code

Elysia 错误代码包含以下内容:

¥Elysia error code consists of:

  • NOT_FOUND

  • PARSE

  • VALIDATION

  • INTERNAL_SERVER_ERROR

  • INVALID_COOKIE_SIGNATURE

  • INVALID_FILE_TYPE

  • UNKNOWN

  • 数字(基于 HTTP 状态)

默认情况下,抛出的错误代码为 UNKNOWN

¥By default, the thrown error code is UNKNOWN.

提示

如果没有返回错误响应,则将使用 error.name 返回错误。

¥If no error response is returned, the error will be returned using error.name.

本地错误

¥Local Error

与其他生命周期相同,我们使用 guard 向 scope 中提供一个错误:

¥Same as others life-cycle, we provide an error into an scope using guard:

typescript
import { Elysia } from 'elysia'

new Elysia()
    .get('/', () => 'Hello', {
        beforeHandle({ set, request: { headers }, error }) {
            if (!isSignIn(headers)) throw error(401)
        },
        error({ error }) {
            return 'Handled'
        }
    })
    .listen(3000)

响应后

¥After Response

在响应发送给客户端之后执行。

¥Executed after the response sent to the client.

建议在以下情况下使用 After Response:

¥It's recommended to use After Response in the following situations:

  • 清理响应

  • 日志记录和分析

示例

¥Example

以下是使用响应句柄检查用户登录的示例。

¥Below is an example of using the response handle to check for user sign-in.

typescript
import { Elysia } from 'elysia'

new Elysia()
    .onAfterResponse(() => {
        console.log('Response', performance.now())
    })
    .listen(3000)

控制台应记录以下内容:

¥Console should log as the following:

bash
Response 0.0000
Response 0.0001
Response 0.0002

响应

¥Response

映射响应 类似,afterResponse 也接受 responseValue 值。

¥Similar to Map Response, afterResponse also accept a responseValue value.

typescript
import { Elysia } from 'elysia'

new Elysia()
	.onAfterResponse(({ responseValue }) => {
		console.log(responseValue)
	})
	.get('/', () => 'Hello')
	.listen(3000)

onAfterResponse 中的 response 不是 Web 标准的 Response,而是从处理程序返回的值。

¥response from onAfterResponse, is not a Web-Standard's Response but is a value that is returned from the handler.

要获取标头和处理程序返回的状态,我们可以从上下文访问 set

¥To get a headers, and status returned from the handler, we can access set from the context.

typescript
import { Elysia } from 'elysia'

new Elysia()
	.onAfterResponse(({ set }) => {
		console.log(set.status, set.headers)
	})
	.get('/', () => 'Hello')
	.listen(3000)