Egg 是阿里巴巴基于 Koa 的有约束和规范的企业级 Web 开发框架,基于 Egg 的项目目录结构和名称有严格的规定,和 ESLint 一样,如果不符合规定那么项目将无法运行,此外,Egg 基于 MVC 的架构模式,M —— Model 层负责应用程序的数据逻辑部分,类似于 Service、V —— View 层负责应用程序的数据显示部分(静态/动态网页),类似于 Router、C —— Controller 层负责应用程序的业务逻辑部分,将数据和页面关联
在使用 EggJS 之前,必须先下载 egg 和 egg-bin 模块,前者是 EggJS 的核心模块,后者是用于本地开发调试的模块,示例如下:
npm install egg --save
npm install egg-bin --save-dev
在项目下载完成之后,可以在 package.json 文件中的 script 属性中添加 dev 命令以便于快速启动项目
在基于 EggJS 项目中,目录结构有严格的限制,以下是 Egg 项目中最简单的结构
在项目中,目录 app 专门用于保存项目的核心代码,子目录 controller 用于保存程序业务逻辑相关的代码,home.js 专门用于编写处理主页的业务逻辑代码,router.js 专门用于编写路由相关的代码,目录 config 专门用于保存项目的配置文件,config.default.js 用于保存项目的默认配置,此外,必须注意的是,除了 controller 目录下的文件之外,上述目录和文件的名称不能擅自修改,否则会报错,每个文件的内容如下所示:
module.exports = {keys: 'org.xin.*?'
}
此处的 keys 即项目用于生成 Cookie 时所用的密钥
const Controller = require('egg').Controller;class HomeController extends Controller {async index() {this.ctx.body = 'Hello World!';}
}module.exports = HomeController;
上述示例中,定义了一个继承自 Controller 的 HomeController 类,此类的所有实例方法即用于处理主页相关的业务逻辑,这些方法必须被标记为 async 异步方法,此外,凡是继承 Controller 类的类中的 this 拥有如下属性:
| 属性 | 含义 |
|---|---|
| this.ctx | 当前请求的 Context 实例,类似于 Koa 中的 ctx |
| this.app | 当前应用的 Application 实例,利用此实例可以获取框架提供的全局实例和方法 |
| this.service | 应用定义的 Service 实例,利用此实例可以访问应用中 Service 层的方法 |
| this.config | 应用运行时和配置相关的选项 |
| this.logger | 应用提供的 logger 实例,用于记录日志,之后再详细说明 |
module.exports = app => {// 从 app 中解构出 router 和 controllerconst {router, controller} = app;/*** 利用 router 的 get 方法处理根路由(类似于 Koa2 中的 router),在此方法中* 第一个参数说明路由* 第二个参数说明处理此路由的方法,此方法来自 controller 目录下的 home.js 所暴露的类中的 index 实例方法* (即 controller 中保存了 controller 目录下的所有内容,访问 controller 就相当于访问 controller 目录)*/router.get('/', controller.home.index);
}
上述示例中,向外界暴露了一个方法,此方法接收一个名称为 app 的参数,此参数即为当前应用的 Application 实例,此实例中拥有如下属性:
| 属性 | 含义 |
|---|---|
| env | 运行环境 |
| name | 项目名称 |
| baseDir | 项目目录 |
| controller | 保存了项目中 controller 目录下的所有内容 |
| loggers | 用于日志记录 |
| middlewares | 保存了项目中的所有中间件 |
| router | 用于路由 |
EggJS 项目中获取请求参数的方式和 Koa 基本相同,示例如下:
const Controller = require('egg').Controller;class UserController extends Controller {async getQuery() {this.ctx.body = this.ctx.query;}async getParams() {this.ctx.body = this.ctx.params;}async getBody() {this.ctx.body = this.ctx.request.body;}
}module.exports = UserController;
module.exports = app => {// 从 app 中解构出 router 和 controllerconst {router, controller} = app;// 处理静态 get 请求router.get('/user/info', controller.user.getQuery);// 处理动态 get 请求router.get('/user/register/:username/:password', controller.user.getParams);// 处理 post 请求router.post('/user/login', controller.user.getBody);
}
默认情况下,上述示例中的 post 请求将失败,原因在于 EggJS 拥有一套安全机制,默认的 post 请求被认为是不安全的,所以必须在 config.default.js 中将此机制忽略,示例如下:
module.exports = {keys: 'org.xin.*?',security: {csrf: {ignoreJSON: true // 默认为 false,当设置为 true 时,将会放过所有 content-type 为 `application/json` 的请求}}
}
在基于 EggJS 的项目中,不用专门处理静态资源,因为 EggJS 已经处理好了,只需要将所有的静态资源存放在 app 目录下的 public 子目录下即可,此处不再详细说明,必须注意的是,目录名称必须为 public,且必须在 app 目录下,在通过浏览器访问静态资源时,必须加上 /public,否则将出现 404 错误,示例如下:
http://127.0.0.1:7001/public/login.html
在基于 EggJS 的项目中,通过插件处理动态资源,在 EggJS 中,插件就是特殊的中间件,专门用于处理那些和请求无关的独立的业务逻辑,要想使用插件处理动态资源,必须先下载此插件,示例如下:
npm install egg-view-ejs --save
在下载完成之后,在 config 目录下新建一个 plugin.js 文件,用以描述插件相关的配置,示例如下:
module.exports = {ejs: {enable: true,package: 'egg-view-ejs'}
}
之后在 config.default.js 中新增如下配置:
module.exports = {view: {mapping: {'.html': 'ejs'}}
}
之后,必须在 app 目录下创建 view 子目录以存放动态资源,资源以 html 为扩展名,然后就可以在 controller 中利用 ctx 的 render 方法渲染动态资源,示例如下:
const Controller = require('egg').Controller;class HomeController extends Controller {async index() {await this.ctx.render('index', {username: 'Reyn Morales',password: 1024})}
}module.exports = HomeController;
在基于 EggJS 的项目中,不论是数据库中的数据还是临时请求网络中的数据,均在 Service 中处理,所以我们必须在 app 目录下新建一个 service 子目录(目录名称不能擅自修改),此目录专门用于保存数据逻辑相关的代码,此处说明如何临时请求网络上的数据,关于数据库相关的内容将在说明如何在 EggJS 中使用 MySQL 和 Sequelize 时说明,示例如下:
const Service = require('egg').Service;class HomeService extends Service {async getNews() {const response = await this.ctx.curl('http://127.0.0.1:3000/getNewsByGetWithoutParams');return JSON.parse(response.data);}
}module.exports = HomeService;
和 Controller 类似,凡是继承 Service 类的类中的 this 也将拥有和 Controller 类中 this 所拥有的属性,此外还包含一些额外的属性,内容如下:
| 属性 | 含义 |
|---|---|
| this.ctx.curl | 发起网络调用 |
| this.ctx.service.otherService | 调用其它 Service |
| this.ctx.db | 发起数据库调用,db 可能是其它插件提前挂载到 app 上的模块 |
实际上 curl 方法可以发起多种网络调用,示例如下:
const Service = require('egg').Service;class HomeService extends Service {async getNews() {/* 不带参数的 get 请求 */// const response = await this.ctx.curl('http://127.0.0.1:3000/getNewsByGetWithoutParams');/* 带参数的 get 请求 */// const response = await this.ctx.curl('http://127.0.0.1:3000/getNewsByGetWithParams?title=SXU');/* 不带参数的 post 请求 */// const response = await this.ctx.curl('http://127.0.0.1:3000/getNewsByPostWithoutParams', {// method: 'post'// });/* 带参数的 post 请求 */const response = await this.ctx.curl('http://127.0.0.1:3000/getNewsByPostWithParams', {method: 'post',data: {name: 'Reyn Morales',age: 21}});return JSON.parse(response.data);}
}module.exports = HomeService;
const Controller = require('egg').Controller;class HomeController extends Controller {async findNews() {this.ctx.body = await this.service.home.getNews();}
}module.exports = HomeController;
类似于 controller,如果通过 this 访问 service 属性,那么就相当于访问 app 目录下的 service 子目录,通过不同的模块名称可以调用不同模块提供的方法,此外,必须注意的是,service 中允许以多级目录的形式存储模块,如果模块是保存在多级目录下的,那么在调用时必须采用链式调用,示例如下:
/* 此处访问 /service/abc/def/temp 模块中的 xxx 方法 */
this.service.abc.def.temp.xxx();
不仅如此,如果 service 中的模块名称中的单词以
_连接或采用首字母大写的驼峰命名方式,那么在调用时必须采用驼峰命名,例如,在调用名称为 get_user 或 GetUser 模块的方法时,必须统一写为 getUser
module.exports = app => {// 从 app 中解构出 router 和 controllerconst {router, controller} = app;// 处理网络数据router.get('/news', controller.home.findNews);
}
在基于 EggJS 的项目中,Cookie 通常在 controller 中添加、获取
示例如下:
const Controller = require('egg').Controller;class HomeController extends Controller {async setCookie() {this.ctx.cookies.set('name', 'reyn', {path: '/',maxAge: 24 * 60 * 60 * 1000,httpOnly: true,signed: true, // 生成签名encrypt: true // 加密存储});this.ctx.body = 'Set Cookie!';}
}module.exports = HomeController;
在基于 EggJS 的项目中,为了安全着想,阿里的安全专家建议我们在添加 Cookie 时,为保存的数据生成一个签名,将来在获取数据时,再利用获取到的数据生成签名并和当初保存的签名进行对比,如果相同,那么表示保存在客户端的数据没有被篡改,否则说明保存在客户端的数据已经被篡改
示例如下:
const Controller = require('egg').Controller;class HomeController extends Controller {async getCookie() {const cookie = this.ctx.cookies.get('name', {signed: true, // 检查签名encrypt: true // 解密读取});this.ctx.body = `Get Cookie -> ${cookie}`;}
}module.exports = HomeController;
在基于 EggJS 中的项目中,继承自 Controller 和 Service 的类的 this 中有 logger 属性实例专门用于日志记录,示例如下:
const Controller = require('egg').Controller;class HomeController extends Controller {async loggerTest() {this.logger.debug('Logger -> Debug');this.logger.info('Logger -> Info');this.logger.warn('Logger -> Warn');this.logger.error('Logger -> Error');this.ctx.body = 'Logger Success';}
}module.exports = HomeController;
在项目运行时,系统将在项目目录中自动创建 logs 和 run 目录,logs 专门用于存储日志,类型如下:
基于 EggJS 的项目中有 5 个级别的日志,分别为 None、Debug、Info、Warn、Error,默认情况下仅输出 Info 及以上级别的日志到文件中,如果想要输出 Debug 日志,那么必须修改 config.default.js 配置文件,示例如下:
module.exports = {logger: {level: 'DEBUG'}
}
此外,在 EggJS 中不用我们手动切割日志,默认情况下 EggJS 将自动帮我们切割日志,默认情况下每一天就是一个新的日志文件,之前的日志文件将以其所记录日志的日期为扩展名,例如:common-error.log.2022-12-12
即使我们通过框架开发的 Web 服务器是请求响应模型,但是在某些情况下,我们必须在某个特定的时刻或时间段执行任务,例如定时删除临时文件、上报应用状态等,EggJS 提供了一套机制来让定时任务的编写和维护更加优雅,首先我们要在 app 目录下新建一个名称为 schedule 的子目录,在子目录下可以以模块的形式创建定时任务,示例如下:
const Subscription = require('egg').Subscription;class UpdateCache extends Subscription {static get schedule() {return {interval: '3s', // 定时任务执行的时间间隔/*** 如果服务器的 CPU 是多核的,那么可以同时执行若干个程序* 此处的 all 即表示当前服务器上所有相同的 Node 进程都执行此任务* 由于用户每次访问服务器可能由不同的 Node 进程处理* 所以将 type 属性设置为 all 可以同步所有进程的信息,以避免每次访问数据时都不相同*/type: 'all'}}async subscribe() {const response = await this.ctx.curl('http://127.0.0.1:3000/getMsg');console.log(response.data.toString());}
}module.exports = UpdateCache;
上述示例中,通过 schedule 静态属性设置定时任务的执行间隔等配置选项,而 subscribe 实例方法是定时任务执行时被运行的函数
定时任务总是在间隔一段时间后才会被运行,如果我们希望在程序一启动时就立刻执行定时任务,此时可以自定义启动项,我们必须在项目目录下(不是 app 目录下)创建一个 app.js 文件,在此文件中自定义启动项,示例如下:
class AppBootHook {constructor(app) {this.app = app}// 此方法将在 EggJS 程序启动完毕后执行async serverDidReady() {// 必须注意的是,此处传递的不是方法名称,而是需要被执行的定时任务所在的模块名称await this.app.runSchedule('updateMessage');}
}module.exports = AppBootHook
上述示例中,函数 serverDidReady 是 EggJS 框架提供的生命周期函数,类似于 Vue 中的生命周期函数,在程序从生到死的过程中的某些时刻将自动调用这些生命周期函数,内容如下:
| 生命周期函数 | 调用时机 |
|---|---|
| configWillLoad | 配置文件即将加载,这是最后动态修改配置的时机 |
| configDidLoad | 配置文件加载完成 |
| didLoad | 文件加载完成 |
| willReady | 插件启动完毕 |
| didReady | worker 准备就绪 |
| serverDidReady | 应用启动完成 |
| beforeClose | 应用即将关闭 |
生命周期函数通常写在项目目录下的 app.js 文件中
虽然 EggJS 在 Application、Context、Request、Response 实例上提供了很多常用的方法供我们使用,但有时这些方法并不能满足我们的需求,所以 EggJS 框架提供了扩展的功能,通过此功能可以相当方便的为 ctx、request、response、application、helper 实例扩展方法,要想为这些实例扩展方法,必须先在 app 目录下新建一个 extend 目录,在目录中新建如下文件:
每个文件分别为各自相应的实例扩展方法,示例如下:
module.exports = {extAppMethod(param) {// application 中的 this 就是 app 实例,通过此实例可以访问 app 上的其它属性或方法return `Call Application ${param}`;}
}
module.exports = {extCtxMethod(param) {// context 中的 this 就是 ctx 实例,通过此实例可以访问 ctx 上的其它属性或方法return `Call Context ${param}`;}
}
const crypto = require('crypto');module.exports = {md5: (msg) => {return crypto.createHash('md5').update(msg).digest('hex');}
}
实际上,helper 中专门用于定义一些工具方法
module.exports = {extReqMethod(param) {// request 中的 this 就是 request 实例,通过此实例可以访问 request 上的其它属性或方法return `Call Request ${param}`;}
}
module.exports = {extResMethod(param) {// response 中的 this 就是 response 实例,通过此实例可以访问 response 上的其它属性或方法return `Call Response ${param}`;}
}
如下示例演示了如何使用扩展的方法:
const Controller = require('egg').Controller;class HomeController extends Controller {async extendTest() {console.log(this.app.extAppMethod('app'));console.log(this.ctx.extCtxMethod('ctx'));console.log(this.ctx.request.extReqMethod('req'));console.log(this.ctx.response.extResMethod('res'));console.log(this.ctx.helper.md5('Reyn Morales'));}
}module.exports = HomeController;
框架 EggJS 本质上是基于 Koa 的,所以 EggJS 的中间件形式和 Koa 的中间件形式相同,不过 EggJS 的中间件必须写在规定的目录中,且 EggJS 为中间件提供了多种使用方式,如果要自定义中间件,必须先在 app 目录下新建一个 middleware 子目录,此目录专门用于编写自定义的中间件,示例如下:
/*** 客户端内核检测的中间件* @param options 选项说明检测哪个内核,必须以 RegExp 的形式说明,例如 { ua: /Chrome/ }* @param app 服务器实例* @returns {(function(*, *): Promise)|*}*/
module.exports = (options, app) => {return async (ctx, next) => {const userAgent = ctx.get('user-agent');if (options.ua.test(userAgent)) {ctx.status = 401;ctx.body = '不支持当前浏览器';}next();}
}
如下是一个使用自定义中间件的示例:
module.exports = app => {// 从 app 中解构出 router 和 controllerconst {router, controller} = app;// 自定义中间件测试const clientCheck = app.middleware.clientCheck({ua: /Chrome/});router.get('/middlewareTest', clientCheck, controller.home.index);
}
上述示例中,通过 app 实例的 middleware 属性相当于访问 middleware 目录,通过 clientCheck 中间件实现了当用户访问 /middlewareTest 路由时检测客户端的功能,实际上,通过 config.default.js 可以让 clientCheck 中间件在全局起作用,示例如下:
module.exports = {// 必须注意的是,Key 必须是中间件所属模块的名称middleware: ['clientCheck'],// 以下的实例将成为中间件的 options 参数clientCheck: {ua: /Chrome/}
}
国际化实际上就是多语言,可以让网页在不同的国家显示不同的语言,也可以让网页支持语言切换,要想让项目实现国际化,必须在 config 目录下新建一个 locale 子目录,对于任意中语言都必须在此目录下新建一个配置文件,示例如下:
module.exports = {Email: 'email',userName: 'username',password: 'password'
}
module.exports = {Email: '邮箱',userName: '用户名',password: '密码'
}
以下是一个国际化的示例:
const Controller = require('egg').Controller;class HomeController extends Controller {async index() {await this.ctx.render('index', {username: this.ctx.__('userName'),password: this.ctx.__('password')})}
}module.exports = HomeController;
如果想在 EggJS 项目中使用 MySQL,必须先下载 mysql2 和 egg-mysql 插件,示例如下:
npm install mysql2 egg-mysql --save
之后在 plugin.js 中开启插件,示例如下:
module.exports = {mysql: {enable: true,package: 'egg-mysql'}
}
之后在 config.default.js 中配置数据库连接信息,示例如下:
module.exports = {mysql: {client: {host: '127.0.0.1',port: '3306',user: 'root',password: '1234',database: 'test'},app: true,agent: false}
}
之后就可以在程序中对数据库进行增删改查了,示例如下:
const Service = require('egg').Service;class UserService extends Service {async createUser(user) {const result = await this.app.mysql.insert('users', {name: user.name,age: user.age})return result.affectedRows === 1;}async getUser(userId) {const result = await this.app.mysql.get('users', {id: userId})return result;}
}module.exports = UserService;
在上述示例中,说明了如何添加和查询记录,至于修改和删除记录此处不再说明,详情可以查询官方文档
const Controller = require('egg').Controller;class UserController extends Controller {async createUser() {const isFailed = await this.service.user.createUser(this.ctx.query);if (isFailed) {this.ctx.body = 'Create Error'}this.ctx.body = 'Create Success'}async getUser() {const result = await this.service.user.getUser(this.ctx.query.id);this.ctx.body = result;}
}module.exports = UserController;
如果想在 EggJS 项目中使用 Sequelize,必须先下载 mysql2 和 egg-sequelize 插件,示例如下:
npm install mysql2 egg-sequelize --save
之后在 plugin.js 中开启插件,示例如下:
module.exports = {sequelize: {enable: true,package: 'egg-sequelize'}
}
之后在 config.default.js 中配置 sequelize 相关信息,示例如下:
module.exports = {sequelize: {dialect: 'mysql',host: '127.0.0.1',port: 3306,user: 'root',password: '1234',database: 'test'}
}
创建一个数据库以及一张 user 表,之后在 app 目录下新建一个 model 子目录,此目录专门用于存放数据模型,示例如下:
module.exports = app => {const {STRING, INTEGER, TINYINT} = app.Sequelize;const User = app.model.define('user', {id: {type: INTEGER, primaryKey: true, autoIncrement: true, allowNull: false},name: {type: STRING(255), allowNull: false},age: TINYINT}, {freezeTableName: true,timestamps: false});return User;
}
数据模型中的属性必须和数据表中的字段相应
之后就可以在程序中对数据库进行增删改查了,示例如下:
const Controller = require('egg').Controller;class UserController extends Controller {async createUser() {const user = await this.ctx.model.User.create(this.ctx.query);this.ctx.body = user;}async getUser() {const user = await this.ctx.model.User.findByPk(this.ctx.query.id);this.ctx.body = user;}
}module.exports = UserController;
如果通过 Sequelize 操纵数据库,那么就不需要再写 Service 相关的代码了,在上述示例中,说明了如何添加和查询记录,至于修改和删除记录此处不再说明,详情可以查询官方文档
实际上,EggJS 框架为了提供了多种配置文件,以方便我们在不同的阶段中使用,内容如下:
| 配置文件 | 加载时机 |
|---|---|
| config.prod.js | 只在生产环境下加载 |
| config.test.js | 只在测试环境下加载 |
| config.local.js | 只在开发环境下加载 |
| config.default.js | 所有环境都会加载 |
也就是说每次最多加载两个配置文件,如果在不同的配置文件中出现了同名的配置选项,那么 default 中的配置将被覆盖,此外,必须注意的是,所有的配置文件都必须写在 config 目录下,示例如下:
以下示例演示了在不同的配置文件中配置数据库连接相关的信息,在开发阶段和上线阶段使用不同的数据库:
module.exports = {sequelize: {dialect: 'mysql',host: '127.0.0.1',port: 3306,user: 'root',password: '1234',database: 'development_test_db'}
}
module.exports = {sequelize: {dialect: 'mysql',host: '127.0.0.1',port: 3306,user: 'root',password: '1234',database: 'production_test_db'}
}
基于 EggJS 的项目通过 EGG_SERVER_ENV 设置环境模式,示例如下:
{"scripts": {"dev": "cross-env EGG_SERVER_ENV=dev egg-bin dev","prod": "cross-env EGG_SERVER_ENV=prod egg-scripts start --daemon"}
}
框架 EggJS 同样提供了脚手架工具以快速构建项目,在使用脚手架工具创建项目之前,必须先下载脚手架工具 egg-init,示例如下:
npm install egg-init -g
在下载完成之后,首先新建一个项目目录,通过 cd 命令切换到此目录之后,利用如下指令初始化 EggJS 项目:
npm init egg --type=simple
选项 type 用于指定初始化 EggJS 项目时所采用的骨架类型,内容如下:
| 骨架类型 | 说明 |
|---|---|
| simple | 简单 egg 应用程序骨架 |
| empty | 空的 egg 应用程序骨架 |
| plugin | egg plugin 骨架 |
| framework | egg framework 骨架 |
在初始化完成之后,通过 cd 命令切换到项目目录下,执行 npm install 下载相关依赖,之后通过如下指令就可以运行项目了
npm run dev # 开发模式
npm run test # 测试模式
npm run start # 生产模式
在基于 EggJS 的项目中具有 CSRF 安全防范功能,默认情况下的 Post 请求是无法成功的,必须在请求头中将 Cookie 中的 csrfToken 一起发送至客户端才能通过 csrf 验证,如果是静态网页,那么可以通过 jQuery 在发送请求之前将 csrfToken 写入请求头,示例如下:
$(function() {/* 动态获取csrfToken*/function getCookie(key) {const res = document.cookie.split(';');for (let i = 0; i < res.length; i++) {const temp = res[i].split('=');if (temp[0].trim() === key) {return temp[1];}}}const csrftoken = getCookie('csrfToken');function csrfSafeMethod(method) {// these HTTP methods do not require CSRF protectionreturn (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));}/* 动态设置csrfToken*/$.ajaxSetup({beforeSend(xhr, settings) {if (!csrfSafeMethod(settings.type) && !this.crossDomain) {xhr.setRequestHeader('x-csrf-token', csrftoken);}},});
});
如果是动态网页,那么还有更简单的写法,此处不再详细说明,详情可以访问官方文档
在基于 EggJS 的项目中,同样可以使用 ajv 利用 JSONSchema 进行数据校验,如果想要使用 ajv,必须先下载 ajv 插件 egg-ajv,示例如下:
npm install egg-ajv --save
之后我们必须在 plugin.js 中开启此插件,示例如下:
'use strict';/** @type Egg.EggPlugin */
module.exports = {ajv: {enable: true,package: 'egg-ajv',},
};
之后我们必须在 app 目录下新建一个 schema 子目录专门用于保存 JSONSchema,示例如下:
const userValidator = {type: 'object',properties: {username: {type: 'string',pattern: '^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$',maxLength: 255,minLength: 3,},password: {type: 'string',pattern: '^[A-Za-z0-9]{6,20}$',maxLength: 20,minLength: 6,},},required: [ 'username', 'password' ],
};module.exports = userValidator;
之后就可以在 controller 中通过 validate 进行数据校验,示例如下:
'use strict';const { Controller } = require('egg');class HomeController extends Controller {async register() {const isValidate = await this.ctx.validate('schema.user', this.ctx.request.body);if (!isValidate) {this.ctx.error(400, this.ctx.helper.errorCode[400]);} else {try {const user = await this.service.user.createUser(this.ctx.request.body);this.ctx.success(user);} catch (e) {this.ctx.error(-1, e.message);}}}
}module.exports = HomeController;
在基于 EggJS 的项目中,可以通过 egg-session-redis 和 egg-redis 插件实现将 Session 存储到 redis 中的功能,在使用之前必须先下载它们,示例如下:
npm install egg-redis egg-session-redis --save
之后在 plugin.js 中开启它们,示例如下:
'use strict';/** @type Egg.EggPlugin */
module.exports = {redis: {enable: true,package: 'egg-redis',},sessionRedis: {enable: true,package: 'egg-session-redis',},
};
之后在 config.default.js 中配置 redis 的连接信息,示例如下:
/* eslint valid-jsdoc: "off" */'use strict';/*** @param {Egg.EggAppInfo} appInfo app info*/
module.exports = appInfo => {/*** built-in config* @type {Egg.EggAppConfig}**/const config = exports = {};config.redis = {client: {port: 6379, // Redis porthost: '127.0.0.1', // Redis hostpassword: '',db: 0,},};return {...config,...userConfig,};
};
之后就可以在 controller 中添加 session 并将数据存储到 redis 中了,示例如下:
'use strict';const { Controller } = require('egg');class HomeController extends Controller {async login() {try {const user = await this.service.user.getUser(this.ctx.request.body);this.ctx.session.user = user;this.ctx.success(user);} catch (e) {this.ctx.error(-1, e.message);}}
}module.exports = HomeController;