npm install egg --type = simple
可选的骨架类型
simple - 简单的egg应用程序骨架
empty - 空的egg应用程序骨架
plugin - egg plugin骨架
Framework - egg framework骨架
`app/router.js` 用于配置 URL 路由规则
`app/controller/**` 用于解析用户的输入,处理后返回相应的结果
`app/service/**` 用于编写业务逻辑层
`app/middleware/**` 用于编写中间件
`app/public/**` 用于放置静态资源
`app/extend/**` 用于框架的扩展
`config/config.{env}.js` 用于编写配置文件
`config/plugin.js` 用于配置需要加载的插件
`test/**` 用于单元测试
`app.js 和 agent.js` 用于自定义启动时的初始化工作
`app/schedule/**` 用于定时任务
`app/view/**` 用于放置模板文件
`app/model/**` 用于放置领域模型
全局应用对象,可以将一些全局的方法和对象挂载到上面,
事件
application包含的事件有
一般需要在应用启动期间进行一些初始化工作,初始化完成之后才可对外提供服务,egg框架提供了统一的入口文件-app.js进行启动过程的自定义,app.js返回一个boot类,可以在该类的声明周期方法中执行启动应用的初始化工作。
包含的声明周期方法有:
生命周期方法 | 说明 |
---|---|
configDidLoad | 配置文件加载完成 |
configWillLoad | 配置文件即将加载,修改配置文件的最终时机 |
didLoad | 文件加载完成 |
willReady | 插件启动完毕 |
didReady | worker准备就绪 |
serverDidReady | 应用启动完成 |
beforeClose | 应用即将关闭 |
Application对象的获取方式
context是请求级别的对象,收到的用户请求会实例化一个context对象,该对象封装了用户的请求信息,框架将所有的Service挂载在context实例上。
获取方式
// app.js
module.exports = app => {
app.beforeStart(async () => {
const ctx = app.createAnonymousContext();
// preload before app start
await ctx.service.posts.load();
});
}
获取方式
egg框架提供了controller基类,并推荐所有的controller均继承该基类实现。
其包含如下属性:
sevice:内置service基类,并推荐所有service继承此基类 属性同controller
helper:提供一些实用的utility函数,可以通过ctx.helper获取请求的helper实例
可以通过框架扩展的形式自定义helper的方法。
config:通过app.config获取到config对象,也可以在controller sevice helper上通过this.config获取
logger: 包含以下几种方法
logger.debug(); logger.info(); logger.warn(); logger.error();
(1)写法
// app/middleware/gzip.js
const isJSON = require('koa-is-json');
const zlib = require('zlib');
module.exports = options => {
return async function gzip(ctx, next) {
await next();
// 后续中间件执行完成后将响应体转换成 gzip
let body = ctx.body;
if (!body) return;
// 支持 options.threshold
if (options.threshold && ctx.length < options.threshold) return;
if (isJSON(body)) body = JSON.stringify(body);
// 设置 gzip body,修正响应头
const stream = zlib.createGzip();
stream.end(body);
ctx.body = stream;
ctx.set('Content-Encoding', 'gzip');
};
};
(2)配置
一个中间件接收两个参数:
(1)在应用中使用
在config.default.js中配置,最终在启动时合并到app.config.appMiddleware
module.exports = {
// 配置需要的中间件,数组顺序即为中间件的加载顺序
middleware: [ 'gzip' ],
// 配置 gzip 中间件的配置
gzip: {
threshold: 1024, // 小于 1k 的响应体不压缩
},
};
(2)在框架和插件中使用
不支持在config.default.js中配置
// app.js
module.exports = app => {
// 在中间件最前面统计请求时间
app.config.coreMiddleware.unshift('report');
};
// app/middleware/report.js
module.exports = () => {
return async function (ctx, next) {
const startTime = Date.now();
await next();
// 上报请求时间
reportTime(Date.now() - startTime);
}
};
(3)在router中使用中间件
上述两种方式配置的中间件是全局的,会处理每一次请求,若指向针对单个路由生效,可以直接在app/router.js中实例化和挂载。
module.exports = app => {
const gzip = app.middleware.gzip({ threshold: 1024 });
app.router.get('/needgzip', gzip, app.controller.handler);
};
框架和插件加载的中间件会在应用层配置的中间件之前,框架默的中间件不可被应用层中间件覆盖,若有同名会报错。
module.exports = {
bodyParser: {
enable: false,
},
};
module.exports = {
gzip: {
match: '/static',
},
};
用来描述请求url和具体承担执行动作的Controller的对应关系,app/router.js用于统一所有路由规则。
router.verb('path-match', app.controller.action);
router.verb('router-name', 'path-match', app.controller.action);
router.verb('path-match', middleware1, ..., middlewareN, app.controller.action);
router.verb('router-name', 'path-match', middleware1, ..., middlewareN, app.controller.action);
app.controller.user.fetch
- 直接指定一个具体的 controller'user.fetch'
- 可以简写为字符串形式(1)query
// app/router.js
module.exports = app => {
app.router.get('/search', app.controller.search.index);
};
// app/controller/search.js
exports.index = async ctx => {
ctx.body = `search: ${ctx.query.name}`;
};
// curl http://127.0.0.1:7001/search?name=egg
(2)params
// app/router.js
module.exports = app => {
app.router.get('/user/:id/:name', app.controller.user.info);
};
// app/controller/user.js
exports.info = async ctx => {
ctx.body = `user: ${ctx.params.id}, ${ctx.params.name}`;
};
// curl http://127.0.0.1:7001/user/123/xiaoming
(3)复杂参数
// app/router.js
module.exports = app => {
app.router.get(/^\/package\/([\w-.]+\/[\w-.]+)$/, app.controller.package.detail);
};
// app/controller/package.js
exports.detail = async ctx => {
// 如果请求 URL 被正则匹配, 可以按照捕获分组的顺序,从 ctx.params 中获取。
// 按照下面的用户请求,`ctx.params[0]` 的 内容就是 `egg/1.0.0`
ctx.body = `package:${ctx.params[0]}`;
};
// curl http://127.0.0.1:7001/package/egg/1.0.0
(4)表单
// app/router.js
module.exports = app => {
app.router.post('/form', app.controller.form.post);
};
// app/controller/form.js
exports.post = async ctx => {
ctx.body = `body: ${JSON.stringify(ctx.request.body)}`;
};
表单校验
// app/router.js
module.exports = app => {
app.router.post('/user', app.controller.user);
};
// app/controller/user.js
const createRule = {
username: {
type: 'email',
},
password: {
type: 'password',
compare: 're-password',
},
};
exports.create = async ctx => {
// 如果校验报错,会抛出异常
ctx.validate(createRule);
ctx.body = ctx.request.body;
};
controller主要对用户的请求参数进行处理(校验和转换),然后调用对应的service方法处理业务,得到业务结果后封装并返回。
项目中的controller类继承于egg.controller,有以下属性挂在this上
service可以进行复杂的数据处理,比如数据库信息获取后的规则计算,第三方服务信息的调用等。
// app/service/user.js
const Service = require('egg').Service;
class UserService extends Service {
async find(uid) {
const user = await this.ctx.db.query('select * from user where uid = ?', uid);
return user;
}
}
module.exports = UserService;
sevice层提供的属性
其中this.ctx包含以下属性和方法