学习egg

Egg学习

基础功能

内置对象
  • Applicant。它是一个全局应用对象,一个应用中只有一个实例。他有四个事件:

    • server:http服务启动后会触发。
    • error:发生异常的时候会触发。可以在这里进行日志记录等处理。
    • request和response:在应用收到请求或者响应请求的时候会分别触发request和response。

    我们可以在Context对象上获取它:ctx.app;或者在继承于 Controller, Service 基类的实例中,可以通过 this.app 访问到 Application 对象。

  • Context。每次收到用户的请求的时候都会创建一个实例。实例上有很多实用的方法。在继承于 Controller, Service 基类的实例中可以通过this.ctx获取,在Middleware中可以通过以下方式获取:

    // Koa v1
    function* middleware(next) {
      // this is instance of Context
      console.log(this.query);
      yield next;
    }
    
    // Koa v2
    async function middleware(ctx, next) {
      // ctx is instance of Context
      console.log(ctx.query);
    }
    

    其他情况下获取是否常用?例如在service/model、定时任务中

  • Request和Response。这两者是继承了KOA的Request和Response。可在Context对象上获取。

  • Control。官方推荐所有Control都继承于框架提供的基类。基类含有ctx、app、config、service、logger属性。引入基类的方法如下:

    // 从 egg 上获取(推荐)
    const Controller = require('egg').Controller;
    class UserController extends Controller {
      // implement
    }
    module.exports = UserController;
    
    // 从 app 实例上获取
    module.exports = app => {
      return class UserController extends app.Controller {
        // implement
      };
    };
    
  • Service。他和Control差不多,含有的属性也一致。引入基类的方法和Control一样,把上面代码中的"Control"换成"Service"即可。

  • Helper。Helper用于提供一些实用函数。它身上的属性和Control基类一样。每次请求时都会实例化。可通过ctx.helper获取。

    helper具体使用案例?只能在app/extend/helper.js中写自定义helper?

  • Config。顾名思义,就是写配置的地方。可以通过 app.config 从 Application 实例上获取,也可以在 Controller, Service, Helper 的实例上通过 this.config 获取到 config 对象。

  • Logger。该对象用于打印日志。每个logger都有四个方法:logger.debug(),logger.info(),logger.warn(),logger.error()。以下是各个logger:

    • App Logger:通过app.logger获取。可用于记录应用级别的日志,如记录启动阶段的一些数据信息,记录一些业务上与请求无关的信息。
    • App CoreLogger:通过app.coreLogger获取。框架和插件则需要通过它来打印应用级别的日志,这样可以更清晰的区分应用和框架打印的日志。
    • Context Logger:通过ctx.logger获取。用于记录和请求相关的日志。
    • Context CoreLogger:通过ctx.coreLogger获取。和 Context Logger 的区别是一般只有插件和框架会通过它来记录日志。
    • Controller Logger & Service logger:通过this.logger获取。它们本质上就是一个 Context Logger,不过在打印日志的时候还会额外的加上文件路径,方便定位日志的打印位置。
  • Subscription。一个规范的订阅模式基类。定时任务使用到了它。引入方法:

    const Subscription = require('egg').Subscription;
    
    class Schedule extends Subscription {
      // 需要实现此方法
      // subscribe 可以为 async function 或 generator function
      async subscribe() {}
    }
    
配置

egg中默认配置为 config.default.js 。指定env时会加载默认配置和对应的配置文件,并且env对应的配置文件会覆盖掉默认配置中的同名配置。下面是三种配置写法:

//使用对象方式
module.exports = {
  logger: {
    dir: '/home/admin/logs/demoapp'
  }
}

//使用`exports.key = value`形式
exports.logger = {
  level: 'DEBUG'
}

//使用function方式,function会接受一个参数,参数内置的属性可参考官方文档
module.exports = appInfo => {
	return {
    logger: {
			dir: '/home/admin/logs/demoapp'
    }
  }
}
中间件
编写中间件
/**
 * 中间件需要exports一个函数,函数接受两个参数
 * @param {*} options 中间件的配置项,框架会将 app.config[${middlewareName}] 传递进来。
 * @param {*} app 当前应用 Application 的实例。
 */

module.exports = (options, app) => {
  return async function test(ctx, next) {
    await next();
    console.log('It is a test middleware. ' + options.msg);
  };
};

使用中间件
//1.在 config.default.js 中加入配置
module.exports = {
  // 配置需要的中间件,数组顺序即为中间件的加载顺序
  middleware: [ 'test' ],

  // 配置 test 中间件的配置
  test: {
    msg: 'config msg', // 小于 1k 的响应体不压缩
  },
};

//2.在app.js中加入以下代码,配置放置在 config.default.js 中
app.config.coreMiddleware.unshift('test');

//3.在指定路由上添加,如在 app/router.js 中添加
module.exports = app => {
  const test = app.middleware.test({ test: 'config msg' });
  app.router.get('/test', test, app.controller.handler);
};

官网说“以上两种方式配置的中间件是全局的,会处理每一次请求。”,可是测试的打印次数不一致,怎么回事?

在应用中使用指的是我们自己书写的中间件,在框架和插件中使用指的是框架和插件本身的中间件,在router中使用指的是就是在特定的路由中使用。

通用配置

通用配置只能在config.default.js中进行设置,在其他地方设置并不会生效。一共有三个通用配置:enable,matchignorematchignore只能使用其中一个。

  • enable用于设置中间件是否开启。可以使用的值为true/false
  • match用于匹配,匹配到的对应的规则则开启中间件。值可以是字符串、正则和函数。
  • ignorematch一样,区别是ignore会在匹配成功时关闭中间件。
路由

路由用于描述url和controller的对应关系。路由会把请求中的url交给对应的controller进行处理。路由的定义方式有以下几种:

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请求的方法。"path-match"指的是匹配的路由,可以是正则和字符串。"router-name"指的是路由别名,似乎用的不多。middleware是给当前的路由添加的中间件,可以有多个。controller就是当前路由要对应的controller。以下是一些例子:

// app/router.js
module.exports = app => {
  const { router, controller } = app;
  router.get('/home', controller.home);
  router.get('/user/:id', controller.user.page);
  router.post('/admin', isAdmin, controller.admin);
  router.post('/user', isLoginUser, hasAdminPermission, controller.user.create);
  router.post('/api/v1/comments', controller.v1.comments.create); // app/controller/v1/comments.js
};
控制器

控制器功能是处理某个请求,并返回该请求对应的结果。比如某个请求需要根据请求中的参数返回对应的页面,那么控制器就是需要解析参数,找到和参数对应的页面并返回。

编写controller

我们一般使用class来编写。

const { Controller } = require('egg').Controller;
class DemoController extends Controller {
	async test(){
    //在实例中我们可以访问Applicant, Context, Service, Config, Logger对象
    const { ctx, app, service, config, logger } = this;
    ctx.body = `hello world!`;
  }
}
module.exports = DemoController;
获取http请求参数
  • query:通过ctx.query可获取url上参数,返回的是一个对象。若有key有多个,则获取第一个。

  • queries:通过ctx.queries可获取url上参数,返回的是一个对象,对象中每个key值对应的是一个数组。若有key有多个,则获取全部。

  • router params:通过ctx.params获取,返回的是一个对象。

  • body:egg内置了bodyparse中间件,可通过cox.request.body获取body。

  • 上传的文件:具体看官方文档。

  • header:通过ctx.headersctx.headerctx.request.headersctx.request.header这些都可以获取到。header中一些常用或者特殊的字段内置在了框架中的getter上,可通过ctx.(specific name)来获取,详细见官网。

  • cookie:通过ctx.cookies获取,通过ctx.cookies.set()来设置。cookie的samesite和httponly属性可在config.default.js中配置。

  • session:通过ctx.session获取,通过ctx.session[name]来设置,若要删除则置为null。session的key和maxAge属性可对config.default.js中的exports.session进行配置。

参数校验
//在 config.default.js 中开启
exports.validate = {
  enable: true,
  package: 'egg-validate',
};

//在具体的Controller中进行校验
//校验函数:ctx.validate(rule, [body])
class PostController extends Controller {
  async test() {
    // 如果不传第二个参数会自动校验 `ctx.request.body`
    this.ctx.validate({
      title: { type: 'string' },
    });
  }
}
响应请求
  • 设置status:ctx.status = value

  • 设置body:ctx.body = value

  • 设置header:ctx.set(key, value)

  • 重定向:ctx.redirect(url)cox.unsafeRedirect(url)。前者需要在config.default.js中配置白名单,例如:

exports.security = {
  domainWhiteList:['.domain.com'],  // 安全白名单,以 . 开头
};
服务

服务是供给Controller使用的,一般是向Controller提供数据服务或者第三方调用服务。以下是使用服务的例子:

//controller/test.js
async test() {
  const { ctx } = this;
  const data = await ctx.service.test.getTestData();
  ctx.body = JSON.parse(data);
}

//service/test.js
const { Service } = require('egg');
class MxTestService extends Service {
  async getTestData() {
    return JSON.stringify({
      test: 'it is a test data',
    });
  }
}
module.exports = MxTestService;
插件

插件的定义:插件用于管理相对独立的业务逻辑。与中间件的区别是:中间件的加载是有先后顺序的,插件会先于框架和应用之前加载。中间件用于拦截请求,插件与请求的关联并不大。

config/plugin.js中配置插件。每个插件的配置选项都有四个选项:

  • enable:布尔值,是否开启插件,默认true
  • package:字符串,npm的模块名称
  • path:字符串,插件的绝对路径
  • env:数组,在指定的环境才会开启

其中path和package是互斥的。内置插件可以通过exports.innerPlugin = boolean来进行开关。

在plugin.[env].js中配置插件,可在对应的环境下加载插件。项目中plugin.local.js中配置的插件在生产环境下不会下载下来。

插件自身的默认配置可在config.default.js中进行配置。如:exports.pluginName={}

定时器

定时器用于定时执行一些任务。例如定时清除日志文件,临时文件,定时更新缓存等。

一个简单的定时器如下:

const { Subscription } = require('egg');

class MxTest extends Subscription {
  static get schedule() {
    return {
      interval: '2s',
      type: 'worker',
    };
  }

  async subscribe() {
    // console.log(`MxTest ${new Date()}`);
  }
}

module.exports = MxTest;

定时方式有intervalcron两种。

  • interval可以是数字,单位为毫秒,也可以是字符串,需要带上时间单位。定时任务将每隔指定的时间执行一次
  • `cron是字符串,格式为"* * * * * *",具体定义见官方文档。它将在指定的时间执行一次,如每三小时执行一次:”0 0 */3 * * *“

类型字段有待理解,"worker"是什么?

框架扩展

在extend文件夹下编写对应的文件,则可以对对应的进行扩展。可扩展的对象和对应的文件命名:

  • Application: application.js
  • Context: context.js
  • Request: request.js
  • Response: response.js
  • Helper: helper.js
启动自定义

框架生命周期函数主要有以下几个:

  • configWillLoad:配置文件即将加载前
  • configDidLoad:配置文件加载完成后
  • didLoad:文件加载完成后
  • willReady:插件启动完毕后
  • didReady:worker 准备就绪后
  • serverDidReady:应用启动完成后
  • beforeClose:应用即将关闭前

注意不要在生命周期函数中做些耗时任务。

核心功能

日志
日志分类
  • appLogger,记录在[appName]-web.log
  • coreLogger,记录在egg-web.log
  • errorLogger,记录在common-error.log
  • agentLogger,记录在egg-agent.log
日志打印

一共有这么几种logger可以使用:ctx.logger, ctx.coreLogger, app.logger, app.coreLogger, agent.logger, agent.corelogger。他们有四种打印方法:debug, info, warn, error。

日志级别

日志分为 NONEDEBUGINFOWARNERROR 5 个级别。对于文件日志,我们可以在config.[env].js文件中配置logger.level为刚才描述的级别,即可控制打印特定级别的日志。默认输出INFO级别以上的日志。生产环境打印debug级别日志需要配置logger.allowDebugAtProd为true。对于终端日志,我们可以在config.[env].js文件中配置logger.consoleLevel为刚才描述的级别,即可控制打印特定级别的日志。默认输出INFO级别以上的日志。

日志切割

默认是按照每天进行切割的,也可以选择按照文件大小和按照每小时进行切割。后两者需要进行配置,详细见文档。

Cookie and Session
Cookie

获取cookie:ctx.cookies。获取cookie某个值:ctx.cookies.get(key [,options])。设置cookie:ctx.cookies.set(key, value [,options])。options可以有signed和encrypt两个键,值类型为boolean。在get中使用则分别表示是否验签和是否解密。在set中使用则分别表示是否加签和是否加密。我们可以在config/config.default.js中设置exports.key来设置一个的私钥,供我们对cookie加解密和验签。

Session

访问session:ctx.session[key]。设置session:ctx.session[key]=value。删除session:ctx.session=null。session可在config.default.js中的exports.session进行配置。它可以配置一个属性"key",来表示存储session的cookie的键是什么。在这里配置的maxAge是对全局的session配置的,单个session可以通过ctx.session.maxAge=xxx进行配置。

HttpClient
使用HttpClient

app.curl(url, options) , ctx.curl(url, options) 。HttpClient在框架初始化时会挂载到app.httpclient上。app.httpclient.request(url, options)可以通过 app.curl(url, options) 来使用。curl方法也同样被挂载到了ctx对象上,我们也可以通过ctx.curl(url, options) 进行使用。options参数配置详见文档。

表单提交
  • Form表单不需要上传文件时,通常都要求以 content-type: application/x-www-form-urlencoded 的格式提交请求数据。HttpClient 会默认以 application/x-www-form-urlencoded 格式发送请求。
  • Form表单需要上传文件时,请求数据格式就必须以 multipart/form-data 进行提交了。这时候我们需要使用到formstream来处理表单。formstream的使用具体见该模块的文档。最后我们要在options字段中设置stream: (你创建的formstream)。项目中使用的是这种方式上传pdf。
  • Form表单需要上传文件时,我们还可以使用steam的方式。我们要使用到fs模块创建一个文件流,最后我们要在options字段中设置stream: (你创建的文件流)
全局 requestresponse 事件

httpclient发出请求或接收到响应后,会分别触发request事件和response事件。我们可以在此对请求或者响应做拦截处理。使用方法如下:

app.httpclient.on('request', req => {
  req.url //请求 url
  req.ctx //是发起这次请求的当前上下文

  // 可以在这里设置一些 trace headers,方便全链路跟踪
});
app.httpclient.on('response', result => {
  result.res.status
  result.ctx //是发起这次请求的当前上下文
  result.req //对应的 req 对象,即 request 事件里面那个 req
});
模板渲染

模板渲染需要使用到插件,官方推荐使用egg-view-nunjucks。这里也以其为例。启动该插件直接在config/plugins.js中启动即可。

其配置可见官方文档。

Context上会挂载三个渲染的方法:

  • render(name, locals) 渲染模板文件, 并赋值给 ctx.body
  • renderView(name, locals) 渲染模板文件, 仅返回不赋值
  • renderString(tpl, locals) 渲染模板字符串, 仅返回不赋值

locals就是我们需要在模板中使用到的数据。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值