LoopBack

ES6的出品为JS成为企业级语言扫清障碍,与之配套的需要一个真正的企业级框架。Express是一个精巧的微内核不足以支撑起一个大项目。
LoopBack 是建立在 Express 基础上的企业级 Node.js 框架,这个框架有以下特点:

  • 编写少量代码就能创建动态端到端的 REST API
  • 支持主流的数据源,例如 Mongodb、MySQL、SOAP 等和 REST API 的数据。
  • 一致化的模型关系和对 API 访问的权限控制
  • 可使用内置的用于移动应用场景下的地理定位、文件服务以及消息推送
  • 提供 Android、iOS 和 JavaScript 的 客户端SDK

LoopBack 作为一个面向企业级的 Web 框架,提供了更丰富的功能,方便添加模型,权限控制,连接数据源等操作。例如可以通过修改配置增加模型,并指定模型的数据源。它默认提供了一些基础模型,例如 User 这个模型包含了注册登录等逻辑,可以非常方便的继承这些内建模型,实现个性化的定制。它还提供了 Hook 编程的机制,提供了可视化的调试页面,自动生成对应的前端 SDK 。这些功能在开发大型 Web 服务的时候,帮助我们更容易查看和管理项目。

StrongLoop 是 IBM 的一家子公司,StrongLoop 的 LoopBack 框架能够轻松地连接数据并将数据公开为 REST 服务。它能够在图形(或命令行)界面中以可视方式创建数据模型,可以使用这些模型自动生成 REST API – 从而为 REST 服务层生成 CRUD 操作,无需编写任何代码。这很重要?因为它使 API 开发变得更容易,并显著减少了从概念到实现所需的时间。

在 LoopBack 框架中,是通过所谓的“数据连接器”来获取和持久化数据。

Cloudant 是一个 DBaaS 服务,是一个衍生的 Apache CouchDB,所以将 LoopBack 连接器连接到 Cloudant 很简单,只需在应用程序中使用现有的社区驱动的 CouchDB LoopBack Connector。

StrongLoop 是生成 LoopBack 框架的工具程序,有的认为Strongloop 相当于Loopback + Angularjs。

相关文章

node 框架 LoopBack 教程
https://cnodejs.org/topic/57e5b2859c495dce044f397c
StrongLoop学习笔记(一)
https://blog.csdn.net/sanpo/article/details/45082089
使用Loopback3.0构建应用程序(一)
https://www.jianshu.com/p/763b1a847d2c
IBM developerWorks,可以其中搜索Strongloop
https://www.ibm.com/developerworks/cn/
Loopback 2.0
http://loopback.io/doc/en/lb2/Remote-methods.html#overview
Loopback 3.8.0
http://apidocs.strongloop.com/loopback/#model-remotemethod
http://apidocs.strongloop.com/loopback/#persistedmodel-create
https://loopback.io/doc/en/lb3/Remote-methods.html
find方法中Filter的where中的语法格式:
http://loopback.io/doc/en/lb2/Where-filter.html#where-clause-for-queries

使用loopback做邮箱验证,LoopBack用户模型提供了注册新用户并确认其电子邮件地址的方法。您还可以使用loopback-component-passport模块将登录信息与Facebook,Google和其他第三方提供商集成。
https://www.jianshu.com/p/21b4d84fb7af

生成lb-services.js

Loopback生成lb-services.js,使用Loopback CLI工具的lb-ng命令

$ lb-ng server.js lbservices/lb-services.js

代码示例

//自定义方法
Unit.remoteMethod(
        'deleteRecursive',
        {
            description:'',
            accessType: 'WRITE',
            accepts: {arg: 'id', type: 'string',description: '机组 id', http: {source: 'query'}},
            returns: [{arg: 'status', type: 'string'}],
            http: [
                {verb: 'delete', path: '/deleteRecursive'}
            ]
        }
    );
#source的选择有 body,path,query
//实现包装find的功能
    Project.findProjects=function(filter,callback) {
        console.log("findProjects",filter);
        var json=JSON.parse(filter);
        Project.find(json,function (err,data) {
            callback(null,data);
        });
    }
    Project.remoteMethod('findProjects', {
            description:'查询工程列表',
            accessType: 'READ',
            accepts: {arg: 'filter', type: 'string',description: '查询条件', http: {source: 'query'}},
            returns: {type: 'array', root: true},
            http: [
                {verb: 'get', path: '/findProjects'}
            ]
        }
    );

Node应用

REPL

REPL(Read Eval Print Loop:交互式解释器)

Node 自带了交互式解释器,可以很好的调试 Javascript 代码,可以执行以下任务:

读取 – 读取用户输入,解析输入了Javascript 数据结构并存储在内存中。
执行 – 执行输入的数据结构
打印 – 输出结果
循环 – 循环操作以上步骤直到用户两次按下 ctrl-c 按钮退出。

http://www.runoob.com/nodejs/nodejs-repl.html

nodejs基础

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型,使其轻量又高效。V8引擎速度非常快,不用担心多线程,锁,并行计算产生的问题。
如果你编码不认真,java会给你丘,而nodejs会给你很多坑,使用WebStorm开发有的错误会报错,有的错误则会运行通过,产生意料之外的行为。

node擅长处理io密集型业务,在处理CPU密集计算的时候可选择以下方案:
1.node业务逻辑都以单线程进行处理,那如果其中包括耗时的复杂计算,可以以rpc 的方式让其他编译型语言处理,node 只负责搬运。 也就是说node负责接受请求、执行io、发送回复,其他逻辑由擅长计算的语言处理。
2.业务逻辑交给node,便于快速开发。其他内容交给其他语言,或者直接交给c++,用node-gyp编译一下。

同步一般只在基础框架的启动时使用,用来加载配置文件、初始化程序等。

Node.js Manual & Documentation
http://nodejs.cn/api/
http://shouce.qdfuns.com/nodejs/index.html
https://www.cnblogs.com/chaojidan/p/4126894.html
http://www.runoob.com/nodejs/nodejs-tutorial.html

node的构架
主要分为三层:应用app >> V8及node内置架构 >> 操作系统。
V8是node运行的环境,可以理解为node虚拟机。node内置架构又可分为三层: 核心模块(javascript实现) >> c++绑定 >> libuv + CAes + http.

事件循环

Node采用的是单线程的处理机制(所有的I/O请求都采用非阻塞的工作方式),至少从Node.js开发者的角度是这样的。而在底层,Node.js借助libuv来作为抽象封装层,从而屏蔽不同操作系统的差异,Node可以借助libuv来实现线程。Libuv库负责Node API的执行。它将不同的任务分配给不同的线程,形成一个事件循环,以异步的方式将任务的执行结果返回给V8引擎。
event loop其实就是一个事件队列,先加入先执行,执行完一次队列再次循环遍历看有没有新事件加入队列。执行中的叫IO events, setImmediate是在当前队列立即执行,setTimout/setInterval是把执行定时到下一个队列,process.nextTick是在当前执行完下次遍历前执行。所以总体顺序是: IO events >> setImmediate >> setTimeout/setInterval >> process.nextTick

node核心模块
有EventEmitter, Stream, FS, Net和全局对象
全局对象有process, console, Buffer和exports

EventEmitter

EventEmitter是node中一个实现观察者模式的类,主要功能是监听和发射消息,用于处理多模块交互问题。EventEmitter典型应用有:1) 模块间传递消息 2) 回调函数内外传递消息 3) 处理流数据,因为流是在EventEmitter基础上实现的. 4) 观察者模式发射触发机制相关应用。
实现一个EventEmitter分三步:定义一个子类,调用构造函数,继承EventEmitter

    var util = require('util');
    var EventEmitter = require('events').EventEmitter;

    function MyEmitter() {
        EventEmitter.call(this);
    } // 构造函数

    util.inherits(MyEmitter, EventEmitter); // 继承

    var em = new MyEmitter();
    em.on('hello', function(data) {
        console.log('收到事件hello的数据:', data);
    }); // 接收事件,并打印到控制台
    em.emit('hello', 'EventEmitter传递消息真方便!');

捕获EventEmitter的错误事件,监听error事件即可。如果有多个EventEmitter,可以用domain来统一处理错误事件。

    var domain = require('domain');
    var myDomain = domain.create();
    myDomain.on('error', function(err){
        console.log('domain接收到的错误事件:', err);
    }); // 接收事件并打印
    //run中隐式的绑定事件分发器
    myDomain.run(function(){
        var emitter1 = new MyEmitter();
        emitter1.emit('error', '错误事件来自emitter1');
        emitter2 = new MyEmitter();
        emitter2.emit('error', '错误事件来自emitter2');
    });

EventEmitter中的newListenser事件可以用来做事件机制的反射,事件管理等。当任何on事件添加到EventEmitter时,就会触发newListener事件。

定时功能
setTimeout/clearTimeout、setInterval/clearInterval、setImmediate/clearImmediate、process.nextTick

Stream
stream是基于事件EventEmitter的数据管理模式。
Stream典型应用有:文件,网络,数据转换,音频视频等。

调节node执行单元的内存大小,用–max-old-space-size 和 –max-new-space-size 来设置 v8 使用内存的上限。

启动调试node程序:node –debug app.js 和 node-inspector

风格

错误优先的回调函数(Error-First Callback)同时返回错误和数据。第一个参数返回错误,并且验证它是否出错;其他参数返回数据。错误优先的回调函数用于传递错误和数据。第一个参数始终应该是一个错误对象, 用于检查程序是否发生了错误。其余的参数用于传递数据。

避免回调地狱

模块化:将回调函数分割为独立的函数。
流程控制库: 例如async
generators结合Promise模块:
Q模块:
co库:使用yield
async/await

流程控制库 async

安装async:npm install async –save,其中–save参数会在工程package.json中dependencies中增加一条依赖。
async主要实现了三个部分的流程控制功能:
集合: Collections
流程控制: Control Flow
工具类: Utils
async不仅适用在node.js里,浏览器中也可以使用。

Nodejs异步流程控制Async

Async提供了很多针对集合的函数,简化对集合的异步操作步骤
https://my.oschina.net/chinacaptain/blog/469810

常用的流程控制:
parallel 并行
series 串行,保证顺序,不依赖
waterfall 依赖串行、瀑布流,在流程之间需要传递结果时使用。
auto 并行加依赖串行
apply 给函数预置第一个参数,然后返回一个新的函数。

async.series({
	one: function(callback){
		callback(null, 1);
	},
	two: function(callback){
		callback(null, 2);
	}
},function(err, results) {
	console.log(results);
});
或者
async.series([
            function(callback){
                callback(null, 1);
            },
            function(callback){
                callback(null, 2);
            }
        ],function(err, results) {
            console.log(results);
        });

async对数据的操作:
async.map 异步执行多个数组,返回结果数组;async.filter异步过滤多个数组,返回结果数组。

Promise

nodejs标准的回调模式在我们处理请求时需要同时提供回调函数,如果有很多个回调就会形成callback瀑布级嵌套回调,或者称为回调陷阱。 Promise能够解决这种问题,允许低层代码创建一个request然后返回一个对象,其代表着未完成的操作,让调用者去决定应该加入什么回调。promise是一个异步编程的抽象,它是一个返回值或抛出exception的代理对象,一般promise对象都有一个then方法,这个then方法是我们如何获得返回值(成功实现承诺的结果值,称为fulfillment)或抛出exception(拒绝承诺的理由,称为rejection),then是用两个可选的回调作为参数,我们可以称为onFulfilled和OnRejected。.then()总是返回一个新的promise,被.then()返回的promise是一个新的promise,它不同于那些.then()被调用的promise,最后调用.then()可能代表失败,那么如果你不捕获这种失败,那么容易导致你的错误exception消失。

Promise.reduce 方法才是一个顺序执行 Promise 的方法,一些顺序执行的方法,比如 Promise.mapSeries 和 Promise.each 都是基于 Promise.reduce 实现的,这里的 reduce 和 Array.reduce 一样。

Promise 函数依靠链式then操作的特性和catch exception 的函数,可以很好的避免函数的callback hell。但还是需要把各个逻辑分别封装在不同的then() 函数中,每一个函数都有自己独特的作用域。如果要共用某个常量或是变量还要把它定义在函数外,Generator 函数最早实现了异步函数同步化的功能。

可以使用 process.on(‘unhandledRejection’,err) 这个工具,它会打印出所有未处理的 reject 状态的 Promise。

co库

借着ES6的Generator迭代器,TJ大神写出了co 库最早实现异步编程同步化的功能。通过co(function *(){})可以使函数内部通过迭代器来控制。而co则是充当了启动器的角色。

async/await

ES7的async/await的出现为实现异步函数操作提供了另一种方式,await 关键字的作用与generator 函数的yield 作用相类似。至此Nodejs无缝链接async函数,Nodejs异步的难题才算是取得了里程碑式的进展。

环境变量 get-env

#app.js中代码:
app.env = require('get-env')({
  init_env1:'init_env1',
  init_env2:'init_env2',
});

#根据以上代码,在命令行中调用时有效的NODE_ENV值有:dev,prod,init_env1,init_env2
export NODE_ENV=init_env1
node app.js 

传递参数

例如:node const-generate.js cx-kqy.txt
process.argv的值相当于是把整个命令按空格分割成字符串数组。

var arguments = process.argv.splice(2);
console.log('所传递的参数是:', arguments);
process.argv.forEach(function (val, index, array) {
    console.log(index + ': ' + val);
});

node端的缓存策略

如果缓存数据量不大,不需要多进程共享,可以node-cache方案或自己实现。如果需要多node.js进程共享并持久化,可以考虑用redis来实现,Redis的优势在于能够供多进程共享,有完善的备份和恢复机制。

child-process

node是异步非阻塞的,这对高并发非常有效。还有其它一些常用需求,比如和操作系统shell命令交互、调用可执行文件、创建子进程进行阻塞式访问或高CPU计算等,child-process就是为满足这些需求而生。child-process顾名思义就是把node阻塞的工作交给子进程去做。

    var cp = require('child_process');
    var child = cp.spawn('echo', ['你好', "钩子"]); // 执行命令
    child.stdout.pipe(process.stdout); // child.stdout是输入流,process.stdout是输出流,这句的意思是将子进程的输出作为当前程序的输入流,然后重定向到当前程序的标准输出,即控制台。

exec、execFile、spawn和fork:exec可以用操作系统原生的方式执行各种命令,如管道 cat ab.txt | grep hello; execFile是执行一个文件; spawn是流式和操作系统进行交互; fork是两个node程序(javascript)之间时行交互。
用fork两个node程序之间交互,原理是子程序用process.on、process.send,父程序里用child.on、child.send进行交互.

    1) fork-parent.js
var cp = require('child_process');
var child = cp.fork('./fork-child.js');
child.on('message', function(msg){
console.log('老爸从儿子接受到数据:', msg);
});
child.send('我是你爸爸,送关怀来了!');

2) fork-child.js
process.on('message', function(msg){
console.log("儿子从老爸接收到的数据:", msg);
process.send("我不要关怀,我要银民币!");
});

Node.js模块

Node.js模块分为核心模块和文件模块;
核心模块是Node.js标准API中提供的模块,可以直接通过require获取;文件模块是存储为单独的文件的模块,可以是javascript代码、Json或编译好的C/C++代码。
核心模块拥有最高的加载优先级,如果有模块与其明明冲突,Node.js总是加载核心模块;
文件模块如果不显式指定文件模块扩展名,则会按照.js、.json、.node的顺序加上扩展名;
文件模块的加载有两种方式,一种是按路径加载,一种是查找node_modules文件夹;
文件模块按路径加载又分为按相对路径加载和按绝对路径加载两种;

加载顺序:
优先加载核心模块;
以路径形式加载文件模块:
① 如果显示指定文件模块扩展名,则在该路径下查找该模块是否存在;
② 如果未显示指定文件模块扩展名,则在该路径下,依次查找以.js、.json、.node为扩展名的模块是否存在;
既不是核心模块,又不是以路径形式表示的模块,则首先在当前目录的node_modules目录中查找该模块是否存在,若不存在则继续在其父目录的node_modules目录中查找,反复执行这一过程直到根目录为止。

Nodejs开发

测试

stub 桩代码(stub)就是在某些组件或模块中,模拟某些功能的代码。桩代码(stub)的作用是占位,让代码在测试过程中顺利运行。stub是用于模拟一个组件或模块的函数或程序,只用于模块的行为。测试时,stub可以为函数调用返回模拟的结果。可以用stub去模拟一个方法,从而避免调用真实的方法,使用stub还可以返回虚构的结果,可以配合断言使用stub。在单元测试中,Stub是完全模拟一个外部依赖,而Mock常用来判断测试通过还是失败。
用stub模拟一个读取文件:

  
 var fs = require('fs');
      var writeFileStub = sinon.stub(fs, 'writeFile', function(path, data, cb)

      {

          return cb(null);

      });

      expect(writeFileStub).to.be.called;

      writeFileStub.restore();

jasmine测试框架

测试金字塔
测试HTTP接口用例时,底层的单元测试应该远比上层的端到端测试要多。
很多单元测试,分别测试各个模块(依赖需要stub)
较少的集成测试,测试各个模块之间的交互(依赖不能stub)
少量端到端测试,去调用真正地接口(依赖不能stub)

socket.connect参数顺序

创建TCP客户端的坑之connect的参数顺序是先port再host:

var net = require('net');
var HOST = '127.0.0.1';
var PORT = 8888;
var client = new net.Socket();
client.connect(PORT, HOST, function() {
    console.log('CONNECTED TO: ' + HOST + ':' + PORT);
    // 建立连接后立即向服务器发送数据,服务器将收到这些数据 
    client.write('message');
});

pm2

pm2 = P (rocess) M (anager)2,是可以用于生产环境的Nodejs的进程管理工具,它内置一个负载均衡,可以保证服务不会中断一直在线,并且提供0秒reload功能,还有其他一系列进程管理、监控功能。

pm2的安装和使用

npm install -g pm2
pm2 start app.js
pm2 save
pm2 startup
pm2 reload all
pm2 list
pm2 restart [id]

~# ls -a显示所有包括隐藏的文件,可以看到.pm2文件夹

更多pm2常用的命令用法介绍
http://i5ting.github.io/node-deploy-practice/
http://www.111cn.net/sys/linux/120062.htm
http://www.pangjian.info/2016/12/02/deploy-nodejs-pm2-1/?utm_medium=referral

缓存

nodejs通过文件名缓存加载过的模块,这里是全局缓存,不是页面内缓存。

grpc

安装grpc-tools
$ sudo npm install grpc-tools -g
把proto文件生成js文件
$ /usr/local/lib/node_modules/grpc-tools/bin/protoc --js_out=import_style=commonjs,binary:. --grpc_out=. --plugin=protoc-gen-grpc=/usr/local/lib/node_modules/grpc-tools/bin/grpc_node_plugin *.proto

不同操作系统平台下需要重新下载grpc,即重新执行 npm install grpc

http参数

参数名是区分大小写的,不存在的参数值是undefined
取get地址?后面的传参
var id=req.query.id;
取rest路径中的参数
var id = req.params.id;
取post时json对象中的参数
id=req.body.id;

http响应内容重写

router.get('/', function(req, res, next) {
    var content=getNowFormatDate();
    //重写,去掉了其它的头
    res.connection.write('HTTP/1.1 200 \r\n\r\n' + content, 'ascii', function(){
        res.connection.emit('end');
    });
    
    //res.send(content);
});

function getNowFormatDate() {
    var date = new Date();
    var seperator1 = "-";
    var seperator2 = ":";
    var currentdate = date.getFullYear() + seperator1 + addZero(date.getMonth() + 1) + seperator1 + addZero(date.getDate())
        + " " + addZero(date.getHours()) + seperator2 + addZero(date.getMinutes()) + seperator2 + addZero(date.getSeconds());
    return currentdate;
}

function addZero(num) {
    if (num >= 0 && num <= 9) {
        num = "0" + num;
    }
    return num;
}

express框架

express项目的目录结构:app.js, package.json, bin/www, public, routes, views。
express常用函数:express.Router路由组件,app.get路由定向,app.configure配置,app.set设定参数,app.use使用中间件。

如何保证你的 cookie 安全?如何阻止 XSS 攻击?
XSS 攻击是指攻击者向Html页面里插入恶意 JavaScript 代码。

为了防止攻击需要对 HTTP header 里的 set-cookie 进行处理:
HttpOnly – 这个属性帮助防止跨站脚本攻击,它禁止通过 JavaScript 访问 cookie。
secure – 这个属性告诉浏览器,仅允许通过 HTTPS 协议访问 cookie。
所以需要做的是在请求头里写 Set-Cookie: sid=; HttpOnly。
若使用 Express 框架,可以使用 express-cookie-session组件,它会默认做出上述防止 XSS 攻击的设置。

express超时重复请求

3种解决办法
1.httpserver设置超时函数
var app = express();
var http = require(‘http’);
var server = http.createServer(app);
server.on(‘timeout’, onTimeout);
function onTimeout(err) {
}
2.加大超时时间
server.timeout=10*60*1000;
3.request或response设置超时处理方法
router.post(‘/xxx’,function(req,res,next){
req.setTimeout(240000,function () {
});
}
http://blog.csdn.net/puncha/article/details/9378197

socket超时

var socket = new net.Socket(); 创建后
socket.setTimeout(10 * 1000, function () {
if(!idle) {
console.log(‘timeout not idle’);
if (callback) {
callback(false, {message: ‘请求超时’});
callback = null;
}
}else{
console.log(‘timeout idle’);
}
});
不执行connect或write,10秒后会打印timeout idle。
等待9秒后再执行connect,如果connect很久,会在connect开始10秒后打印timeout not idle。
即创建socket和connect或write会开始新一轮超时计时。但不能多次设置settimeout,会引发emit数量警告。
idle为自定义的状态,在connect或wirte时设置idle=false。

安装最新稳定版本
https://segmentfault.com/a/1190000007542620

淘宝NPM镜像CNPM
http://npm.taobao.org/
npm install -g cnpm –registry=https://registry.npm.taobao.org

node中使用redis

    var redis = require("redis"),
    client = redis.createClient();

    client.set("foo_rand000000000000", "some fantastic value");
    client.get("foo_rand000000000000", function (err, reply) {
        console.log(reply.toString());
    });
    client.end();

时序攻击(timing attack)

时序攻击属于侧信道攻击/旁路攻击(Side Channel Attack),侧信道攻击是指利用信道外的信息,比如加解密的速度/加解密时芯片引脚的电压/密文传输的流量和途径等进行攻击的方式,一个词形容就是“旁敲侧击”。
举一个最简单的计时攻击的例子,某个函数负责比较用户输入的密码和存放在系统内密码是否相同,如果该函数是从第一位开始比较,发现不同就立即返回,那么通过计算返回的速度就知道了大概是哪一位开始不同的,这样就实现了电影中经常出现的按位破解密码的场景。密码破解复杂度成千上万倍甚至百万千万倍的下降。
最简单的防御方法是:“发现错误的时候并不立即返回,而是设一个标志位,直到完全比较完两个字符串再返回”。
时序攻击并非是一种理论攻击方法,OpenSSL、OpenSSH等应用都曾经有时序攻击漏洞

在进行数据校验(security credentials)时,避免任何信息泄露是最重要的。所以,我们要控制数据校验的执行时间。我们要保证,不管传过来的数据是什么,我们校验数据消耗的时间是相同的。如果你做不到这一点,程序对时序攻击的抵抗力很低。Node.js 使用 JavaScript V8 引擎, 为了高速运行网页而开发的,性能优异。V8引擎比较数据的方式是字节比较, 一旦发现有一个字节不一致, 比较运算就会停止。 因此,攻击者判断传入的 password 校验时间,核验时间越长说明 password 正确的部分越多。要修复这个问题, 可以使用 npm 模块 cryptiles。cryptiles.fixedTimeComparison()。

UDP / Datagram Sockets 数据报套接字模块 和 Net模块

在线文档
http://shouce.qdfuns.com/nodejs/dgram.html
http://shouce.qdfuns.com/nodejs/net.html
http://www.runoob.com/nodejs/nodejs-net-module.html

var socket = dgram.createSocket('udp4');创建的udp socket有close方法,没有destroy方法。
var socket = new net.Socket();创建的tcp socket没有close方法,有destroy方法。

cocos2d-x中node.getContentSize()的问题

在android真机中测试发现,如果没有setContentSize,调用Layer,Node的子类的getContentSize()值有可能是小于等于0的,这导致程序异常。