Web开发通常伴随着很多配套基础设置,比如数据库,REST和Socket.IO等
MySQL
框架提供了 egg-mysql 插件来访问 MySQL 数据库。这个插件既可以访问普通的 MySQL 数据库,也可以访问基于 MySQL 协议的在线数据库服务。
由于对 MySQL 数据库的访问操作属于 Web 层中的数据处理层,因此我们强烈建议将这部分代码放在 Service 层中维护。
直接提供CRUD函数
- Create:可以直接使用 insert 方法插入一条记录。
- Read:可以直接使用 get 方法或 select 方法获取一条或多条记录。select 方法支持条件查询与结果的定制。
- Update:可以直接使用 update 方法更新数据库记录。
- Delete:可以直接使用 delete 方法删除数据库记录。
插件本身也支持拼接与直接执行 sql 语句。使用 query 可以执行合法的 sql 语句。
注意!!我们极其不建议开发者拼接 sql 语句,这样很容易引起 sql 注入!!
如果必须要自己拼接 sql 语句,请使用 mysql.escape 方法。
MySQL事务处理
事务是必须满足4个条件(ACID): Atomicity(原子性)、Consistency(一致性)、Isolation(隔离性)、Durability(可靠性)
对于一个事务来讲,一定伴随着 beginTransaction、commit 或 rollback,分别代表事务的开始,成功和失败回滚。
egg-mysql 提供了两种类型的事务。
- 手动控制
- 优点:beginTransaction, commit 或 rollback 都由开发者来完全控制,可以做到非常细粒度的控制。
- 缺点:手写代码比较多,不是每个人都能写好。忘记了捕获异常和 cleanup 都会导致严重 bug。
- 自动控制
- 优点:使用简单,不容易犯错,就感觉事务不存在的样子。
- 缺点:整个事务要么成功,要么失败,无法做细粒度控制。
如果需要调用 MySQL 内置的函数(或表达式),可以使用 Literal。
ORM
和目前成熟的Java Web一样,对于较为复杂的应用,我们可能会需要一个 ORM 框架来帮助我们管理数据层的代码。而在 Node.js 社区中,sequelize 是一个广泛使用的 ORM 框架,它支持 MySQL、PostgreSQL、SQLite 和 MSSQL 等多个数据源。
安装egg-sequelize插件和mysql2模块
npm install --save egg-sequelize mysql2
我们可以使用mysql命令直接修改和创建表。但是这并不是一个对多人协作非常友好的开发模式。在项目的演进过程中,每一个迭代都有可能对数据库数据结构做变更,怎样跟踪每一个迭代的数据变更,并在不同的环境(开发、测试、CI)和迭代切换中,快速变更数据结构呢?这时候我们就需要 Migrations 来帮我们管理数据结构的变更了。
sequelize 提供了 sequelize-cli 工具来实现 Migrations,我们也可以在 egg 项目中引入 sequelize-cli。
npm install --save-dev sequelize-cli
关于更多,文档其实十分详细了,到时候要用直接去看吧
有数据库访问的单元测试直接写起来会特别繁琐,特别是很多接口我们需要创建一系列的数据才能进行,造测试数据是一个非常繁琐的过程。为了简化单测,我们可以通过 factory-girl 模块来快速创建测试数据。
npm install --save-dev factory-girl
REST
REST强调的是如何规范API。
在 RESTful 风格的设计中,我们会通过响应状态码来标识响应的状态,保持响应的 body 简洁,只返回接口数据。
这里主要学习一下错误处理
在接口处理发生错误的时候,如果是客户端请求参数导致的错误,我们会返回 4xx 状态码,如果是服务端自身的处理逻辑错误,我们会返回 5xx 状态码。所有的异常对象都是对这个异常状态的描述,其中 error 字段是错误的描述,detail 字段(可选)是导致错误的详细原因。
在编码中,Controller 和 Service 都有可能抛出异常,这也是我们推荐的编码方式,当发现客户端参数传递错误或者调用后端服务异常时,通过抛出异常的方式来进行中断。通过中间件来同意错误处理。
module.exports = () => {
return async function errorHandler(ctx, next) {
try {
await next();
} catch (err) {
// 所有的异常都在 app 上触发一个 error 事件,框架会记录一条错误日志
ctx.app.emit('error', err, ctx);
const status = err.status || 500;
// 生产环境时 500 错误的详细错误内容不返回给客户端,因为可能包含敏感信息
const error = status === 500 && ctx.app.config.env === 'prod'
? 'Internal Server Error'
: err.message;
// 从 error 对象上读出各个属性,设置到响应中
ctx.body = { error };
if (status === 422) {
ctx.body.detail = err.errors;
}
ctx.status = status;
}
};
};
module.exports = {
// 加载 errorHandler 中间件
middleware: [ 'errorHandler' ],
// 只对 /api 前缀的 url 路径生效
errorHandler: {
match: '/api',
},
};
Socket.IO
Socket.IO 是一个基于 Node.js 的实时应用程序框架,在即时通讯、通知与消息推送,实时分析等场景中有较为广泛的应用。
WebSocket 的产生源于 Web 开发中日益增长的实时通信需求,对比基于 http 的轮询方式,它大大节省了网络带宽,同时也降低了服务器的性能消耗; socket.io 支持 websocket、polling 两种数据传输方式以兼容浏览器不支持 WebSocket 场景下的通信需求。
框架提供了 egg-socket.io 插件,并增加了以下开发规约
更多
关于Loader的了解,插件和框架的开发,文档都有相关的介绍,日后有需求再深入实践。
值得注意的是,在“如何贡献”部分,我们可以感受egg开发如何高效管理issue标签、文档、Commit 提交规范以及发布管理的。