第一章:Node.js与内置模块(fs文件系统、path路径模块、http服务器模块)
第二章:Nodejs模块化(npm与包、开发自己的包、模块加载机制)
第三章:Nodejs之Express(基本使用、Express路由)(一)
第四章:Nodejs之Express( Express 中间件、中间件的分类、自定义中间件)(二)
第五章:Nodejs之解决接口跨域问题
第六章:Nodejs操作Mysql数据库
第七章:Node之前后端身份认证(Session认证机制、JWT 认证机制)
第九章:Nodejs之egg基本使用(egg服务、egg-mysql、CSRF漏洞、egg-jwt、Swagger)
Express 是 Node.js 社区⼴泛使⽤的框架,简单且扩展性强,⾮常适合做个⼈项⽬。企业级开发框架,上层框架,提供给社区框架。
$ mkdir egg-example && cd egg-example
$ npm init egg --type=simple
$ npm install
//启动项⽬ 使⽤npm start 或者 npm run dev
$ npm start
...
$ npm stop
使⽤simple⻣架创建出来的项⽬⽬录如下:

这⾥介绍⼀下框架中内置的⼀些基础对象,包括从 Koa 继承⽽来的 4 个对象(Application、 Context、 Request、Response) 以及框架扩展的⼀些对象(Controller、Service、 Helper、Config、Logger)。
全局应⽤对象,在⼀个应⽤中,只会实例化⼀个,它继承⾃ Koa.Application,在它上⾯我们可以挂载⼀些全局的⽅法和对象。
获取⽅式:this.app、this.ctx.app
/*
可以通过 ctx.app 访问到 Application 对象
在继承于 Controller, Service 基类的实例中,可以通过 this.app 访问到Application
*/
// app/controller/user.js
class UserController extends Controller {
async fetch() {
// 往全局app中保存数据
this.app.cache = {name:'tom'};
// 或者 this.ctx.body = this.app.cache.name;
this.ctx.body = this.ctx.app.cache.name;
}
}
Context 是⼀个请求级别的对象,继承⾃ Koa.Context。在每⼀次收到⽤户请求时,框架会实例化⼀个 Context 对象,这个对象封装了这次⽤户请求的信息,并提供了许多便捷的⽅法来获取请求参数或者设置响应信息。框架会将所有的 Service 挂载到 Context 实例上,⼀些插件也会将⼀些其他的⽅法和对象挂载到它上⾯。
// app/controller/user.js
class UserController extends Controller {
async fetch() {
// this.ctx就是Context对象
this.ctx.body = "";
} }
Request 是⼀个请求级别的对象,继承⾃ Koa.Request。封装了 Node.js 原⽣的HTTPRequest 对象,提供了⼀系列辅助⽅法获取 HTTP 请求常⽤参数。
获取⽅式:this.ctx.request
Response 是⼀个响应级别的对象,继承⾃ Koa.Response。封装了 Node.js 原⽣的HTTP Response 对象,提供了⼀系列辅助⽅法设置 HTTP 响应。
获取⽅式:this.ctx.response
// 可以在 Context 的实例上获取到当前请求的 Request(ctx.request) 和
Response(ctx.response) 实例。
// app/controller/user.js
class UserController extends Controller {
async fetch() {
const { app, ctx } = this;
const id = ctx.request.query.id;
ctx.response.body = app.cache.name;
//ctx.body = app.cache.name;
}
}
框架提供了⼀个 Controller 基类,并推荐所有的 Controller 都继承于该基类实现。
这个Controller 基类有下列属性(也就是说Controller内的this有哪些内容):
// app/controller/user.js
// 从 egg 上获取(推荐)
const {Controller} = require('egg');
class UserController extends Controller {
// implement
}
module.exports = UserController;
框架提供了⼀个 Service 基类,并推荐所有的 Service 都继承于该基类实现。Service 基类的属性和 Controller 基类属性⼀致,访问⽅式也类似:
let { Service } = require("egg");
class UserService extends Service {
}
module.exports = UserService;
Helper ⽤来提供⼀些实⽤的 utility 函数。 它的作⽤在于我们可以将⼀些常⽤的动作抽离在helper.js ⾥⾯成为⼀个独⽴的函数,这样可以⽤ JavaScript 来写复杂的逻辑,避免逻辑分散各 处,同时可以更好的编写测试⽤例。Helper ⾃身是⼀个类,有和 Controller 基类⼀样的属性, 它也会在每次请求时进⾏实例化,因此 Helper上的所有函数也能获取到当前请求相关的上下⽂ 信息。可以在 Context 的实例上获取到当前请求的 Helper( ctx.helper ) 实例。
class UserService extends Service {
// implement
}
module.exports = UserService;
// 从 app 实例上获取
module.exports = (app) => {
return class UserService extends app.Service {
// implement
};
};
// app/controller/user.js
class UserController extends Controller {
async fetch() {
const { app, ctx } = this;
const id = ctx.query.id;
const user = app.cache.get(id);
ctx.body = ctx.helper.formatUser(user);
}
}
// app/extend/helper.js
module.exports = {
formatUser(user) {
return only(user, [ 'name', 'phone' ]);
}
我们推荐应⽤开发遵循配置和代码分离的原则,将⼀些需要硬编码的业务配置都放到配置⽂件中,同时配置⽂件⽀持各个不同的运⾏环境使⽤不同的配置,使⽤起来也⾮常⽅便,所有框架、插件和应⽤级别的配置都可以通过 Config 对象获取到。我们可以通过 app.config 从 Application 实例上获取到 config 对象,也可以在Controller, Service, Helper 的实例上通过this.config 获取到 config 对象。
获取⽅式:this.config、this.app.config
框架内置了功能强⼤的⽇志功能,可以⾮常⽅便的打印各种级别的⽇志到对应的⽇
志⽂件中,每⼀个 logger 对象都提供了 4 个级别的⽅法:
获取⽅式:this.logger(this.ctx.logger)、this.app.logger
Router 主要⽤来描述请求URL和具体承担执⾏动作的Controller 的对应关系, 框架约定了app/router.js ⽂件⽤于统⼀所有路由规则。
// app/router.js
module.exports = app => {
const { router, controller, jwt } = app;
router.get('/', controller.home.index);
// 提供学生查询接口
router.get('/student/findAll', jwt, controller.student.findAll);
// 学生删除接口
router.delete('/student/deleteById', controller.student.deleteById);
// 学生新增和修改的路由
router.post("/student/saveOrUpdate", controller.student.saveOrUpdate);
// 分页查询接口
router.get('/student/pageQuery', controller.student.pageQuery);
// 学生分页模糊查询
router.get('/student/pageQueryM', controller.student.pageQueryM);
// 提供通过id查询学生详情
router.get('/student/findById', controller.student.findById);
// 登录接口
router.post('/user/login', controller.user.login);
};
Controller 负责解析⽤户的输⼊,处理后返回相应的结果。框架推荐 Controller 层主要对⽤户的请求参数进⾏处理(校验、转换),然后调⽤对应的 service ⽅法处理业务,得到业务结果后封装并返回。所有的 Controller ⽂件都必须放在 app/controller ⽬录下,可以⽀持多级⽬录,访问的时候可以通过⽬录名级联访问。
'use strict';
const { Controller } = require('egg');
class StudentController extends Controller {
// 查询接口处理函数
async findAll() {
};
}
module.exports = StudentController;
const query = this.ctx.query; //如/a?name=tom&age=12,结果为{name:'tom',age:12}
const queries = this.ctx.queries //如/a?name=tom&name=lisi&age=12,结果为{name:['tom','lisi'],age:[12]}
const params = this.ctx.params; //如路由路径为/a/:id,使⽤/a/1路由,结果
为{id:1}
const data = this.ctx.request.body; //如请求体中传递了数据,则在这⾥可以
接收到
ctx.host //localhost:7001
ctx.protocol //http
ctx.ips //[]
ctx.ip // ::1
1234
console.log(ctx.get('cookie'));
console.log(ctx.request.header['cookie']);
let count = ctx.cookies.get('count', { httpOnly: false, signed:
false });
count = count ? Number(count) : 0;
ctx.cookies.set('count', ++count);
httpOnly: 设置键值对是否可以被 js 访问,默认为 true,不允许被 js 访问。
secure: signed:设置是否对 Cookie 进⾏签名,如果设置为 true,则设置键值对的
时候会同时对这个键值对的值进⾏签名,后⾯取的时候做校验,可以防⽌前端对这
个值进⾏篡改。默认为 true。
Cookie 在 Web 应⽤中经常承担标识请求⽅身份的功能,所以 Web 应⽤在 Cookie 的基础上封装了 Session 的概念,专⻔⽤做⽤户身份识别。通过 Cookie,我们可以给每⼀个⽤户设置⼀个 Session,⽤来存储⽤户身份相关的信息,这份信息会加密后存储在 Cookie 中,实现跨请求的⽤户身份保持。
const ctx = this.ctx;
// 获取 Session 上的内容
const userId = ctx.session.userId;
const posts = await ctx.service.post.fetch(userId);
// 设置session上的内容
ctx.session.userId = 1;
// 删除session上的内容
ctx.session.userId = null;
我们并不想在 Controller 中实现太多业务逻辑,所以提供了⼀个 Service 层进⾏业务逻辑的封装,这不仅能提⾼代码的复⽤性,同时可以让我们的业务逻辑更好测试。在 Controller 中可以调⽤任何⼀个 Service 上的任何⽅法,同时 Service 是懒加载的,只有当访问到它的时候框架才会去实例化它。
const res = await ctx.service.service中的⽂件名.service中的⽅法名(参 数);
this.ctx.status=201;
通常会返回 Content-Type为application/json 格式的 body,内容是⼀个 JSON 字符串。this.ctx.body = {
name: 'egg',
category: 'framework',
language: 'Node.js'
};
this.ctx.body = 'Hello
';
this.ctx.redirect(url)