(一)egg-基础功能

(一)egg-基础功能

    • 快速上手
    • 目录结构
    • 内置对象
      • (1)Application
      • (2)Context
      • (3)response & request
      • (4)Controller
      • (5)service & helper & Config & logger
    • 中间件
      • 中间件的编写
      • 中间件的使用
      • 通用配置
    • 路由
      • 参数获取
    • 控制器
    • 服务

快速上手

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/**` 用于放置领域模型

内置对象

(1)Application

全局应用对象,可以将一些全局的方法和对象挂载到上面,

事件
application包含的事件有

  • server 在http服务完成启动之后,将HTTP server通过该事件暴露出来。
  • error 异常事件
  • request 收到请求触发
  • response 响应请求触发

一般需要在应用启动期间进行一些初始化工作,初始化完成之后才可对外提供服务,egg框架提供了统一的入口文件-app.js进行启动过程的自定义,app.js返回一个boot类,可以在该类的声明周期方法中执行启动应用的初始化工作。

包含的声明周期方法有:

生命周期方法 说明
configDidLoad 配置文件加载完成
configWillLoad 配置文件即将加载,修改配置文件的最终时机
didLoad 文件加载完成
willReady 插件启动完毕
didReady worker准备就绪
serverDidReady 应用启动完成
beforeClose 应用即将关闭

Application对象的获取方式

  • 作为参数
  • 在context对象上,通过ctx.app
  • 在继承于controller和service基类的实例中,可以通过this.app访问到

(2)Context

context是请求级别的对象,收到的用户请求会实例化一个context对象,该对象封装了用户的请求信息,框架将所有的Service挂载在context实例上。

获取方式

  • 在controller中获取 this.ctx
  • 在sevice中获取 this.ctx
  • middleware中,this或ctx
  • 在非用户请求情况下需要访问service/model等context实例上的对象
// app.js
module.exports = app => {
  app.beforeStart(async () => {
    const ctx = app.createAnonymousContext();
    // preload before app start
    await ctx.service.posts.load();
  });
}
  • 定时任务,使用ctx作为参数

(3)response & request

获取方式

  • ctx.request
  • ctx.response
    获取post的body内容应该使用ctx.request.body

(4)Controller

egg框架提供了controller基类,并推荐所有的controller均继承该基类实现。
其包含如下属性:

  • ctx :当前请求的context实例
  • app:application实例
  • config:应用的配置
  • service:应用的所有service
  • logger:日志对象

(5)service & helper & Config & logger

  • 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)配置

一个中间件接收两个参数:

  • options:中间件的配置项(如上代码)
  • app:当前Application的实例

中间件的使用

(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);
};

框架和插件加载的中间件会在应用层配置的中间件之前,框架默的中间件不可被应用层中间件覆盖,若有同名会报错。

通用配置

  • enable:控制中间件是否开启。
module.exports = {
  bodyParser: {
    enable: false,
  },
};
  • match:设置只有符合某些规则的请求才会经过这个中间件。
module.exports = {
  gzip: {
    match: '/static',
  },
};
  • ignore:设置符合某些规则的请求不经过这个中间件。

路由

用来描述请求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);
  • verb: HTTP方法
  • router-name:路由别名
  • path-match - 路由 URL 路径。
  • middleware1 - 在 Router 里面可以配置多个 Middleware。
  • controller - 指定路由映射到的具体的 controller 上,controller 可以有两种写法:
    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上

  • this.ctx:请求的上下文对象的实例,可以通过其拿到框架处理好的各种属性和方法。
  • this.app:获取全局对象和方法
  • this.service:访问业务层,等价this.ctx.service
  • this.config:对应配置项
  • this.logger:日志

服务

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
  • this.app
  • this.sevice
  • this.config
  • this.logger

其中this.ctx包含以下属性和方法

  • this.ctx.curl:发起网络调用
  • this.ctx.service.otherService:调用其他service
  • this.ctx.db:发起数据库调用。

你可能感兴趣的:(nodejs)