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的更多相关文章

  1. koa : Express出品的下一代基于Node.js的web框架

    https://www.liaoxuefeng.com/wiki/001434446689867b27157e896e74d51a89c25cc8b43bdb3000/001434501579966a ...

  2. 强大的 Node.js Web 框架 - Daze.js

    去年年初对 Node.js 比较感兴趣,也用了很多 Node.js 的框架,但是开发体验不是特别好,我之前也是后端转前端,然后再接触 Node.js ,所以用过挺多的服务端框架,相对js而言,设计一款 ...

  3. 7个Node.js的Web框架

    NodeJS也就是Node,是众所周知的使用javascript构建Web应用框架,它启动一个服务器非常简单,如下: var http = require('http'); http.createSe ...

  4. js基础系列框架:JS重要知识点(转载)

    这里列出了一些JS重要知识点(不全面,但自己感觉很重要).彻底理解并掌握这些知识点,对于每个想要深入学习JS的朋友应该都是必须的. 讲解还是以示例代码搭配注释的形式,这里做个小目录: JS代码预解析原 ...

  5. 150行代码搭建异步非阻塞Web框架

    最近看Tornado源码给了我不少启发,心血来潮决定自己试着只用python标准库来实现一个异步非阻塞web框架.花了点时间感觉还可以,一百多行的代码已经可以撑起一个极简框架了. 一.准备工作 需要的 ...

  6. Express 4.x Node.js的Web框架

    为了防止无良网站的爬虫抓取文章,特此标识,转载请注明文章出处.LaplaceDemon/SJQ. http://www.cnblogs.com/shijiaqi1066/p/3821150.html ...

  7. Express 4.x Node.js的Web框架----《转载》

    本文使用node.js v0.10.28 + express 4.2.0 1 Express概述 Express 是一个简洁而灵活的node.js的MVC Web应用框架,提供一系列强大特性创建各种W ...

  8. [Node.js]Express web框架

    摘要 Express是一个简洁灵活的node.js web应用框架,提供了一系列强大特性帮助你创建各种web应用和丰富的http工具.使用express可以快速创建一个完整功能的网站. Express ...

  9. 基于node.js的web框架express

    1.安装node.js方法: window :https://nodejs.org/en/ linux:http://www.runoob.com/nodejs/nodejs-install-setu ...

随机推荐

  1. 【学习笔记】tensorflow图片读取

    目录 图像基本概念 图像基本操作 图像基本操作API 图像读取API 狗图片读取 CIFAR-10二进制数据读取 TFRecords TFRecords存储 TFRecords读取方法 图像基本概念 ...

  2. splay详解(一)

    前言 Spaly是基于二叉查找树实现的, 什么是二叉查找树呢?就是一棵树呗:joy: ,但是这棵树满足性质—一个节点的左孩子一定比它小,右孩子一定比它大 比如说 这就是一棵最基本二叉查找树 对于每次插 ...

  3. spring注解的相关配置

    一.<context:annotation-config> 和 <context:component-scan> <context:annotation-config&g ...

  4. 惊喜,重磅福利!免费开源ERP-企业信息化金矿

    Odoo,以前叫OpenERP,是比利时Odoo S.A.公司开发的一个企业应用软件套件,开源套件包括一个企业应用快速开发平台,以及几千个Odoo及第三方开发的企业应用模块.Odoo适用于各种规模的企 ...

  5. 为你揭秘知乎是如何搞AI的——窥大厂 | 数智方法论第1期

    文章发布于公号[数智物语] (ID:decision_engine),关注公号不错过每一篇干货. 数智物语(公众号ID:decision_engine)出品 策划.编写:卷毛雅各布 「我们相信,在垃圾 ...

  6. 41.Odoo产品分析 (四) – 工具板块(10) – 问卷(1)

    查看Odoo产品分析系列--目录 在该模块下,可以创建问卷,收集答案,打印统计.  安装"问卷"模块,首页显示当前各个阶段中的问卷:  打开"开发者模式",能对 ...

  7. git 更新分支的信息

    假如服务器的某个分支删除了,但是本地通过git branch -av还是可以看得到,感觉很烦,通过以下命令就可以更新分支的情况. git fetch origin --prune

  8. Greenplum扩容

    Greenplum支持原有主机扩展Segment个数.新增主机.和混合扩展 本文以在已有机器上扩展节点为例 1.可按照hostname:address:port:fselocation:dbid:co ...

  9. 【转载】IIC SPI UART串行总线

    一.SPISPI(Serial Peripheral Interface,串行外设接口)是Motorola公司提出的一种同步串行数据传输标准,在很多器件中被广泛应用. 接口SPI接口经常被称为4线串行 ...

  10. c/c++浮点数在内存中存储方式

    转自:https://www.cnblogs.com/dolphin0520/archive/2011/10/02/2198280.html 任何数据在内存中都是以二进制的形式存储的,例如一个shor ...