Skip to content

错误处理

本页提供了一个更高级的指南,用于在 Elysia 中有效处理错误。

如果你还没有阅读 “生命周期 (onError)”,建议先阅读它。

自定义验证消息

在定义模式时,可以为每个字段提供自定义验证消息。

当验证失败时,该消息将原样返回。

ts
import { Elysia } from 'elysia'

new Elysia().get('/:id', ({ params: { id } }) => id, {
    params: t.Object({
        id: t.Number({
            error: 'id 必须是数字'
        })
    })
})

如果 id 字段验证失败,响应将返回 id 必须是数字

localhost

GET

验证详情

schema.error 返回一个值将原样返回验证消息,但有时你也希望返回验证细节,比如字段名和期望类型。

你可以通过使用 validationDetail 来实现这一点。

ts
import { Elysia, validationDetail } from 'elysia'

new Elysia().get('/:id', ({ params: { id } }) => id, {
    params: t.Object({
        id: t.Number({
            error: validationDetail('id 必须是数字') 
        })
    })
})

这将在响应中包含所有验证详情,比如字段名和期望类型。

localhost

GET

但是如果你计划在每个字段都使用 validationDetail,手动添加会很麻烦。

你可以在 onError 钩子中自动处理验证详情。

ts
new Elysia()
    .onError(({ error, code }) => {
        if (code === 'VALIDATION') return error.detail(error.message) 
    })
    .get('/:id', ({ params: { id } }) => id, {
        params: t.Object({
            id: t.Number({
                error: 'id 必须是数字'
            })
        })
    })
    .listen(3000)

这将为每个带有自定义消息的验证错误添加自定义验证详情。

生产环境中的验证详情

默认情况下,如果 NODE_ENVproduction,Elysia 会省略所有验证详情。

这样做是为了防止泄露验证模式的敏感信息,比如字段名和期望类型,这可能被攻击者利用。

Elysia 只会返回验证失败的信息,而不包含任何详情。

json
{
    "type": "validation",
    "on": "body",
    "found": {},
    // 仅对自定义错误显示
    "message": "x 必须是数字"
}

message 属性是可选的,默认省略,除非你在模式中提供了自定义错误消息。

自定义错误

Elysia 支持类型层级和实现层级的自定义错误。

默认情况下,Elysia 有一组内置错误类型,如 VALIDATIONNOT_FOUND,会自动缩小类型。

如果 Elysia 不认识该错误,错误代码将是 UNKNOWN,默认状态码为 500

但你也可以通过 Elysia.error 添加带类型安全的自定义错误,它能帮助缩小错误类型,提供完整类型安全和自动补全,并支持自定义状态码,如下所示:

typescript
import { 
Elysia
} from 'elysia'
class
MyError
extends
Error
{
constructor(public
message
: string) {
super(
message
)
} } new
Elysia
()
.
error
({
MyError
}) .
onError
(({
code
,
error
}) => {
switch (
code
) {
// 自动补全 case 'MyError': // 类型缩小 // 悬停查看 error 的类型为 `CustomError` return
error
} }) .
get
('/:id', () => {
throw new
MyError
('Hello Error')
})

自定义状态码

你也可以通过在自定义错误类中添加 status 属性,为你的自定义错误指定状态码。

typescript
import { Elysia } from 'elysia'

class MyError extends Error {
    status = 418

    constructor(public message: string) {
        super(message)
    }
}

当抛出该错误时,Elysia 会使用此状态码。

否则你也可以在 onError 钩子中手动设置状态码。

typescript
import { Elysia } from 'elysia'

class MyError extends Error {
	constructor(public message: string) {
		super(message)
	}
}

new Elysia()
	.error({
		MyError
	})
	.onError(({ code, error, status }) => {
		switch (code) {
			case 'MyError':
				return status(418, error.message)
		}
	})
	.get('/:id', () => {
		throw new MyError('Hello Error')
	})

自定义错误响应

你也可以在自定义错误类中提供一个自定义的 toResponse 方法,当错误被抛出时返回自定义响应。

typescript
import { Elysia } from 'elysia'

class MyError extends Error {
	status = 418

	constructor(public message: string) {
		super(message)
	}

	toResponse() {
		return Response.json({
			error: this.message,
			code: this.status
		}, {
			status: 418
		})
	}
}

抛出或返回

大多数错误处理可以通过抛出错误并在 onError 中处理完成。

status 可能会让人困惑,因为它既可以作为返回值也可以抛出错误。

根据你的具体需求,它可以是 返回抛出

  • 如果 status抛出,会被 onError 中间件捕获。
  • 如果 status返回,不会被 onError 中间件捕获。

请看以下代码:

typescript
import { Elysia, file } from 'elysia'

new Elysia()
    .onError(({ code, error, path }) => {
        if (code === 418) return 'caught'
    })
    .get('/throw', ({ status }) => {
        // 这会被 onError 捕获
        throw status(418)
    })
    .get('/return', ({ status }) => {
        // 这不会被 onError 捕获
        return status(418)
    })
localhost

GET