搭建Node.js的Web框架egg.js
1 egg.js的Request处理流程:

2. 使用nodejs下载egg.js框架
(1)现在nodejs中全局安装egg-init
即在nodejs安装根目录下执行 :
d:cd nodejs npm install egg-init -g
注意:如何报错并提示是日志的原因,则删除nodejs的_cache目录即可
(3)快速生成最简单的项目
egg-init egg-example --type=simple
(4)在项目中安装node_nodules
cd egg-example npm install
(5)启动内置的Web服务器
npm run dev
3. egg.js常见的项目目录
egg-project
├── package.json
├── app.js (可选)
├── agent.js (可选)
├── app
| ├── router.js
│ ├── controller
│ | └── home.js
│ ├── service (可选)
│ | └── user.js
│ ├── middleware (可选)
│ | └── response_time.js
│ ├── schedule (可选)
│ | └── my_task.js
│ ├── public (可选)
│ | └── reset.css
│ ├── view (可选)
│ | └── home.tpl
│ └── extend (可选)
│ ├── helper.js (可选)
│ ├── request.js (可选)
│ ├── response.js (可选)
│ ├── context.js (可选)
│ ├── application.js (可选)
│ └── agent.js (可选)
├── config
| ├── plugin.js
| ├── config.default.js
│ ├── config.prod.js
| ├── config.test.js (可选)
| ├── config.local.js (可选)
| └── config.unittest.js (可选)
└── test
├── middleware
| └── response_time.test.js
└── controller
└── home.test.js
4. 控制器的示例代码如下:
home.js
'use strict';
const Controller = require('egg').Controller;
class HomeController extends Controller {
async index() {
const { ctx } = this;
ctx.body = 'hi, egg';
}
}
module.exports = HomeController;
5. 路由的示例代码如下:
router.js
'use strict';
/**
* @param {Egg.Application} app - egg application
*/
module.exports = app => {
const { router, controller } = app;
router.get('/', controller.home.index);
};
注意:egg.js是没有入口文件的,或者说egg.js框架的入口文件就是router.js即路由分发文件, 那么,egg.js是在什么加载框架核心文件和框架呢,egg.js的Web服务器是使用内置的Web服务器,而不是一般Web程序使用Tomcat,Apache,Nginx,IIS这样的第三方Web服务器,egg.js在控制台启动内置服务器时已经完成了加载框架的工作
6. 配置文件如下:
default.conf.js
/* eslint valid-jsdoc: "off" */
'use strict';
/**
* @param {Egg.EggAppInfo} appInfo app info
*/
module.exports = appInfo => {
/**
* built-in config
* @type {Egg.EggAppConfig}
**/
const config = {};
// use for cookie sign key, should change to your own and keep security
config.keys = appInfo.name + '_1552535331853_9413';
// add your middleware config here
config.middleware = [];
// add your user config here
const userConfig = {
// myAppName: 'egg',
};
return {
...config,
...userConfig,
};
};
7. 本地开发调试:
(1)在本地开发,调试,测试,需要使用egg-bin模块,安装egg-bin模块,可以使用命令:
npm install egg-bin --save-dev
(2) 将egg-bin模块添加到packages.json文件中
{
"scripts": {
"dev": "egg-bin dev"
}
}
一般来说,这两步是自动完成的; 一般来说,在项目根目录下使用 npm install 即在项目根目录下安装框架相关文件会自带已经安装了egg-bin, 是否已经安装egg-bin, 只需在项目跟目下看package.json文件的scripts即可
例如:
package.json
{
"name": "example",
"version": "1.0.0",
"description": "",
"private": true,
"egg": {
"declarations": true
},
"dependencies": {
"egg": "^2.15.1",
"egg-scripts": "^2.11.0"
},
"devDependencies": {
"autod": "^3.0.1",
"autod-egg": "^1.1.0",
"egg-bin": "^4.11.0",
"egg-ci": "^1.11.0",
"egg-mock": "^3.21.0",
"eslint": "^5.13.0",
"eslint-config-egg": "^7.1.0",
"webstorm-disable-index": "^1.2.0"
},
"engines": {
"node": ">=8.9.0"
},
"scripts": {
"start": "egg-scripts start --daemon --title=egg-server-example",
"stop": "egg-scripts stop --title=egg-server-example",
"dev": "egg-bin dev",
"debug": "egg-bin debug",
"test": "npm run lint -- --fix && npm run test-local",
"test-local": "egg-bin test",
"cov": "egg-bin cov",
"lint": "eslint .",
"ci": "npm run lint && npm run cov",
"autod": "autod"
},
"ci": {
"version": "8"
},
"repository": {
"type": "git",
"url": ""
},
"author": "",
"license": "MIT"
}
可以看到scripts标签下有start, stop, dev, debug, test, test-local, cov, lint, ci, autod 相关标签
执行start命令的方法为 在项目根目录下执行 npm run start
8. egg.js框架的后台启动流程(以运行npm run dev为例)
步骤:
(1)在控制台中执行命令,对应的命令解析请查看项目根目录的package.json文件的配置
从配置文件中可以看出npm run dev 其实是egg-bin dev;
"scripts": {
"start": "egg-scripts start --daemon --title=egg-server-example",
"stop": "egg-scripts stop --title=egg-server-example",
"dev": "egg-bin dev",
"debug": "egg-bin debug",
"test": "npm run lint -- --fix && npm run test-local",
"test-local": "egg-bin test",
"cov": "egg-bin cov",
"lint": "eslint .",
"ci": "npm run lint && npm run cov",
"autod": "autod"
},
所以查找node_modules/egg-bin模块
(2)调用node_modules/egg-bin下的lib/start-cluster.js及lib/cmd/dev.js
require(options.framework).startCluster(options)
继续追踪发现start-cluster.js调用require(options.framework).startCluster(options) 来实现启动框架
其中options.framework指的是node-moduels/egg模块, 即调用了node-modules/egg下的index.js的startCuster方法
(3)调用node-modules/egg下的index.js的startCluster方法
发现node-modules/egg的index.js中:
exports.startCluster = require('egg-cluster').startCluster;
即调用了node-modules/egg-cluster的index.js中的startCluster方法
(4)调用node-modules/egg-cluster下的index.js的startCluster方法
发现:
exports.startCluster = function(options, callback) {
new Master(options).ready(callback);
};
即调用了node-modules/egg-cluster的lib/master.js
(5)调用net模块的createServer方法
startMasterSocketServer(cb) {
// Create the outside facing server listening on our port.
require('net').createServer({ pauseOnConnect: true }, connection => {
// We received a connection and need to pass it to the appropriate
// worker. Get the worker for this connection's source IP and pass
// it the connection.
/* istanbul ignore next */
if (!connection.remoteAddress) {
connection.close();
} else {
const worker = this.stickyWorker(connection.remoteAddress);
worker.send('sticky-session:connection', connection);
}
}).listen(this[REALPORT], cb);
}
遇到的问题:
使用npm run dev启动框架时报错:
Error: [egg-core] load file: F:\project\chgg-data-server\lib\plugin\egg-wechat-o
auth\agent.js, error: Cannot find module 'co-wechat-oauth'
使用npm install co-wechat-aouth即可
再次执行命令 npm run dev 服务器成功启动
9. 什么是CSRF Token
(1)遇到的问题:
使用PostMan调用restAPI接口测试时,报403错误,具体为CSRF Token

(2)CSRF Token的意义:
防止用户跨站请求,CSRF Token是在用户登录时由服务器生成的随机加密字符串,一般存放在用户端Cookie,这样防止了无需登录服务器请求服务器API的问题
(3)怎么解决:
a. 在表单中一般通过添加一个隐藏域{%csrftoken%}
b. 在AJAX请求中通过请求头携带csrftoken
$.ajax({
url:"/login/",
type:"POST",
data:{"usr":"root","pwd":"123"},
headers:{ "X-CSRFtoken":$.cookie("csrftoken")},
success:function (arg) {
}
})
c. csrf在前端的key为:X-CSRFtoken,到后端的时候一般会自动添加HTTP_,并且最后为HTTP_X_CSRFtoken
(4)后台发现需要获取微信公众号的access_token作为csrftoken
10. 使用启动命令在测试或者生产环境启动时失败
启动指令
//此命令为在项目根目录下package.json文件中配置的npm run test
实际上执行的命令是
egg-scripts stop --title=chgg-data-server;egg-scripts start --port= --daemon --title=chgg-data-server --env=test//即先关闭已经启动的守护进程;然后重新启动,指定端口为7001,为守护进程方式启动, 指定启动进程的标题为chgg-data-server, 指定环境为test
执行期间报错
[egg-scripts] Wait Start: ... [egg-scripts] Wait Start: ... [egg-scripts] Wait Start: ... [egg-scripts] Wait Start: ... [egg-scripts] Got error when startup: [egg-scripts] -- ::, ERROR [agent_worker] receive disconnect event on child_process fork mode, exiting with code: [egg-scripts] -- ::, ERROR [agent_worker] exit with code:
解决办法:删除node_modules目录重新安装
使用命令npm install报错提示killed

原因是内存不够用特别是交换区内存不够导致的
使用以下命令可查看内存的使用情况:
free -h
使用swapfile实现虚拟内存:
使用一下命令查看是否配置了交换区域:
swapon -s
配置1G的虚拟内存:
fallocate -l 1G /swapfile
确认是否正确:
ls -lh /swapfile
修改swapfile的权限,防止安全隐患:
/swapfile
设置系统交换区域:
mkswap /swapfile
启用交换区:
swapon /swapfile
验证swapfile是否正确:
swapon -s
设置开机自启动该交换区文件:
vim /etc/fstab # 添加一行 /swapfile none swap sw
再次启动egg.js,启动成功:

11. 如何获取域名对应的ip地址
背景:在测试服务器上调试接口的时候无法确定是否使用了正确的域名
(1)使用windows的nslookup工具查询
cmd->nslookup->输入域名即可

(2)在线工具查询
在百度中输入nslookup进入工具查询

12. 配置nginx代理服务器
- 背景
- egg.js内部自带的Web服务器使用端口区分不同的应用,但是对外的Web服务应该使用域名来区分,端口统一默认为80,所以需要配置nginx作为代码服务器
- 代理服务器的作用:
- 使用域名区分同一主机上的不同Web应用,而不是使用端口区分
- FQ作用,使用中间代理服务器访问国外网站
- 防火墙作用,做中间的安全问题处理
- 缓存作用,做中间的缓存作用
- 反向代理进行访问分发,做负载均衡
例如:
使用http协议的代理
/etc/nginx/conf.d/data_stage.conf
# 将47.95.140.28:7001映射成data.stage.chinahanguang.com:80# 80端口为HTTP协议的默认端口# 127.0.0.1代表为本机,本机对外IP为47.95.140.28
server{
listen ;
server_name data.stage.chinahanguang.com;
location / {
proxy_pass http://127.0.0.1:7001;
}
}
使用https协议的代理
/etc/nginx/conf.d/data_stage.conf
server {
listen ;
server_name data.stage.chinahanguang.com;
# ssl证书和签名在域名注册域名或域名绑定的地方获取
ssl on;
ssl_certificate /data/cert/data_stage.pem;
ssl_certificate_key /data/cert/data_stage.key;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1. TLSv1.;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4;
ssl_prefer_server_ciphers on;
# ssl_dhparam /etc/ssl/certs/dhparam.pem;
location / {
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://127.0.0.1:7001;
}
}
在/etc/nginx.conf文件中
include /etc/nginx/conf.d/*.conf
13. egg.js的中间件MiddleWare简单介绍
中间件即为在Request到Route之间进行进行过滤请求
(1)在config/config.default.js中配置中间件
例如:以下配置了3个中间件
exports.middleware = ['errorHandler', 'erpAuthrized', 'ipLimited'];
exports.errorHandler = {
rex_match: /^\/api/g,
};
exports.erpAuthrized = {
rex_match: /^\/api\/v/g,
};
exports.ipLimited = {
rex_match: /^\/api\/private/g,
};
(2)中间件的代码示例:
middleware/errHandler.js
module.exports = options => {
return async function errorHandler(ctx, next) {
if (!new RegExp(options.rex_match).test(ctx.originalUrl)) {
await next();
return;
}
try {
await next();
} catch (err) {
// 所有的异常都在 app 上触发一个 error 事件,框架会记录一条错误日志
ctx.app.emit('error', err, ctx);
const status = err.status || 500;
// 生产环境时 500 错误的详细错误内容不返回给客户端,因为可能包含敏感信息
const error = status === 500 && ctx.app.config.env === 'prod'
? 'Internal Server Error'
: err.message;
// 从 error 对象上读出各个属性,设置到响应中
ctx.body = { error };
if (status === 422) {
ctx.body.detail = err.errors;
}
ctx.status = status;
}
};
};
middleware/erp_authrized.js
/**
* 该中间件通过ERP登录后系统的SessionId做出限定
*
* @param options
* @returns {erpAuthrized} config.default.js中注册中间件名称
*/
module.exports = options => {
return async function erpAuthrized(ctx, next) {
"use strict";
const REDIS_SESSSION_PREFIX = "WX_APP:SESSION:";
//这里options.rex_match为/^\/api\/v/g
//这里ctx.originalUrl为HTTP请求的去除主机的后半部分url
if (!new RegExp(options.rex_match).test(ctx.originalUrl)) {
//next()为MiddleWare中间件的下一个职责链
await next();
return;
}
let sessionId = ctx.header["session-id"];
if (sessionId) {
const userSession = await ctx.app.redis.get(REDIS_SESSSION_PREFIX + sessionId);
if (userSession) {
ctx.erp_wechat_session = JSON.parse(userSession);
await next();
} else {
ctx.statusCode = 401;
ctx.body = {
code: 205,
message: "登录已过期,请重新登录!"
}
}
} else {
ctx.statusCode = 401;
ctx.body = {
code: 204,
message: "非法请求!"
}
await next();
return;
}
};
};
middleware/ip_limited.js
const requestIp = require('request-ip');
const PRIVATE_API_IP_WHITE_LIST = require('../model/redis/REDIS_KEYS').PRIVATE_API_IP_WHITE_LIST;
/**
* 该中间件用于限制某些私有API的快速访问,通过请求IP对其进行控制
*
* @param options
* @returns {ipLimited} config.default.js中注册中间件名称
*/
module.exports = options => {
return async function ipLimited(ctx, next) {
"use strict";
//如果url版本使用的是v及正式版本,则需要验证ip地址是否在白名单中
if (!new RegExp(options.rex_match).test(ctx.originalUrl)) {
await next();
return;
}
//获取客户端的ip地址
const clientIp = requestIp.getClientIp(ctx.request);
if (clientIp) {
//白名单存放在Redis中
const ipStr = await ctx.app.redis.get(PRIVATE_API_IP_WHITE_LIST);
const ipWhiteList = JSON.parse(ipStr);
console.log('ipWhiteList = ', ipWhiteList);
//如果客户端ip在白名单中,则授权通过,执行下一个职责链
if (!!ipWhiteList && ipWhiteList.includes(clientIp)) {
await next();
} else {
ctx.statusCode = 401;
ctx.body = {
code: 203,
message: "请求IP未被授权!"
}
}
} else {
ctx.statusCode = 500;
ctx.body = {
code: 505,
message: "未获取到服务器IP!"
}
}
};
};
搭建Node.js的Web框架egg.js的更多相关文章
- koa : Express出品的下一代基于Node.js的web框架
https://www.liaoxuefeng.com/wiki/001434446689867b27157e896e74d51a89c25cc8b43bdb3000/001434501579966a ...
- 强大的 Node.js Web 框架 - Daze.js
去年年初对 Node.js 比较感兴趣,也用了很多 Node.js 的框架,但是开发体验不是特别好,我之前也是后端转前端,然后再接触 Node.js ,所以用过挺多的服务端框架,相对js而言,设计一款 ...
- 7个Node.js的Web框架
NodeJS也就是Node,是众所周知的使用javascript构建Web应用框架,它启动一个服务器非常简单,如下: var http = require('http'); http.createSe ...
- js基础系列框架:JS重要知识点(转载)
这里列出了一些JS重要知识点(不全面,但自己感觉很重要).彻底理解并掌握这些知识点,对于每个想要深入学习JS的朋友应该都是必须的. 讲解还是以示例代码搭配注释的形式,这里做个小目录: JS代码预解析原 ...
- 150行代码搭建异步非阻塞Web框架
最近看Tornado源码给了我不少启发,心血来潮决定自己试着只用python标准库来实现一个异步非阻塞web框架.花了点时间感觉还可以,一百多行的代码已经可以撑起一个极简框架了. 一.准备工作 需要的 ...
- Express 4.x Node.js的Web框架
为了防止无良网站的爬虫抓取文章,特此标识,转载请注明文章出处.LaplaceDemon/SJQ. http://www.cnblogs.com/shijiaqi1066/p/3821150.html ...
- Express 4.x Node.js的Web框架----《转载》
本文使用node.js v0.10.28 + express 4.2.0 1 Express概述 Express 是一个简洁而灵活的node.js的MVC Web应用框架,提供一系列强大特性创建各种W ...
- [Node.js]Express web框架
摘要 Express是一个简洁灵活的node.js web应用框架,提供了一系列强大特性帮助你创建各种web应用和丰富的http工具.使用express可以快速创建一个完整功能的网站. Express ...
- 基于node.js的web框架express
1.安装node.js方法: window :https://nodejs.org/en/ linux:http://www.runoob.com/nodejs/nodejs-install-setu ...
随机推荐
- Java开发笔记(五十一)多态的发生场景
江湖上传闻,面向对象之所以厉害,是因为它拥有封装.继承与多态三项神技,只要三板斧一出,号令天下谁敢不从.前面费了老大的劲才讲清楚封装和继承,那么多态又是怎样的神乎其神呢?下面先通过一个简单的例子来说明 ...
- react create-react-app 跨域
"proxy":"http://youAddr.com" 直接到根目录package.json里增加上面这行就行了,改成自己需要的地址.
- 【代码笔记】Web-CSS-CSS 教程
一,效果图. 二,代码. <!DOCTYPE html> <html> <head> <meta charset="utf-8"> ...
- Apex 中文件夹相关的单元测试
Salesforce 中的文件夹 在 Salesforce 中,我们可以建立各种文档.报表.仪表板.电子邮件模板等.它们都被保存在相应的文件夹中. Salesforce 的后端将这些文件夹保存为 Fo ...
- 从零学习Fluter(七):Flutter打包apk详解
写一个win上 flutter 打包apk的教程 这篇文档介绍一下flutter打包发布正式版apk 整体来看,和命令行打包rn的方法相差不大 打包前先做检查工作&查看构建配置 Android ...
- 2019Java查漏补缺(三)
1.为什么这个public的类的类名必须和文件名相同 是为了方便虚拟机在相应的路径中找到相应的类所对应的字节码文件 2.java8 的一些新特性: 3: 数据库隔离级别 隔离级别 ...
- Windows WMIC命令使用详解(附实例)
第一次执行WMIC命令时,Windows首先要安装WMIC,然后显示出WMIC的命令行提示符.在WMIC命令行提示符上,命令以交互的方式执行 执行“wmic”命令启动WMIC命令行环境.这个命令可以在 ...
- 【原】Java学习笔记020 - 面向对象
package cn.temptation; public class Sample01 { public static void main(String[] args) { // 成员方法的参数列表 ...
- Java调用windows命令
JAVA调用windows的cmd命令 用起来会让程序变得更加简洁明了,非常实用. 核心就是使用 Runtime类. cmd的xcopy就有很强大的文件夹,文件处理功能. 下面就以xcopy来说明,如 ...
- SQLServer修改数据列
修改数据列 在开发和生产过程中,列名的拼写错误或者列名的更改是需要操作数据表的,大多数情况下都是不需要修改的. 以下几种情况下我们并不能直接修改数据列: 1.用于索引的列. 2.用于 CHECK.FO ...