搭建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 ...
随机推荐
- 【学习笔记】tensorflow图片读取
目录 图像基本概念 图像基本操作 图像基本操作API 图像读取API 狗图片读取 CIFAR-10二进制数据读取 TFRecords TFRecords存储 TFRecords读取方法 图像基本概念 ...
- splay详解(一)
前言 Spaly是基于二叉查找树实现的, 什么是二叉查找树呢?就是一棵树呗:joy: ,但是这棵树满足性质—一个节点的左孩子一定比它小,右孩子一定比它大 比如说 这就是一棵最基本二叉查找树 对于每次插 ...
- spring注解的相关配置
一.<context:annotation-config> 和 <context:component-scan> <context:annotation-config&g ...
- 惊喜,重磅福利!免费开源ERP-企业信息化金矿
Odoo,以前叫OpenERP,是比利时Odoo S.A.公司开发的一个企业应用软件套件,开源套件包括一个企业应用快速开发平台,以及几千个Odoo及第三方开发的企业应用模块.Odoo适用于各种规模的企 ...
- 为你揭秘知乎是如何搞AI的——窥大厂 | 数智方法论第1期
文章发布于公号[数智物语] (ID:decision_engine),关注公号不错过每一篇干货. 数智物语(公众号ID:decision_engine)出品 策划.编写:卷毛雅各布 「我们相信,在垃圾 ...
- 41.Odoo产品分析 (四) – 工具板块(10) – 问卷(1)
查看Odoo产品分析系列--目录 在该模块下,可以创建问卷,收集答案,打印统计. 安装"问卷"模块,首页显示当前各个阶段中的问卷: 打开"开发者模式",能对 ...
- git 更新分支的信息
假如服务器的某个分支删除了,但是本地通过git branch -av还是可以看得到,感觉很烦,通过以下命令就可以更新分支的情况. git fetch origin --prune
- Greenplum扩容
Greenplum支持原有主机扩展Segment个数.新增主机.和混合扩展 本文以在已有机器上扩展节点为例 1.可按照hostname:address:port:fselocation:dbid:co ...
- 【转载】IIC SPI UART串行总线
一.SPISPI(Serial Peripheral Interface,串行外设接口)是Motorola公司提出的一种同步串行数据传输标准,在很多器件中被广泛应用. 接口SPI接口经常被称为4线串行 ...
- c/c++浮点数在内存中存储方式
转自:https://www.cnblogs.com/dolphin0520/archive/2011/10/02/2198280.html 任何数据在内存中都是以二进制的形式存储的,例如一个shor ...