本文首发于:kapeter.com/post/32
文章列表:
这是Eggjs最佳实践系列的第二篇文章。
前期调研
在设计之前,先来看一下一份规范的接口输出协议是怎么样的。
{
code: "-10001",
message: "The route does not exist, please check whether the route is correct.",
result: null,
sysTime: 1620632378967
}
命名上可能有所差异,但基本上都是按照以下四个属性:
code: 全局唯一的响应码,前端可以根据该响应码进行对应的错误处理;message:响应码(code)的语义化解释;result:用于存放接口数据;sysTime:可选,当前服务器时间,可以与客户端时间进行比对。
封装输出函数
通过调研,我们发现规范的接口输出协议具有统一的格式。我们可以封装一个接口输出函数,来统一输出结果。
我们可以借助Eggjs的框架扩展能力来实现这个函数。
在app/extend/response.js文件中,创建两个方法。
'use strict';
module.exports = {
/**
* @description
* @param {*} { data, status }
*/
success({ data, status }) {
const { ctx } = this;
ctx.body = {
code: '0',
message: 'success',
result: data || null,
sysTime: ctx.helper.now(),
};
ctx.status = status || 200;
},
/**
* @description
* @param {*} { status, code, message, data }
*/
failure({ status, code, message, data }) {
const { ctx } = this;
ctx.body = {
code: code || '-1',
message: message || 'no message',
result: data || null,
sysTime: ctx.helper.now(),
};
ctx.status = status || 200;
},
};
success和failure功能基本一致,前者用于正常响应,code统一为0,message统一为success;后者用于错误响应,code和message需要自行传入。为了前端方便处理,状态码建议全部是使用200(即成功响应)。
现在,我们就可以在控制器中使用这两个函数了。
async index() {
const { ctx, config } = this;
ctx.response.failure(config.ERR_TYPE.NOT_EXIST_ROUTE);
}
async test() {
const { ctx } = this;
ctx.response.success({ data: ['test', 'test2'] });
}
输出结果如下图:
细心的同学可能还发现了一个小细节,输出系统时间时,使用了一个自行定义的函数now()。
这同样是通过框架扩展能力实现的,扩展了helper类。这样就不需要每次去copy代码。
合理的封装可以大大提升开发效率,优化代码结构。
统一管理错误类型
我们已经实现了统一输出协议,是不是这样就够了呢?答案是否定的。设想一下,当系统发展到一定阶段,出现多模块,多人协作的情况时,如果每个人都维护自己的错误响应码(code),就有可能出现code重复使用,不同类型的code无法区分的问题。
为了解决这个问题,就需要在设计之初,对code码进行统一管理。
在Eggjs中,一切配置相关的数据都放在config文件夹。
我新建了一个error.js文件,用来专门存放错误配置。这里,通过不同的前缀来区分不同类型的错误。
// config/error.js
module.exports = {
// 系统错误, -10000级别
UNKNOWN_ERROR: { code: '-10000', message: 'Internal server error.' }, // 未知错误
NOT_EXIST_ROUTE: { code: '-10001', message: 'The route does not exist' }, // 路由不存在
// 上游系统错误 -20000级别
Up_SYS_TIME_OUT: { code: '-20001', message: 'Upstream system timeout.' }, // 上游系统超时
// 业务错误,-30000级别
NOT_LOGIN: { code: '-30001', message: 'Not login.' }, // 未登录
};
随后,将这些配置注入到系统配置中去。
// config/config.default.js
const ERR_TYPE = require('./error');
const userConfig = {
ERR_TYPE,
};
需要使用的时候,可以从app.config.ERR_TYPE中获取这些配置,上文已演示这一点。