输出数据
输出的结果包含成功和失败两种情况。在控制器方法中直接 return 即可返回成功结果,使用 fail() 抛出业务错误。
注意
如果使用空 return 或者不 return,将会返回 404 状态码,除非之前给 ctx.body 设置了值。
成功输出
import { Get, Post, Context } from 'zenweb';
export class TestController {
// 直接 return 返回成功结果
@Get()
test1() {
return 'OK'; // → {"data":"OK"}
}
@Get()
test2() {
return { id: 1, name: 'Alice' }; // → {"data":{"id":1,"name":"Alice"}}
}
@Get()
test3() {
return [1, 2, 3]; // → {"data":[1,2,3]}
}
// 使用 ctx.success() 显式设置结果
@Get()
test4(ctx: Context) {
ctx.success('OK');
// 注意:ctx.success() 不会中断执行,后续再次调用会覆盖之前的结果
}
}
失败输出
使用 fail() 来抛出业务错误,它会立即中断代码执行:
import { Get, fail } from 'zenweb';
export class TestController {
@Get()
test1() {
fail('这是一个错误'); // → {"code":null,"message":"这是一个错误"}
}
@Get()
test2() {
fail(100); // → {"code":100,"message":"request fail"}
}
@Get()
test3() {
fail(1111, '带有错误代码的错误信息'); // → {"code":1111,"message":"带有错误代码的错误信息"}
}
@Get()
test4() {
fail({
code: 123,
message: '自定义错误细节',
data: { field: 'email', reason: 'duplicate' },
}); // → {"code":123,"message":"自定义错误细节","data":{"field":"email","reason":"duplicate"}}
}
}
提示
fail() 是一个异常抛出函数,可以在代码的任何地方使用,不需要 return 就可阻止程序继续执行。
配置 result 模块
在 create() 中传入 result 选项来控制输出行为:
src/index.ts
import { create } from 'zenweb';
create({
result: {
// 默认失败代码,当 fail() 未指定 code 时使用
failCode: 500,
// 默认失败 HTTP 状态码
// 422 表示"业务错误"(区别于 500 服务器错误)
failStatus: 422,
// 默认失败消息
failMessage: 'request fail',
// 在响应头中输出错误代码
// true → 使用默认头名称 "X-Fail-Code"
// 'X-Error-Code' → 自定义头名称
// false → 关闭
failCodeHeader: true,
// 是否暴露意外错误信息(非 fail() 抛出的 Error)
// 开发环境建议开启,生产环境务必关闭
exposeUnexpected: process.env.NODE_ENV !== 'production',
// 意外错误的 HTTP 状态码
unexpectedStatus: 500,
// 自定义成功结果包装
successWrap(ctx, data) {
return { code: 0, data, timestamp: Date.now() };
},
// 自定义失败结果包装
failWrap(ctx, err) {
return { code: err.code, msg: err.message, data: err.data };
},
},
});
配置选项说明
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
failCode | number | - | 默认失败代码 |
failStatus | number | 422 | 默认失败 HTTP 状态码 |
failMessage | string | 'request fail' | 默认失败消息 |
failCodeHeader | boolean \| string | true | 在响应头中输出错误代码 |
exposeUnexpected | boolean | 开发环境 true | 是否暴露意外错误详情 |
unexpectedStatus | number | 500 | 意外错误状态码 |
unexpectedCode | number | 500 | 意外错误代码 |
successWrap | function | - | 自定义成功结果包装函数 |
failWrap | function | - | 自定义失败结果包装函数 |
renders | ResultRender[] | - | 自定义渲染器列表 |
failCodeHeader 响应头
当 failCodeHeader: true(默认开启)时,fail() 的错误代码会写入 HTTP 响应头 X-Fail-Code:
# 请求一个会 fail(100) 的接口
curl -i http://127.0.0.1:7001/test
# HTTP/1.1 422 Unprocessable Entity
# X-Fail-Code: 100
# Content-Type: application/json; charset=utf-8
#
# {"code":100,"message":"request fail"}
前端可以根据 X-Fail-Code 响应头判断请求是否成功,无需解析响应体。
消息代码管理
当项目规模变大时,散落在代码中的错误字符串难以维护。推荐使用 message-codes.json 集中管理错误消息:
message-codes.json
{
"user.notfound": "用户不存在",
"user.login.fail": "用户名或密码错误",
"user.duplicate": "用户名 {name} 已存在",
"order.stock.insufficient": "商品 {name} 库存不足,当前剩余 {stock} 件"
}
然后在代码中使用消息代码:
import { Get, fail, Context } from 'zenweb';
export class UserController {
@Get()
async detail(ctx: Context) {
const user = await findUser(1);
if (!user) {
// 使用消息代码,会自动从 message-codes.json 中查找对应的错误消息
ctx.messageCodeResolver.fail('user.notfound');
}
return user;
}
@Get()
async register(ctx: Context) {
// 支持参数替换
ctx.messageCodeResolver.fail('user.duplicate', { name: 'Alice' });
// 输出: {"code":null,"message":"用户名 Alice 已存在"}
}
}
也可以使用 format() 只获取消息文本而不抛出错误:
const msg = ctx.messageCodeResolver.format('user.duplicate', { name: 'Bob' });
console.log(msg); // "用户名 Bob 已存在"
自定义结果渲染器
除了默认的 JSON 输出,你还可以注册自定义渲染器来改变输出格式。例如根据请求的 Accept 头返回不同格式:
src/index.ts
import { create } from 'zenweb';
import { ResultRender, Context } from 'zenweb';
// 自定义 XML 渲染器
class XMLRender implements ResultRender {
enwrap = true;
type = 'xml';
match(ctx: Context) {
return ctx.accepts('application/xml') === 'application/xml';
}
render(ctx: Context, data: unknown) {
// 将数据转换为 XML 格式
return toXML(data);
}
}
create({
result: {
renders: [XMLRender],
},
});
渲染器按注册顺序匹配,如果所有自定义渲染器都不匹配,则使用默认的 JSONRender。
ctx.isFail 判断
在中间件中可以通过 ctx.isFail 判断当前请求是否产生了业务错误:
import { Middleware } from 'zenweb';
function logMiddleware(): Middleware {
return async (ctx, next) => {
await next();
if (ctx.isFail) {
ctx.log.warn('业务错误: %s', ctx.body);
}
};
}