位置:首页 > 其他技术 > Node.js在线教程 > Node.js快速入门

Node.js快速入门

Node.js是什么?

Node.js是建立在谷歌Chrome的JavaScript引擎(V8引擎)的Web应用程序框架。 它的最新版本是:v0.12.7(在编写本在线教程时的版本)。Node.js在官方网站的定义文件内容如下:

Node.js® is a platform built on Chrome's JavaScript runtime for easily building fast, scalable network applications. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient, perfect for data-intensive real-time applications that run across distributed devices.

Node.js自带运行时环境可在Javascript脚本的基础上可以解释和执行(这类似于JVM的Java字节码)。这个运行时允许在浏览器以外的任何机器上执行JavaScript代码。由于这种运行时在Node.js上,所以JavaScript现在可以在服务器上并执行。

Node.js还提供了各种丰富的JavaScript模块库,它极大简化了使用Node.js来扩展Web应用程序的研究与开发。

Node.js = 运行环境+ JavaScript库

Node.js特性

  • Node.js库的异步和事件驱动的API全部都是异步就是非阻塞。它主要是指基于Node.js的服务器不会等待API返回的数据。服务器移动到下一个API调用,Node.js发生的事件通知机制后有助于服务器获得从之前的API调用的响应。

  • 非常快的内置谷歌Chrome的V8 JavaScript引擎,Node.js库代码执行是非常快的。

  • 单线程但高度可扩展 - Node.js使用具有循环事件单线程模型。事件机制有助于服务器在一个非阻塞的方式响应并使得服务器高度可扩展,而不是创建线程限制来处理请求的传统服务器。Node.js使用单线程的程序,但可以提供比传统的服务器(比如Apache HTTP服务器)的请求服务数量要大得多。

  • 没有缓冲 - Node.js的应用从来不使用缓冲任何数据。这些应用只是输出数据在块中。

  • 许可证协议 - Node.js 在 MIT 协议 下发布

都有谁在使用Node.js?

以下是包含正在使用node.js的项目,应用和公司,一个详尽的清单请点击 github维基链接查看,这些清单里包括:eBay, General Electric, GoDaddy, Microsoft, PayPal, Uber, Wikipins, Yahoo!, Yammer 并越来越多加入继续扩大这个列表:使用NodeJS的项目, 应用和公司

概念

下图描述了 Node.js 的一些重要组成部分,我们将详细的在后续章节进行讨论。


Node.js Concepts

在哪里可以使用Node.js?

以下是Node.js证明自己完美的技术的合作伙伴的领域。

  • I/O 绑定应用程序

  • 数据流应用

  • 数据密集型实时应用(DIRT)

  • JSON API的应用程序

  • 单页面应用

在哪些地方不要使用Node.js?

不建议使用Node.js的就是针对CPU密集型应用。

Node.js - 环境安装配置

如果愿意安装设置Node.js环境,需要计算机上提供以下两个软件:

一、文本编辑器

二、Node.js二进制安装包

文本编辑器

这将用来编写程序代码。 一些编辑器包括:Windows记事本,OS Edit命令,Brief, Epsilon, EMACS和VIM或vi。

文本编辑器的名称和版本的在不同的操作系统可能不太相同。例如,记事本可用在Windows,VIM或vi可以在Windows以及Linux或UNIX上使用。

编辑器创建的文件称为源文件并包含程序的源代码。 对于Node.js的程序的源文件名通常命名扩展是 ".js".

在开始编程之前,请确保文本编辑器可用,并且有足够的经验来写计算机程序,将其保存在一个文件,如:helloworld.js,编译并执行。

Node.js 运行时

编写源文件中的源代码只需知道了解JavaScript。Node.js解释器将用于解释和执行JavaScript代码。

Node.js的发行版以二进制安装在SunOS,Linux,Mac OS X和Windows的32位(386)和64位(AMD64)的x86处理器架构操作系统。

以下部分将指导如何将Node.js的二进制分发版安装在各种操作系统上。

下载Node.js的归档文件

下载最新版本Node.js的安装归档文件在: Node.js 下载. 在写这篇在线教程的时候,下载的是 node-v0.12.7-x64.msi 并复制到“桌面”。

OS Archive name
Windows node-v0.12.7-x64.msi
Linux node-v0.12.7-linux-x86.tar.gz
Mac node-v0.12.7-darwin-x86.tar.gz
SunOS node-v0.12.7-sunos-x86.tar.gz

在UNIX/Linux/Mac OS X和SunOS上安装(可选)

解压缩下载归档文件到 /usr/local, 创建一个NodeJs树 /usr/local/nodejs. 例如:

tar -C /usr/local -xzf node-v0.12.0-linux-x86.tar.gz

添加 /usr/local/nodejs 到PATH环境变量。

OS 输出
Linux export PATH=$PATH:/usr/local/nodejs
Mac export PATH=$PATH:/usr/local/nodejs
FreeBSD export PATH=$PATH:/usr/local/nodejs

在Windows上安装Node.js(本在线教程中使用)

使用MSI文件,并按照提示安装node.js,默认情况下,安装程序将 Node.js 发行到 C:\Program Files\nodejs. 但这里我们需要修改安装路径到:D:\yiibai_worksp\nodejs,并将 D:\yiibai_worksp\nodejs 目录添加到Window系统的PATH环境变量中。重新启动打开命令提示符,以使更改生效。

第一步:双击下载的  “node-v0.12.7-x64.msi" 文件,出现提示安装界面:

第二步:选择安装目录,这里安装在目录 D:\yiibai_worksp\nodejs 中,如下图:

第三步:选择安装的模块和功能,这里全部安装,并添加到系统环境变量,如下图所示:

最后一步:安装完成!

验证安装:执行文件

创建一个js文件名为test.js 在 D:\>yiibai_worksp\nodejs.

File: test.js

console.log("Hello World")

现在运行test.js看到的结果:

D:\yiibai_worksp\nodejs> node test.js

验证输出结果:

Node.js - 第一个应用程序

在使用 Node.js 创建 Hello World ! 应用程序之前, 让我们看看Node.js的应用程序的组成部分。Node.js应用程序由以下三个重要部分组成:

  • 导入所需模块: 使用require指令来加载javascript模块

  • 创建一个服务器: 服务器这将听监听在Apache HTTP服务器客户端的请求。

  • 读取请求并返回响应: 在前面的步骤中创建的服务器将响应读取由客户机发出的HTTP请求(可以是一个浏览器或控制台)并返回响应。

创建Node.js应用

步骤 1:导入所需的包

使用require指令来加载 HTTP 模块。

var http = require("http")

步骤 2:使用http.createServer方法创建HTTP服务器。通过参数函数请求并响应。编写示例实现返回“Hello World”。服务器在8081端口监听。

http.createServer(function (request, response) {
   // HTTP Status: 200 : OK
   // Content Type: text/plain
   response.writeHead(200, {'Content-Type': 'text/plain'});
   // send the response body as "Hello World"
   response.end('Hello World\n');
}).listen(8081);
// console will print the message
console.log('Server running at http://127.0.0.1:8081/');

步骤 3: 创建一个 js 文件在 helloworld.js 在 D:\>yiibai_worksp.

File: helloworld.js

var http = require("http")
http.createServer(function (request, response) {  
   response.writeHead(200, {'Content-Type': 'text/plain'});  
   response.end('Hello World\n');
}).listen(8081);
console.log('Server running at http://127.0.0.1:8081/');

现在运行 helloworld.js 看到结果:

D:\yiibai_worksp\nodejs>node helloworld.js

验证输出,服务器应用已经启动!

Server running at http://127.0.0.1:8081/

向Node.js服务器发出请求

打开浏览器(IE),在地址栏中输入:http://127.0.0.1:8081/ 在浏览器中,看看下面的结果。

Node.js - REPL

REPL代表读取评估和演示打印循环,它就像 Window 下的控制台的计算机环境,或 Unix/Linux 系统的 Shell命令输入响应输出。 Node.js或Node 捆绑了一个REPL环境。可执行以下任务。

  • 读取- 读取用户的输入,解析输入的JavaScript数据结构并存储在内存

  • 计算- 采取并评估计算数据结构

  • 打印- 打印结果

  • 循环 - 循环上面的命令,直到用户按Ctrl-C两次终止

Node 的REPL 与 Node.js 的实验代码非常有用,用于调试JavaScript代码。

特点

REPL可以通过简单地在shell/控制台运行node不带任何参数来启动。

D:\yiibai_worksp\nodejs> node

可以看到REPL 命令提示符:

D:\yiibai_worksp\nodejs> node
>

简单的表达式

让我们尝试在REPL命令提示符下执行简单的数学运算:

D:\yiibai_worksp\nodejs>node
> 1 + 3
4
> 1 + ( 2 * 3 ) - 4
3
>

使用变量

使用变量储存值后并打印。如果不使用var关键字,那么值存储在变量并打印。而如果使用var关键字存储值,那么不会打印。稍后,可以使用这两个变量。使用console.log()打印来任何东西

D:\yiibai_worksp\nodejs> node
> x = 10
10
> var y = 10
undefined
> x + y
20
> console.log("Hello World")
Hello Workd
undefined

多行表达

Node REPL支持类似于JavaScript的多表达。请参阅下列do-while循环:

D:\yiibai_worksp\nodejs> node
> var x = 0
undefined
> do {
... x++;
... console.log("x: " + x);
... } while ( x < 5 );
x: 1
x: 2
x: 3
x: 4
x: 5
undefined
>

... 当按下进入自动打开来后括号。Node自动检查表达式的连续性。

下划线变量

使用_得到最后的结果。

D:\yiibai_worksp\nodejs> node
> var x = 10
undefined
> var y = 20
undefined
> x + y
30
> var sum = _
undefined
> console.log(sum)
30
undefined
>

REPL 命令

  • ctrl + c - 终止当前命令

  • ctrl + c twice - 终止 Node REPL

  • ctrl + d - 终止 Node REPL

  • Up/Down Keys - 查看命令历史记录和修改以前的命令

  • tab Keys - 当前命令列表

  • .help - 列出所有命令

  • .break - 退出多行表达

  • .clear - 从多行表达式退出

  • .save - 当前 Node REPL会话保存到一个文件

  • .load - 加载文件的内容到当前Node REPL会话

D:\yiibai_worksp\nodejs> node
> var x = 10
undefined
> var y = 20
undefined
> x + y
30
> var sum = _
undefined
> console.log(sum)
30
undefined
> .save test.js
Session saved to:test.js
> .load test.js
> var x = 10
undefined
> var y = 20
undefined
> x + y
30
> var sum = _
undefined
> console.log(sum)
30
undefined
>

Node.js - npm

npm 表示节点程序包管理器。npm 提供以下两个主要功能:

  • Node.js包/模块的在线软件仓库,可通过搜索 search.nodejs.org

  • 命令行实用程序安装包,作为Node.js版本管理和依赖包管理。

Node.js v0.6.3版本后才开始捆绑 npm 安装。为了验证,打开控制台,然后输入以下命令并查看结果:

D:\yiibai_worksp\nodejs>npm --version
2.11.3

全局VS本地安装

默认情况下,NPM安装在任何依赖性的本地模式。在这里,本地模式指的是包在安装目录node_modules,位于Node应用的文件夹中。本地部署的包都可以通过require() 访问 。

全局安装的软件包/依赖存储在<用户目录>/npm目录。 这种依赖关系可以在命令行中使用Node.js的(命令行界面)任何功能,但不能使用require()在Node直接应用导入。

我们来开始安装明确,使用本地来安装一个流行的Web框架。

D:\yiibai_worksp\nodejs>npm install express
express@4.11.2 node_modules\express
|-- merge-descriptors@0.0.2
|-- utils-merge@1.0.0
|-- methods@1.1.1
|-- escape-html@1.0.1
|-- fresh@0.2.4
|-- cookie@0.1.2
|-- range-parser@1.0.2
|-- media-typer@0.3.0
|-- cookie-signature@1.0.5
|-- vary@1.0.0
|-- finalhandler@0.3.3
|-- parseurl@1.3.0
|-- serve-static@1.8.1
|-- content-disposition@0.5.0
|-- path-to-regexp@0.1.3
|-- depd@1.0.0
|-- qs@2.3.3
|-- debug@2.1.1 (ms@0.6.2)
|-- send@0.11.1 (destroy@1.0.3, ms@0.7.0, mime@1.2.11)
|-- on-finished@2.2.0 (ee-first@1.1.0)
|-- type-is@1.5.7 (mime-types@2.0.9)
|-- accepts@1.2.3 (negotiator@0.5.0, mime-types@2.0.9)
|-- etag@1.5.1 (crc@3.2.1)
|-- proxy-addr@1.0.6 (forwarded@0.1.0, ipaddr.js@0.1.8)

一旦NPM完成下载,可以通过查看 D:\yiibai_worksp\nodejs\node_modules 的内容验证,或键入以下命令:

D:\yiibai_worksp\nodejs> npm ls
D:\yiibai_worksp\nodejs
|-- express@4.11.2
  |-- accepts@1.2.3
  | |-- mime-types@2.0.9
  | | |-- mime-db@1.7.0
  | |-- negotiator@0.5.0
  |-- content-disposition@0.5.0
  |-- cookie@0.1.2
  |-- cookie-signature@1.0.5
  |-- debug@2.1.1
  | |-- ms@0.6.2
  |-- depd@1.0.0
  |-- escape-html@1.0.1
  |-- etag@1.5.1
  | |-- crc@3.2.1
  |-- finalhandler@0.3.3
  |-- fresh@0.2.4
  |-- media-typer@0.3.0
  |-- merge-descriptors@0.0.2
  |-- methods@1.1.1
  |-- on-finished@2.2.0
  | |-- ee-first@1.1.0
  |-- parseurl@1.3.0
  |-- path-to-regexp@0.1.3
  |-- proxy-addr@1.0.6
  | |-- forwarded@0.1.0
  | |-- ipaddr.js@0.1.8
  |-- qs@2.3.3
  |-- range-parser@1.0.2
  |-- send@0.11.1
  | |-- destroy@1.0.3
  | |-- mime@1.2.11
  | |-- ms@0.7.0
  |-- serve-static@1.8.1
  |-- type-is@1.5.7
  | |-- mime-types@2.0.9
  |   |-- mime-db@1.7.0
  |-- utils-merge@1.0.0
  |-- vary@1.0.0

上面列出的内容很长,这里省略了一部分。

现在,让我们尝试安装明确,使用全局安装流行Web框架。

D:\yiibai_worksp\nodjs> npm install express - g

一旦NPM完成下载,可以通过查看<用户目录>/nmp/node_modules中的内容来验证。或输入以下命令:

D:\yiibai_worksp\nodejs> npm ls -g

以上可能会遇网络问题无法安装,需要翻墙。

安装模块

安装模块很简单,只要键入以下命令。

D:\yiibai_worksp\nodejs> npm install express

现在,可以在 js 文件中使用它,如下:

var express = require('express');

package.json 包

package.json是存在于任何Node应用程序/模块的根目录,并用于定义一个包的属性。 让我们打开 package.json express包在 D:\yiibai_worksp\nodejs\node_modules\express\

{
  "name": "express",
  "description": "Fast, unopinionated, minimalist web framework",
  "version": "4.11.2",
  "author": {
    "name": "TJ Holowaychuk",
    "email": "tj@vision-media.ca"
  },
  "contributors": [
    {
      "name": "Aaron Heckmann",
      "email": "aaron.heckmann+github@gmail.com"
    },
    {
      "name": "Ciaran Jessup",
      "email": "ciaranj@gmail.com"
    },
    {
      "name": "Douglas Christopher Wilson",
      "email": "doug@somethingdoug.com"
    },
    {
      "name": "Guillermo Rauch",
      "email": "rauchg@gmail.com"
    },
    {
      "name": "Jonathan Ong",
      "email": "me@jongleberry.com"
    },
    {
      "name": "Roman Shtylman",
      "email": "shtylman+expressjs@gmail.com"
    },
    {
      "name": "Young Jae Sim",
      "email": "hanul@hanul.me"
    }
  ],
  "license": "MIT",
  "repository": {
    "type": "git",
    "url": "https://github.com/strongloop/express"
  },
  "homepage": "http://expressjs.com/",
  "keywords": [
    "express",
    "framework",
    "sinatra",
    "web",
    "rest",
    "restful",
    "router",
    "app",
    "api"
  ],
  "dependencies": {
    "accepts": "~1.2.3",
    "content-disposition": "0.5.0",
    "cookie-signature": "1.0.5",
    "debug": "~2.1.1",
    "depd": "~1.0.0",
    "escape-html": "1.0.1",
    "etag": "~1.5.1",
    "finalhandler": "0.3.3",
    "fresh": "0.2.4",
    "media-typer": "0.3.0",
    "methods": "~1.1.1",
    "on-finished": "~2.2.0",
    "parseurl": "~1.3.0",
    "path-to-regexp": "0.1.3",
    "proxy-addr": "~1.0.6",
    "qs": "2.3.3",
    "range-parser": "~1.0.2",
    "send": "0.11.1",
    "serve-static": "~1.8.1",
    "type-is": "~1.5.6",
    "vary": "~1.0.0",
    "cookie": "0.1.2",
    "merge-descriptors": "0.0.2",
    "utils-merge": "1.0.0"
  },
  "devDependencies": {
    "after": "0.8.1",
    "ejs": "2.1.4",
    "istanbul": "0.3.5",
    "marked": "0.3.3",
    "mocha": "~2.1.0",
    "should": "~4.6.2",
    "supertest": "~0.15.0",
    "hjs": "~0.0.6",
    "body-parser": "~1.11.0",
    "connect-redis": "~2.2.0",
    "cookie-parser": "~1.3.3",
    "express-session": "~1.10.2",
    "jade": "~1.9.1",
    "method-override": "~2.3.1",
    "morgan": "~1.5.1",
    "multiparty": "~4.1.1",
    "vhost": "~3.0.0"
  },
  "engines": {
    "node": ">= 0.10.0"
  },
  "files": [
    "LICENSE",
    "History.md",
    "Readme.md",
    "index.js",
    "lib/"
  ],
  "scripts": {
    "test": "mocha --require test/support/env --reporter spec --bail --check-leaks test/ test/acceptance/",
    "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --require test/support/env --reporter dot --check-leaks test/ test/acceptance/",
    "test-tap": "mocha --require test/support/env --reporter tap --check-leaks test/ test/acceptance/",
    "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --require test/support/env --reporter spec --check-leaks test/ test/acceptance/"
  },
  "gitHead": "63ab25579bda70b4927a179b580a9c580b6c7ada",
  "bugs": {
    "url": "https://github.com/strongloop/express/issues"
  },
  "_id": "express@4.11.2",
  "_shasum": "8df3d5a9ac848585f00a0777601823faecd3b148",
  "_from": "express@*",
  "_npmVersion": "1.4.28",
  "_npmUser": {
    "name": "dougwilson",
    "email": "doug@somethingdoug.com"
  },
  "maintainers": [
    {
      "name": "tjholowaychuk",
      "email": "tj@vision-media.ca"
    },
    {
      "name": "jongleberry",
      "email": "jonathanrichardong@gmail.com"
    },
    {
      "name": "shtylman",
      "email": "shtylman@gmail.com"
    },
    {
      "name": "dougwilson",
      "email": "doug@somethingdoug.com"
    },
    {
      "name": "aredridel",
      "email": "aredridel@nbtsc.org"
    },
    {
      "name": "strongloop",
      "email": "callback@strongloop.com"
    },
    {
      "name": "rfeng",
      "email": "enjoyjava@gmail.com"
    }
  ],
  "dist": {
    "shasum": "8df3d5a9ac848585f00a0777601823faecd3b148",
    "tarball": "http://registry.npmjs.org/express/-/express-4.11.2.tgz"
  },
  "directories": {},
  "_resolved": "https://registry.npmjs.org/express/-/express-4.11.2.tgz",
  "readme": "ERROR: No README data found!"
}

Package.json 属性

  • name - 包的名称

  • version - 包的版本

  • description - 包的描述

  • homepage - 包的网站主页

  • author - 包的作者

  • contributors - 包的贡献者的名称列表

  • dependencies - 依赖性列表。npm自动安装所有在node_module文件夹中的包提到的依赖关系。

  • repository - 包的存储库类型和URL

  • main - 包的入口点

  • keywords - 关键字

卸载模块

请使用以下命令卸载模块。

D:\yiibai_worksp\nodejs> npm uninstall express

当npm卸载的软件包,可以通过查看<用户目录>/npm/node_modules 的内容验证。或键入以下命令:

D:\yiibai_worksp\nodejs> npm ls

更新模块

更新的package.json并更改其版本依赖关系进行更新,运行以下命令。

D:\yiibai_worksp\nodejs> npm update

搜索模块

使用npm搜索包名。

D:\yiibai_worksp\nodejs>npm search express

创建一个模块

创建模块需要要生成package.json。让我们使用 npm 产生 package.json。

D:\yiibai_worksp\nodejs> npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sane defaults.

See 'npm help json' for definitive documentation on these fields
and exactly what they do.

Use 'npm install <pkg> --save' afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
name: (Nodejs_WorkSpace)

一旦的 package.json 产生。使用一个有效的电子邮件地址在npm库网站上注册自己,使用如面的命令。

D:\yiibai_worksp\nodejs> npm adduser

现在,是时候来发布模块了:

D:\yiibai_worksp\nodejs> npm publish

Node.js - 回调概念

什么是回调?

回调是一种异步相当于一个函数。回调函数被调用在完成既定任务。Node大量使用了回调。Node所有的API写的都是支持回调的这样一种方式。例如,一个函数读取一个文件可能开始读取文件,并立即返回控制到执行环境 使得下一个指令可以马上被执行。一旦文件 I/O 完成,它会调用回调函数,同时传递回调函数,该文件作为参数的内容。因此不会有堵塞或等待文件I/O。这使得Node.js高度可扩展,因此可以处理大量的请求,而无需等待任何函数来返回结果。

阻塞代码示例

创建一个 txt 文件:test.txt 在 D:\>yiibai_worksp\nodejs 目录

Yiibai.Com 

创建一个名为test.js 的js文件在 D:\>yiibai_worksp\nodejs

var fs = require("fs");
var data = fs.readFileSync('test.txt');
console.log(data.toString());
console.log("Program Ended");

现在运行 test.js 看到的结果:

D:\yiibai_worksp\nodejs>node test.js

验证输出结果:

gitbook.net
Program Ended

非阻塞代码示例

创建一个 txt 文件:test.txt 在 D:\>yiibai_worksp\nodejs 目录

Yiibai.Com 

更新 test.js 的内容在目录D:\>yiibai_worksp\nodejs

var fs = require("fs");

fs.readFile('test.txt', function (err, data) {
    if (err) return console.error(err);
    console.log(data.toString());
});
console.log("Program Ended");

现在运行 test.js 看到的结果:

D:\yiibai_worksp\nodejs> node test.js

验证输出

Program Ended
gitbook.net

事件循环概述

Node JS是单线程应用程序,但它通过事件和回调的概念,支持并发。NodeJS的每一个API都是异步的,作为一个单独的线程,它使用异步函数调用来维护并发。Node使用观察者模式。Node线程保持一个事件循环,每当任何任务完成后得到结果,它触发通知事件侦听函数来执行相应的事件。

事件驱动编程

Node.js使用大量事件,这也是为什么Node.js相对于其他类似技术比较快的原因之一。当Node启动其服务器,就可以简单地初始化其变量,声明函数,然后等待事件的发生。

虽然事件似乎类似于回调。不同之处在于当回调函数被调用异步函数返回结果,其中的事件处理工作在观察者模式。监听事件函数作为观察者。每当一个事件被解雇,其监听函数开始执行。Node.js有多个内置的事件。 主要扮演者是 EventEmitter,可使用以下语法导入。

//import events module
var events = require('events');
//create an eventEmitter object
var eventEmitter = new events.EventEmitter();

示例

创建一个 js 文件名为 test.js 的文件在D:\>yiibai_worksp\nodejs

File: test.js

//import events module
var events = require('events');
//create an eventEmitter object
var eventEmitter = new events.EventEmitter();

//create a function connected which is to be executed 
//when 'connection' event occurs
var connected = function connected() {
   console.log('connection succesful.');
  
   // fire the data_received event 
   eventEmitter.emit('data_received.');
}

// bind the connection event with the connected function
eventEmitter.on('connection', connected);
 
// bind the data_received event with the anonymous function
eventEmitter.on('data_received', function(){
   console.log('data received succesfully.');
});

// fire the connection event 
eventEmitter.emit('connection');

console.log("Program Ended.");

现在运行test.js看到的结果:

D:\yiibai_worksp\nodejs> node test.js

验证输出。服务器已经启动

connection succesful.
data received succesfully.
Program Ended.

Node应用程序如何工作?

在Node 应用,任何异步函数接受回调作为最后的参数,并回调函数接受错误作为第一个参数。我们再看一下前面的例子。

var fs = require("fs");

fs.readFile('test.txt', function (err, data) {
    if (err){
	   console.log(err.stack);
	   return;
	}
    console.log(data.toString());
});
console.log("Program Ended");

这里fs.readFile是一个异步函数,其目的是用于读取文件。 如果在文件的读取发生了错误,则err 对象将包含相应的错误,否则data将包含该文件的内容。readFile通过err和data到回调函数后,文件读取操作完成。

Node.js - 事件发射器

EventEmitter类在于事件的模块。它通过通俗易懂的语法如下:

//import events module
var events = require('events');
//create an eventEmitter object
var eventEmitter = new events.EventEmitter();

当EventEmitter实例出现任何错误,它会发出“error”事件。当新的监听器添加,'newListener'事件被触发,当一个监听器被删除,'removeListener“事件被触发。

EventEmitter提供多种属性,如:on和emit。on属性用于绑定与该事件的函数,而 emit 用于触发一个事件。

方法

Sr. No. 方法 描述
1 addListener(event, listener) 指定事件添加一个监听器到监听器数组的结尾。不用检查查看监听器是否已经添加。多个调用传递事件和监听器的相同组合将导致在监听器被多次添加。返回发射器,因此调用可以链接。
2 on(event, listener) 添加一个监听器到指定事件的监听器数组的结尾。没有进行检查以查看是否收听已经添加。多个调用传递事件和监听器的相同的组合将导致监听器被多次添加。 返回发射器,因此调用可以链接。
3 once(event, listener) 增加了一次监听事件。该监听器只被调用在下一次的事件被触发后它将被删除。 返回发射器,因此调用可链接。
4 removeListener(event, listener) 返回发射器,因此调用可链接。注意:改变数组索引的监听器数组在监听器后面。removeListener从监听数组中删除一个监听器的一个实例。如果单个监听器已多次添加到监听器数组中指定的事件,那么removeListener被多次调用删除每个实例。 返回发射器,因此调用可链接。
5 removeAllListeners([event]) 删除所有监听器,或者那些指定的事件。这不是一个好主意,删除监听器在其他地方添加代码,特别是当它在发射器但没有创建(如套接字或文件流)。返回发射器,因此调用可链接。
6 setMaxListeners(n) 默认情况下,如果超过10监听器添加特定事件EventEmitters会打印警告。这是一个有用的默认这有助于发现内存泄漏。显然,并非所有的发射器应限制在10个。此功能可以设置增加。设置为零无限。
7 listeners(event) 返回监听指定事件的数组。
8 emit(event, [arg1], [arg2], [...]) 为了与提供的参数执行每个监听器。如果事件有监听器返回true,否则为false。

类方法

Sr. No. 方法 描述
1 listenerCount(emitter, event) 返回对于一个给定事件的监听器的数量。

事件

Sr. No. 事件名称 参数 描述
1 newListener
  • event - 字符串 - 事件名称

  • listener- 函数-事件处理函数

事件在任何时间发出添加监听器。当该事件被触发时,监听器可能还没有为事件添加到监听器的数组。
2 removeListener
  • event - 字符串事件名称

  • listener- 函数的事件处理函数

事件在任何时候发出删除一个监听器。当该事件被触发时,监听器可能还没有为事件添加到监听器的数组。

示例

创建一个js文件名为test.js,在 D:\>yiibai_worksp\nodejs

File: test.js

var events = require('events');
var eventEmitter = new events.EventEmitter();

//listener #1
var listner1 = function listner1() {
   console.log('listner1 executed.');
}

//listener #2
var listner2 = function listner2() {
  console.log('listner2 executed.');
}

// bind the connection event with the listner1 function
eventEmitter.addListener('connection', listner1);

// bind the connection event with the listner2 function
eventEmitter.on('connection', listner2);

var eventListeners = require('events').EventEmitter.listenerCount(eventEmitter,'connection');
console.log(eventListeners + " Listner(s) listening to connection event");

// fire the connection event 
eventEmitter.emit('connection');

// remove the binding of listner1 function
eventEmitter.removeListener('connection', listner1);
console.log("Listner1 will not listen now.");

// fire the connection event 
eventEmitter.emit('connection');

eventListeners = require('events').EventEmitter.listenerCount(eventEmitter,'connection');
console.log(eventListeners + " Listner(s) listening to connection event");

console.log("Program Ended.");

现在运行test.js看到的结果:

D:\yiibai_worksp\nodejs> node test.js

验证输出。服务器已经启动

2 Listner(s) listening to connection event
listner1 executed.
listner2 executed.
Listner1 will not listen now.
listner2 executed.
1 Listner(s) listening to connection event
Program Ended.

Node.js - 缓冲模块

缓冲器模块可以被用来创建缓冲区和SlowBuffer类。缓冲模块可以使用以下语法导入。
var buffer = require("buffer")

Buffer 类

Buffer类是一个全局类,可以在应用程序,无需导入缓冲模块进行访问。缓冲区是一种整数数组并对应于原始存储器V8堆以外分配。 缓冲区不能调整大小。

类方法

Sr. No. 方法 参数 描述
1 Buffer.isEncoding(encoding)
  • 编码字符串 - 编码字符串测试

如果编码是一种有效的编码参数,返回true,否则返回false
2 Buffer.isBuffer(obj)
  • obj Object

  • Return: Boolean

测试 obj 是否为一个缓冲区
3 Buffer.byteLength(string[, encoding])
  • string字符串

  • 编码字符串,可选,默认: 'utf8'

  • Return: Number

给出了一个字符串的实际字节长度。encoding 的默认值是 'utf8'.这与String.prototype.length是不一样的,因为它返回字符串中的字符的数目。
4 Buffer.concat(list[, totalLength])
  • 缓冲区列表数组列表对象到Concat

  • 当串联后缓冲区的数目是totalLength总长度

返回一个缓冲器是连接所有的缓冲器列表中在一起的结果。
5 Buffer.compare(buf1, buf2)
  • buf1 Buffer

  • buf2 Buffer

同样作为buf1.compare(buf2)。用于排序缓冲器数组。

示例

创建一个js文件名为test.js,在 D:\>yiibai_worksp\nodejs

File: test.js

//create a buffer
var buffer = new Buffer(26);
console.log("buffer length: " + buffer.length);

//write to buffer
var data = "gitbook.net";
buffer.write(data);
console.log(data + ": " + data.length + " characters, " + Buffer.byteLength(data, 'utf8') + " bytes");

//slicing a buffer
var buffer1 = buffer.slice(0,14);
console.log("buffer1 length: " + buffer1.length);
console.log("buffer1 content: " + buffer1.toString());

//modify buffer by indexes
for (var i = 0 ; i < 26 ; i++) {
  buffer[i] = i + 97; // 97 is ASCII a
}
console.log("buffer content: " + buffer.toString('ascii'));

var buffer2 = new Buffer(4);

buffer2[0] = 0x3;
buffer2[1] = 0x4;
buffer2[2] = 0x23;
buffer2[3] = 0x42;

//reading from buffer
console.log(buffer2.readUInt16BE(0));
console.log(buffer2.readUInt16LE(0));
console.log(buffer2.readUInt16BE(1));
console.log(buffer2.readUInt16LE(1));
console.log(buffer2.readUInt16BE(2));
console.log(buffer2.readUInt16LE(2));


var buffer3 = new Buffer(4);
buffer3.writeUInt16BE(0xdead, 0);
buffer3.writeUInt16BE(0xbeef, 2);

console.log(buffer3);

buffer3.writeUInt16LE(0xdead, 0);
buffer3.writeUInt16LE(0xbeef, 2);

console.log(buffer3);
//convert to a JSON Object
var json = buffer3.toJSON();
console.log("JSON Representation : ");
console.log(json);

//Get a buffer from JSON Object
var buffer6 = new Buffer(json);
console.log(buffer6);

//copy a buffer
var buffer4 = new Buffer(26);
buffer.copy(buffer4);
console.log("buffer4 content: " + buffer4.toString());

//concatenate a buffer
var buffer5 = Buffer.concat([buffer,buffer4]);
console.log("buffer5 content: " + buffer5.toString());

现在运行test.js看到的结果:

D:\yiibai_worksp\nodejs> node test.js

验证输出结果

buffer length: 26
gitbook.net: 18 characters, 18 bytes
buffer1 length: 14
buffer1 content: YiiBai
buffer content: abcdefghijklmnopqrstuvwxyz
772
1027
1059
8964
9026
16931
<Buffer de ad be ef>
<Buffer ad de ef be>
buffer4 content: abcdefghijklmnopqrstuvwxyz
buffer5 content: abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz

Node.js - 数据流

什么是数据流?

数据流是从源数据读取或写入数据到目标对象以。在节点中,有四种类型的流:

  • Readable - 数据流,其是用于读操作

  • Writable - 数据流,用在写操作

  • Duplex - 数据流,其可以用于读取和写入操作

  • Transform - 双相类型流,输出基于输入进行计算

每种类型的流是EventEmitter,并且一次引发几个事件。例如,一些常用的事件是:

  • data - 当有数据可读取时触发此事件

  • end - 当没有更多的数据读取时触发此事件

  • error - 当有错误或接收数据写入时触发此事件

  • finish - 当所有数据已刷新到底层系统时触发此事件

从数据流中读取

创建一个txt文件名为test.txt,在 D:\>yiibai_worksp\nodejs

Yiibai.Com

创建一个文件 test.js 在目录 D:\>yiibai_worksp\nodejs

var fs = require("fs");
var data = '';
//create a readable stream
var readerStream = fs.createReadStream('test.txt');

//set the encoding to be utf8. 
readerStream.setEncoding('UTF8');

//handle stream events
readerStream.on('data', function(chunk) {
   data += chunk;
});

readerStream.on('end',function(){
   console.log(data);
});

readerStream.on('error', function(err){
   console.log(err.stack);
});
console.log("Program Ended");

现在运行 test.js 看到的结果如下:

D:\yiibai_worksp\nodejs> node test.js

验证输出结果:

Program Ended
gitbook.net

写入到流

更新文件 test.js 内容在目录 D:\>yiibai_worksp\nodejs

var fs = require("fs");
var data = 'gitbook.net';
//create a writable stream
var writerStream = fs.createWriteStream('test1.txt');

//write the data to stream
//set the encoding to be utf8. 
writerStream.write(data,'UTF8');

//mark the end of file
writerStream.end();

//handle stream events
writerStream.on('finish', function() {
    console.log("Write completed.");
});

writerStream.on('error', function(err){
   console.log(err.stack);
});
console.log("Program Ended");

现在运行 test.js 看到的结果:

D:\yiibai_worksp\nodejs>node test.js

验证输出:

Program Ended
Write completed.

打开文件 test1.txt 在目录 D:\>yiibai_worksp\nodejs. 验证输出结果:

Yiibai.Com

管道流

管道是一种机制,一个流的输出连接到另一个流(作为另外一个流的输入)。它通常用来从一个流中获取数据,并通过该流输出到另一个流。管道没有对操作限制。考虑上面的例子中,在这里我们使用readerStream 读取test.txt的内容,并使用 writerStream 写入 test1.txt。现在,我们将用管道来简化操作,或者从一个文件中读取并写入到另一个文件。

更新 test.js 文件的内容,在目录 D:\>yiibai_worksp\nodejs, 如下:
var fs = require("fs");

//create a readable stream
var readerStream = fs.createReadStream('test.txt');

//create a writable stream
var writerStream = fs.createWriteStream('test2.txt');

//pipe the read and write operations
//read test.txt and write data to test2.txt
readerStream.pipe(writerStream);

console.log("Program Ended");

现在运行test.js看到的结果:

D:\yiibai_worksp\nodejs> node test.js

验证输出的结果:

Program Ended

打开文件 test2.txt 在 D:\>yiibai_worksp\nodejs. 验证输出结果:

Yiibai.Com

Node.js - 文件系统

fs模块用于文件I/O。 fs模块可以使用以下语法导入。
var fs = require("fs")

同步 vs 异步

每一个fs模块的方法都有同步和异步形式。异步方法接受一个最后的参数为完成回调函数,而回调函数的第一个参数是错误。它优选使用异步方法来代替同步方法,前者从未阻塞程序执行,而后者则会阻塞。

示例

创建一个js文件名为test.js 在目录 D:\>yiibai_worksp\nodejs 

File: test.js

var fs = require("fs");

//Asynchronous read
fs.readFile('test.txt', function (err, data) {
    if (err) return console.error(err);
    console.log("Asynchronous read: " + data.toString());
});

//Synchronous read
var data = fs.readFileSync('test.txt');
console.log("Synchronous read: " + data.toString());

console.log("Program Ended");

现在运行test.js看到的结果:

D:\yiibai_worksp\nodejs> node test.js

验证输出结果:

Synchronous read: gitbook.net
Program Ended
Asynchronous read: gitbook.net

方法

Sr. No. 方法 描述
1 fs.rename(oldPath, newPath, callback) rename()异步,不带参数可能是个例外,提供完成回调
2 fs.ftruncate(fd, len, callback) ftruncate()异步,不带参数可能是个例外,提供完成回调
3 fs.ftruncateSync(fd, len)  ftruncate() 同步
4 fs.truncate(path, len, callback) truncate()异步. 没有参数时它可能是例外,提供给完成回调。
5 fs.truncateSync(path, len) truncate()同步
6 fs.chown(path, uid, gid, callback) chown()异步. 没有参数时它可能是例外,提供给完成回调。
7 fs.chownSync(path, uid, gid) chown() 同步
8 fs.fchown(fd, uid, gid, callback) fchown()异步. 没有参数时它可能是例外,提供给完成回调。
9 fs.fchownSync(fd, uid, gid) fchown()异步
10 fs.lchown(path, uid, gid, callback) lchown()异步. 没有参数时它可能是例外,提供给完成回调。
11 fs.lchownSync(path, uid, gid) lchown()同步
12 fs.chmod(path, mode, callback) chmod()异步. 没有参数时它可能产生例外,提供给完成回调。
13 fs.chmodSync(path, mode) chmod() 同步
14 fs.fchmod(fd, mode, callback) fchmod() 异步. 没有参数时它可能产生例外,提供给完成回调。
15 fs.fchmodSync(fd, mode) fchmod() 同步
16 fs.lchmod(path, mode, callback) lchmod() 异步, 没有参数时它可能产生例外,提供给完成回调。仅可在Mac OS X 上使用
17 fs.lchmodSync(path, mode)  lchmod() 同步
18 fs.stat(path, callback) stat() 同步,回调函数得到两个参数(err, stats) ,其中stats是一个fs.Stats对象
19 fs.lstat(path, callback) lstat()异步,回调函数得到两个参数(err, stats),其中stats是一个fs.Stats对象。lstat()等同于stat(),不同之处在于,如果路径path是一个符号链接,那么链接本身是stat-ed,而不是指文件。
20 fs.fstat(fd, callback) fstat() 异步. 回调函数得到两个参数 (err, stats) ,其中统计数据是一个fs.Stats对象。fstat()等同于stat(),除了该文件是stat-ed 由文件描述符fd指定。
21 fs.statSync(path) stat()同步,返回fs.Stats的一个实例。
22 fs.lstatSync(path) lstat()同步,返回fs.Stats的一个实例。
23 fs.fstatSync(fd)  fstat() 同步,返回fs.Stats的一个实例。
24 fs.link(srcpath, dstpath, callback) link() 异步.  没有参数时它可能产生例外,提供给完成回调。
25 fs.linkSync(srcpath, dstpath) link() 同步
26 fs.symlink(srcpath, dstpath[, type], callback) symlink()异步,没有参数时它可能是例外,提供给完成回调。该类型参数可以设置为 'dir', 'file', 或 'junction' (默认为'file') ,只适用于Windows(忽略其他平台)。请注意,Windows结点需要目标路径是绝对的。当使用'junction',目标参数将自动归绝对路径。
27 fs.symlinkSync(srcpath, dstpath[, type]) symlink()同步
28 fs.readlink(path, callback) readlink()同步,回调函数得到两个参数(err, linkString)。
29 fs.realpath(path[, cache], callback) realpath() 异步. 回调函数得到两个参数(err, resolvedPath)。可以使用process.cwd解决相对路径。缓存cache是一个对象字面量,可以用来强制一个特定路径解析或避免额外的fs.stat调用真正的路径映射路径。
30 fs.realpathSync(path[, cache]) realpath()同步,返回解决路径。
31 fs.unlink(path, callback) unlink()异步,没有参数时它可能产生例外,提供给完成回调。
32 fs.unlinkSync(path)  unlink()同步
33 fs.rmdir(path, callback) rmdir()异步,没有参数时它可能产生例外,提供给完成回调。
34 fs.rmdirSync(path) rmdir()同步
35 fs.mkdir(path[, mode], callback) mkdir(2)异步,模式默认为0777。
36 fs.mkdirSync(path[, mode]) mkdir()同步
37 fs.readdir(path, callback) readdir(3) 同步,读取目录中的内容。回调函数得到两个参数 (err, files) ,其中文件是文件的数组名称在目录中,不包括 '.' 和 '..'.
38 fs.readdirSync(path) readdir()同步,返回文件名数组不含 '.' 和 '..'.
39 fs.close(fd, callback) close() 同步,没有参数时它可能产生例外,提供给完成回调。
40 fs.closeSync(fd) close() 同步
41 fs.open(path, flags[, mode], callback) 异步文件打开
42 fs.openSync(path, flags[, mode]) fs.open()同步
43 fs.utimes(path, atime, mtime, callback)
 
44 fs.utimesSync(path, atime, mtime) 改变所提供的路径引用的文件的文件时间戳。
45 fs.futimes(fd, atime, mtime, callback)
 
46 fs.futimesSync(fd, atime, mtime) 改变由提供的文件的描述符所引用的文件的文件时间戳。
47 fs.fsync(fd, callback) fsync(2) 异步,没有参数时它可能产生例外,提供给完成回调。
48 fs.fsyncSync(fd) fsync(2)同步
49 fs.write(fd, buffer, offset, length[, position], callback) fd指定的文件写缓冲器
50 fs.write(fd, data[, position[, encoding]], callback) 指定的fd写入文件数据。如果数据不是一个缓冲区实例则该值将被强制转换为字符串。
51 fs.writeSync(fd, buffer, offset, length[, position]) fs.write()同步。返回写入的字节数。
52 fs.writeSync(fd, data[, position[, encoding]]) fs.write()同步。返回写入的字节数。
53 fs.read(fd, buffer, offset, length, position, callback) 从fd指定文件读取数据。
54 fs.readSync(fd, buffer, offset, length, position) fs.read同步。返回 bytesRead 读取的数量。
55 fs.readFile(filename[, options], callback) 异步读取文件的全部内容。
56 fs.readFileSync(filename[, options]) fs.readFile同步,返回文件名的内容。
57 fs.writeFile(filename, data[, options], callback) 异步数据写入到文件,如果它已经存在则替换文件。数据可以是字符串或缓冲。
58 fs.writeFileSync(filename, data[, options]) fs.writeFile 同步
59 fs.appendFile(filename, data[, options], callback) 异步数据追加到文件,创建文件如果文件不存在。数据可以是字符串或缓冲。
60 fs.appendFileSync(filename, data[, options]) fs.appendFile同步
61 fs.watchFile(filename[, options], listener) 关注有关文件名更改,回调监听器在每个被访问的文件名时。
62 fs.unwatchFile(filename[, listener]) 停止监视对于文件名的更改。如果指定监听器,只有特定的监听器被删除。否则,所有监听器都会删除,已经有效地停止监听文件名。
63 fs.watch(filename[, options][, listener]) 关注文件名的变化,其中 filename 是一个文件或目录。返回是一个 fs.FSWatcher 的对象。
64 fs.exists(path, callback) 测试给定的路径通过使用文件系统检查是否存在。然后调用回调参数为真或假。
65 fs.existsSync(path) fs.exists同步
66 fs.access(path[, mode], callback) 由path指定的文件测试用户的权限。mode是一个可选整数,指定要执行辅助功能的检查。
67 fs.accessSync(path[, mode]) fs.access同步。这将引发辅助功能失败,否则什么也不做。
68 fs.createReadStream(path[, options]) 返回一个新的ReadStream对象
69 fs.createWriteStream(path[, options]) 返回一个新的WriteStream对象
70 fs.symlink(srcpath, dstpath[, type], callback) 异步symlink(). 该类型参数可以设置为'dir','file',或'junction'(默认为“文件”),只适用于Windows(忽略其他平台)。请注意,Windows交界点需要的目标路径是绝对的。当使用'junction',目标参数将自动标准化为绝对路径。

标志

标志进行读/写操作是:

  • r - 打开文件进行读取。如果该文件不存在发生异常。

  • r+ - 打开文件进行读取和写入。如果该文件不存在发生异常。

  • rs - 打开文件,用于读取在同步方式。指示操作系统绕过本地文件系统高速缓存。这是对NFS挂载打开文件,因为它可以让你跳过潜在陈旧的本地缓存中很有用。它对I/O性能有非常现实的影响,除非需要它,否则不要使用此标志。注意,这无法打开 fs.open() 到一个同步阻塞调用。如果这不是真想要的,那么应该使用fs.openSync()。

  • rs+ - 打开文件进行读取和写入,告诉操作系统同步地打开它。 对于'rs'有关异步使用 - 要慎用。

  • w - 打开文件进行写入。该文件被创建(如果它不存在)或截断清空内容(如果存在)。

  • wx - 类似“w”,但如果路径存在,则失败。

  • w+ - 打开文件进行读取和写入。该文件被创建(如果它不存在)或截断清空内容(如果存在)。

  • wx+ - 类似 “w+”,但如果路径存在,则失败

  • a - 打开文件进行追加。如果文件不存在,则创建该文件

  • ax - 类似“a”,但如果路径存在则失败

  • a+ - 打开文件进行读取和追加内容。如果文件不存在,则创建该文件

  • ax+' - 类似 'a+',但如果路径存在则会失败

示例

创建一个 txt 文件:test.txt 在 D:\>yiibai_worksp\nodejs,内容如下:
Yiibai.Com 

创建一个js文件名为test.js 在D:\>yiibai_worksp\nodejs 目录中。

File: test.js

var fs = require("fs");
var buffer = new Buffer(1024);

//Example: Opening File
function openFile(){
   console.log("\nOpen file");
   fs.open('test.txt', 'r+', function(err,fd) {
      if (err) console.log(err.stack);
	  console.log("File opened");     
   });
}

//Example: Getting File Info
function getStats(){
   console.log("\nGetting File Info");
   fs.stat('test.txt', function (err, stats) {
      if (err) console.log(err.stack);
      console.log(stats);
      console.log("isFile ? "+stats.isFile());
      console.log("isDirectory ? "+stats.isDirectory());
   });
}

//Example: Writing File
function writeFile(){
   console.log("\nWrite file");
   fs.open('test1.txt', 'w+', function(err,fd) {
      var data = "gitbook.net - Simply Easy Learning!";
      buffer.write(data); 
      
      fs.write(fd, buffer,0,data.length,0,function(err, bytes){
         if (err) console.log(err.stack);
	     console.log(bytes + " written!");	     
      });	
   });
}

//Example: Read File
function readFile(){
   console.log("\nRead file");
   fs.open('test1.txt', 'r+', function(err,fd) {
      if (err) console.log(err.stack);
      fs.read(fd, buffer,0,buffer.length,0,function(err, bytes){
         if (err) console.log(err.stack);
	     console.log(bytes + " read!");
	     if(bytes > 0){
            console.log(buffer.slice(0,bytes).toString());
	     }
      });	
   });
}

function closeFile(){
   console.log("\nClose file");
   fs.open('test.txt', 'r+', function(err,fd) {
      if (err) console.log(err.stack);	  
	  fs.close(fd,function(){
	     if (err) console.log(err.stack);	  
		 console.log("File closed!");
	  });
   });
}

function deleteFile(){
   console.log("\nDelete file");
   fs.open('test1.txt', 'r+', function(err,fd) {
	   fs.unlink('test1.txt', function(err) {
		  if (err) console.log(err.stack);	  	  	  
		  console.log("File deleted!");	 
	   });
   });
}

function truncateFile(){
   console.log("\nTruncate file");
   fs.open('test.txt', 'r+', function(err,fd) {
	   fs.ftruncate(fd, function(err) {
		  if (err) console.log(err.stack);	  	  	  
		  console.log("File truncated!");	 
	   });
   });
}

function createDirectory(){
	console.log("\nCreate Directory");
	fs.mkdir('test',function(err){
      if(!err){
	     console.log("Directory created!");
	  }
      if(err && err.code === 'EEXIST'){
         console.log("Directory exists!");	
      } else if (err) {       
         console.log(err.stack);	  	 
      }
	});
}

function removeDirectory(){
	console.log("\nRemove Directory");
	fs.rmdir('test',function(err){
      if(!err){
	     console.log("Directory removed!");
	  }
      if (err) {      
		 console.log("Directory do not exist!");		           
      }
	});
}

function watchFile(){
   fs.watch('test.txt', function (event, filename) {
      console.log('event is: ' + event);   
   });
}

//Opening file
openFile();

//Writing File
writeFile();

//Reading File   
readFile();

//Closing Files
closeFile();

//Getting file information
getStats();
   
//Deleting Files
deleteFile();
   
//Truncating Files
truncateFile();
   
//Creating Directories
createDirectory();

//Removing Directories
removeDirectory();
   
//Watching File Changes
watchFile();

现在运行 test.js 看到的结果:

D:\yiibai_worksp\nodejs> node test.js

验证输出:

Open file

Write file

Read file

Close file

Getting File Info

Delete file

Truncate file

Create Directory

Remove Directory
File opened
{ dev: 0,
  mode: 33206,
  nlink: 1,
  uid: 0,
  gid: 0,
  rdev: 0,
  ino: 0,
  size: 0,
  atime: Fri Jan 01 2010 00:02:15 GMT+0530 (India Standard Time),
  mtime: Sun Feb 15 2015 13:33:09 GMT+0530 (India Standard Time),
  ctime: Fri Jan 01 2010 00:02:15 GMT+0530 (India Standard Time) }
isFile ? true
isDirectory ? false
Directory created!
Directory removed!
event is: rename
event is: rename
42 written!
42 read!
gitbook.net - Simply Easy Learning!
File closed!
File deleted!
File truncated!
event is: change

Node.js - 工具模块

在这篇文章中,我们将讨论一些Node.js库提供的工具模块,这是非常常见的,经常使用在整个应用程序中。

Sr.No. 模块的名称和说明
1 Console
用于打印输出和错误信息
2 Process
用于获取当前进程的信息,提供处理相关活动的多个事件
3 OS Module
提供基本的操作系统相关的实用功能
4 Path Module
实用工具提供用于处理和转化文件路径
5 Net Module
提供服务器和客户端的数据流。作为一个网络应用包
6 DNS Module
提供的功能做实际的DNS查找,以及使用底层操作系统的名称解析功能
7 Domain Module
提供一种方式来处理多个不同的I/O操作为一个组

Node.js - 控制台

 

Console是一个全局对象,用于打印输出和错误。目标是文件是用在同步方式,或当目标是管道终端为异步方式。

 

方法

Sr. No. 方法 描述
1 console.log([data][, ...]) 打印及带有换行符到stdout,此函数可将多个参数在printf()类似的方式
2 console.info([data][, ...]) 打印及带有换行符到stdout,此函数可将多个参数在printf()类似的方式
3 console.error([data][, ...]) 打印及带有换行符到stderr,此函数可将多个参数在printf()类似的方
4 console.warn([data][, ...]) 打印及带有换行符到stderr,此函数可将多个参数在printf()类似的方式
5 console.dir(obj[, options]) 使用util.inspect在obj和生成的字符串打印到stdout
6 console.time(label) 标记时间
7 console.timeEnd(label) 完成定时,记录输出
8 console.trace(message[, ...]) 打印到stderr“Trace :',其次是格式化的消息和堆栈跟踪到当前位置
9 console.assert(value[, message][, ...]) 类似于assert.ok(),但错误信息被格式化为util.format(message...)

示例

创建一个js文件名为test.js 在目录 D:\>yiibai_worksp\nodejs

File: test.js

var counter = 10;

console.log("Counter: %d", counter);

console.time("Getting data");
//make a database call to retrive the data
//getDataFromDataBase();
console.timeEnd('Getting data');

console.info("Program Ended!")

现在运行test.js看到的结果:

D:\yiibai_worksp\nodejs> node test.js

验证输出

Counter: 10
Getting data: 0ms
Program Ended!

Node.js - 进程

process 是一个全局性对象,并用于表示Node进程。

退出码

节点正常退出时带状态0码,在没有更多的异步操作挂起时。其他退出代码描述如下:

Code 名称 描述
1 未捕获致命异常 有一个未捕获的异常,它不是由域或uncaughtException事件处理程序处理
2 未使用 通过对于Bash滥用内置保留
3 内部JavaScript解析错误 JavaScript源代码,在内部Node引导过程引起了解析错误。这是极为罕见的,一般只是在Node本身的开发过程中才发生的。
4 内部的JavaScript评估计算失败 JavaScript源代码,在内部Node的引导过程未能评估计算时返回一个函数值。这是极为罕见的,一般只是在Node本身的开发过程中发生的。
5 致命错误 在V8中的一个致命不可恢复的错误。通常情况下一个消息将被打印到stderr使用前缀致命错误。
6 非函数内部异常处理程序 有一个未捕获的异常,但内部致命的异常处理函数被莫名其妙地设置为一个非函数,并且不能被调用。
7 内部异常处理程序运行时故障 这里是一个未捕获的异常,并试图处理它内部的致命的异常处理函数本身抛出一个错误。
8 未使用
 
9 无效的参数 要么未知选项指定,或没有值时需要提供一个值的选项。
10 内部的JavaScript运行时故障 JavaScript源代码,在内部Node引导过程中抛出一个错误,当引导函数被调用。这是极为罕见的,一般Node是在开发过程中发生的。
12 无效的调试参数 --debug和/或--debug-brk选项都设置,但不能选择无效的端口号。
>128 信号退出 如果Node接收到一个致命信号如SIGKILL或SIGHUP,然后它的退出代码是128加上的信号代码的值。这是一个标准Unix,实际上由于退出代码被定义为7位整数,信号退出设置的高序位,包含所述信号的代码值。

事件

Process(进程)是一个eventEmitter,并且它发出下列事件

Sr.No. Event 描述
1 exit 发射当进程即将退出。没有办法来防止事件循环退出在这一点上, 并且一旦所有的退出监听器运行完成,程序将退出。
2 beforeExit 当Node清空它的事件循环,并没有其他事情安排此事件被发射。通常情况下,当没有预定的工作Node退出,但是一个监听'beforeExit'可进行异步调用,并导致Node继续。
3 uncaughtException 发射当异常冒泡一路回到事件循环。如果一个监听器添加了此异常,则缺省操作(这是打印堆栈跟踪并退出)不会发生。
4 Signal Events 发射当进程接收到信号,例如SIGINT,SIGHUP等

属性

Process(进程)提供了许多有用的特性,以更好地控制系统的相互作用。

Sr.No. 属性 描述
1 stdout 可写流到标准输出
2 stderr 可写流到stderr
3 stdin 可写流标准输入
4 argv 包含命令行参数数组。第一元件将是“节点”,第二个元素是JavaScript文件的名称。接下来元素的内容是额外的命令行参数
5 execPath 这是启动进程的可执行文件的绝对路径
6 execArgv 这是一组Node特定的命令行选项从可执行启动进程
7 env 包含用户环境的对象
8 exitCode 许多可作为进程退出代码优雅地退出,或者是通过process.exit()无需指定代码退出
9 version 编译时属性暴露NODE_VERSION
10 versions 属性用来暴露node及其依赖的版本字符串
11 config 对象包含JavaScript用来表示编译当前Node可执行的配置选项能力。这是类似于“config.gypi”文件,所产生在./configure脚本运行时
12 pid 进程的PID
13 title getter/setter设置 'ps' 所显示
14 arch 在什么处理器架构上运行: 'arm', 'ia32', 或 'x64'
15 platform 正在运行在什么平台: 'darwin', 'freebsd', 'linux', 'sunos' 或 'win32'
16 mainModule 备选的方法来检索require.main。不同的是,如果主模块改变在运行时,require.main仍可能指的是原来的主模块中被要求的变化发生之前的模块。通常它是安全假设的,这两个指的是相同的模块

方法

Process(进程)提供了许多有用的方法,以更好的控制系统的相互作用。

Sr.No. 方法 描述
1 abort() 这导致node发出中止。node退出并生成一个核心文件
2 chdir(directory) 改变进程的当前工作目录,或如果失败将引发异常
3 cwd() 返回进程的当前工作目录
4 exit([code]) 终止使用指定的代码进程。如果省略,退出使用'成功'的代码0
5 getgid() 获取进程组标识,这是数值组ID,而不是组名。此函数仅适用于POSIX平台(即非Windows,Android)。
6 setgid(id) 设置进程组的标识。 (详看setgid(2).) 这要么接受一个数字ID或组名字符串。如果指定了组名,则此方法阻塞在解析到一个数字ID。此函数仅适用于POSIX平台(即非Windows,Android)。
7 getuid() 获取进程的用户身份。这是数字ID,而不是用户名。此函数仅适用于POSIX平台(即非Windows,Android)。
8 setuid(id) 设置进程的用户的身份。(请参阅setgid(2))。这接受一个数字ID或用户名字符串。如果指定一个用户名,这个方法会阻塞,同时解析到一个数字ID。此函数仅适用于POSIX平台(即非Windows,Android)。
9 getgroups() 返回带有补充组ID的数组。POSIX离开未指定是否该有效组ID包函,而Node.js保证它始终是。此函数仅适用于POSIX平台(即非Windows,Android)。
10 setgroups(groups) 设置辅助组标识。这是一个权限操作,这意味着需要以root或具有CAP_SETGID权限。此函数仅适用于POSIX平台(即非Windows,Android)。
11 initgroups(user, extra_group) 读取/etc/group文件并初始化组访问列表,使用所有这些组的用户是其成员。这是一个权限操作,这意味着需要以root或具有CAP_SETGID权力。此函数仅适用于POSIX平台(即非Windows,Android)。
12 kill(pid[, signal]) 发送信号给一个进程。pid是进程ID和信号描述发送信号的字符串。信号的名字就像'SIGINT'或'SIGHUP'字符串。如果省略,信号是'SIGTERM'。
13 memoryUsage() 返回描述以字节为单位的Node进程的内存使用量的对象
14 nextTick(callback) 一旦当前事件循环转到运行结束,调用回调函数
15 umask([mask]) 设置或读取进程的文件模式创建掩码。子进程继承父进程的掩码。返回老掩码如果掩码参数给出,否则返回当前掩码
16 uptime() Node已经运行的秒数
17 hrtime() 返回当前高分辨率实时在[seconds, nanoseconds] 元组。它是相对于过去的任意时间。它是不相关的一天中的时间,因此,不会受到时钟偏移。 主要用途是用于测量时间间隔之间的性能。

示例

创建一个js文件名为test.js 在目录 D:\>yiibai_worksp\nodejs

File: test.js

var util = require('util');

//printing to console
process.stdout.write("Hello World!" + "\n");

//reading passed parameter
process.argv.forEach(function(val, index, array) {
   console.log(index + ': ' + val);
});

//executable path
console.log(process.execPath);

//print the current directory
console.log('Current directory: ' + process.cwd());
	
//print the process version
console.log('Current version: ' + process.version);

//print the memory usage
console.log(util.inspect(process.memoryUsage()));

现在运行test.js看到的结果:

D:\yiibai_worksp\nodejs> node test.js

验证输出:

Hello World!
0: node
1: D:\yiibai_worksp\nodejs\test.js
D:\yiibai_worksp\nodejs\node.exe
Current directory: D:\yiibai_worksp\nodejs
Current version: v0.12.7
{ rss: 16379904, heapTotal: 9751808, heapUsed: 4067896 }

Node.js - OS 模块

os模块用于一些基本的操作系统相关的实用功能。os模块可以使用以下语法导入。

var os = require("os")

方法

Sr. No. 方法 描述
1 os.tmpdir() 返回临时文件操作系统的默认目录
2 os.endianness() 返回CPU的存储方式。可能的值是“BE”或“LE”
3 os.hostname() 返回操作系统的主机名
4 os.type() 返回操作系统名称
5 os.platform() 返回操作系统平台
6 os.arch() 返回操作系统的CPU架构。可能的值是“64”,“arm”和“ia32”
7 os.release() 返回操作系统版本
8 os.uptime() 返回系统正常运行时间(秒)
9 os.loadavg() 返回包含1,5和15分钟的负载量平均值的数组
10 os.totalmem() 返回以字节系统存储器的总量
11 os.freemem() 返回可用系统内存量(字节)
12 os.cpus() 返回一个包含每个已安装的CPU/核心的信息对象的数组:model, speed (in MHz),和时间(含有用在CPU/核心的毫秒数量的对象:user, nice, sys, idle, and irq).
13 os.networkInterfaces() 得到的网络接口的列表

属性

Sr. No. 属性 描述
1 os.EOL 操作系统限定行尾的适当标记的常量

示例

创建一个js文件名为test.js 在 D:\>yiibai_worksp\nodejs 

File: test.js

var os = require("os");

//endianness
console.log('endianness : ' + os.endianness());

//type
console.log('type : ' + os.type());

//platform
console.log('platform : ' + os.platform());

//totalmem
console.log('total memory : ' + os.totalmem() + " bytes.");

//freemem
console.log('free memory : ' + os.freemem() + " bytes.");

现在运行test.js看到的结果:

D:\yiibai_worksp\nodejs> node test.js

验证输出:

endianness : LE
type : Windows_NT
platform : win32
total memory : 4201889792 bytes.
free memory : 1233211392 bytes.

Node.js - Path 模块

path 模块用于处理和转换文件路径。path模块可以使用以下语法导入。

var path = require("path")

属性

进程提供了许多有用的特性,以获得更好的控制系统的相互作用。

Sr.No. 属性 描述
1 path.sep 特定于平台的文件分隔符 '\\' 或 '/'.
2 path.delimiter 特定于平台的路径分隔符, ; 或 ':'.
3 path.posix 提供访问路径上述方法,但始终在POSIX兼容的方式进行交互
4 path.win32 提供访问路径上述方法,但总是在Win32兼容的方式进行交互

方法

Sr. No. 方法 描述
1 path.normalize(p) 规范化字符串路径,采用'..'和'.'部分
2 path.join([path1][, path2][, ...]) 连接所有参数一起和规范生成的路径
3 path.resolve([from ...], to) 解析到一个绝对路径
4 path.isAbsolute(path) 确定路径是否是一个绝对路径。绝对路径将始终解析到相同的位置,而不考虑工作目录
5 path.relative(from, to) 解决相对路径从from到to
6 path.dirname(p) 返回路径的目录名称,类似于Unix的目录名的命令。
7 path.basename(p[, ext]) 返回路径的最后部分,类似于UNIX命令基名。
8 path.extname(p) 返回路径的扩展,从最后一个“.”结束到字符串在路径的最后部分。如果没有'.'在路径最后部分或第一个字符的是“.”,那么它返回一个空字符串。
9 path.parse(pathString) 从路径字符串返回对象
10 path.format(pathObject) 从一个对象返回 path.parse上述相反的路径字符串

示例

创建一个js文件名为test.js 在 D:\>yiibai_worksp\nodejs

File: test.js

var path = require("path");

//normalization
console.log('normalization : ' + path.normalize('/test/test1//2slashes/1slash/tab/..'));

//join
console.log('joint path : ' + path.join('/test', 'test1', '2slashes/1slash', 'tab', '..'));

//resolve
console.log('resolve : ' + path.resolve('test.js'));

//extName
console.log('ext name : ' + path.extname('test.js'));

现在运行test.js看到的结果:

D:\yiibai_worksp\nodejs> node test.js

验证输出:

normalization : \test\test1\2slashes\1slash
joint path : \test\test1\2slashes\1slash
resolve : D:\yiibai_worksp\nodejs\test.js
ext name : .js

Node.js - Net模块

net 模块是用来创建服务器和客户端。它提供了一个异步网络包装。 net模块可以使用以下语法导入。

var net = require("net")

方法

Sr. No. 方法 描述
1 net.createServer([options][, connectionListener]) 创建一个新的TCP服务器。connectionListener参数自动设置为监听器的'连接'事件
2 net.connect(options[, connectionListener]) 工厂方法,它返回一个新的“net.Socket”,并连接到提供的地址和端口
3 net.createConnection(options[, connectionListener]) 工厂方法,它返回一个新的“net.Socket”,并连接到提供的地址和端口
4 net.connect(port[, host][, connectListener]) 创建于主机的TCP连接端口。如果省略主机,默认使用“localhost”。connectListener参数将作为一个监听器“连接”事件。它返回一个新的“net.Socket”工厂方法
5 net.createConnection(port[, host][, connectListener]) 创建于主机的TCP连接端口。如果省略主机,默认是“localhost”。 connectListener参数将作为一个监听器“连接”事件。它返回一个新的“net.Socket”工厂方法
6 net.connect(path[, connectListener]) 创建Unix套接字连接路径。connectListener参数将作为一个监听器“连接”事件。工厂方法返回一个新的“net.Socket”
7 net.createConnection(path[, connectListener]) 创建Unix套接字连接路径,connectListener参数将作为一个监听器“连接”事件。工厂方法返回一个新的“net.Socket”
8 net.isIP(input) 测试输入是否为IP地址,返回0则是无效的字符串,返回4则是IP版本4的地址, 返回6 则是IP版本6的地址
9 net.isIPv4(input) 返回true,如果输入的是版本4的IP地址,否则返回false
10 net.isIPv6(input) 返回true,如果输入的是版本6的IP地址,否则返回false

Class:net.Server

此类用于创建TCP或本地服务器。

方法

Sr. No. 方法 描述
1 server.listen(port[, host][, backlog][, callback]) 开始接受指定的端口和主机上的连接。如果省略主机,服务器将接受针对任何IPv4地址(INADDR_ANY)连接,端口值为0时将分配一个随机端口
2 server.listen(path[, callback]) 启动本地socket服务器监听给定的路径上的连接
3 server.listen(handle[, callback]) 句柄对象可以被设置为一个服务器或socket(下面_handle构件任何东西),或一个{fd:<n>}对象。这将导致服务器接受指定句柄连接,但可以推测该文件描述符或句柄已被绑定到端口或域套接字。监听文件描述符在Windows上不支持。
4 server.listen(options[, callback]) port,host和backlog选项的属性,以及可选callback 函数,这些做一个调用 server.listen(port, [host], [backlog], [callback]) 。或者,可使用的路径选项指定一个UNIX套接字。
5 server.close([callback]) 最终关闭时结束所有连接和服务器发出一个“关闭”事件
6 server.address() 操作系统返回服务器的绑定地址,所述地址族名称和端口。
7 server.unref() 在服务器上调用unref将允许程序退出,如果这是在事件系统中唯一的活动服务器。如果服务器已经unrefd再次调用unref将无任何作用。
8 server.ref() 与unref相反,如果它是仅存的服务器(默认行为)调用ref在unrefd之前服务器不会让程序退出。如果服务器refd调用再次ref不会有任何效果。
9 server.getConnections(callback) 异步获取服务器上的并发连接数量。 当套接字被送往forks。回调函数应该用两个参数 err和count。

事件

Sr. No. 事件 描述
1 listening 发射在服务器绑定调用server.listen后
2 connection 当一个新的连接创建时发射。Socket对象,连接对象提供给事件处理。Socket是net.Socket的一个实例。
3 close 服务器关闭时发出。需要注意的是,如果连接存在,这个事件是不会发射的,直到所有的连接都结束。
4 error 当错误发生时发出。 “关闭”将被直接调用此事件之后。

Class:net.Socket

这个对象是一个TCP或本地套接字的抽象。net.Socket的实例实现全双工流接口。它们可以由用户创建,并用作一个客户端(使用connect()),也可以由Node创建并通过服务器的'连接'事件传递给用户。

事件

net.Socket是一个eventEmitter,它发出下列事件。

Sr.No. 事件 描述
1 lookup 解析主机名之后,但在连接之前发射。不适用于Unix套接字
2 connect 当成功建立一个套接字连接时发射
3 data 当接收数据时发射。参数数据将是一个缓冲或字符串。重新编码的数据是通过socket.setEncoding()设定的
4 end 当套接字的另一端发送一个FIN包发射
5 timeout 如果套接字超闲置时发射。这是唯一的,以通知套接字已经空闲。用户必须手动关闭连接
6 drain 当写缓冲区变空时发射。可用于限制上传
7 error 当错误发生时发射,“关闭”事件将被直接称为继此事件。
8 close 当套接字被完全关闭时发射,参数had_error是一个布尔值,指明如果套接字是由于传输错误关闭。

属性

net.Socket提供了许多有用的特性,以更好的控制套接字的相互作用。

Sr.No. 属性 描述
1 socket.bufferSize 此属性表示要写入当前缓冲的字符数
2 socket.remoteAddress 远程IP地址的字符串表示。例如, '74.125.127.100' 或 '2001:4860:a005::68'
3 socket.remoteFamily 远程IP系列(簇)的字符串表示。 'IPv4' 或 'IPv6'
4 socket.remotePort 远程端口的数字表示。例如,80 或 21
5 socket.localAddress 远程客户端连接到本地的IP地址的字符串表示。例如,如果正在在“0.0.0.0”监听,客户端连接到 '192.168.1.1', 那么它的值就是 '192.168.1.1'
6 socket.localPort 本地端口的数字表示。例如,80 或 21
7 socket.bytesRead 接收的字节数
8 socket.bytesWritten 发送的字节数

方法

Sr. No. 方法 描述
1 new net.Socket([options]) 构造一个新的 Socket 对象
2 socket.connect(port[, host][, connectListener]) 打开一个给定的套接字连接。如果端口和主机给出,那么套接字就会被打开作为TCP套接字,如果省略主机,将使用localhost。如果路径给定,套接字就会被打开作为一个Unix套接字到该路径
3 socket.connect(path[, connectListener]) 打开一个给定的套接字连接。如果端口和主机给出,那么套接字就会被打开作为TCP套接字,如果省略主机,将使用localhost。如果路径给定,套接字就会被打开作为一个Unix套接字到该路径
4 socket.setEncoding([encoding]) 套接字为只读流设置编码
5 socket.write(data[, encoding][, callback]) 发送套接字上的数据。第二个参数指定的编码是在字符串的情况 - 它默认为UTF8编码。
6 socket.end([data][, encoding]) 半关闭套接字,即它发送FIN包。可能服务器仍然会发送一些数据。
7 socket.destroy() 确保没有更多的I/O活动发生在此套接字。只有在错误的情况下(解析错误)有必要。
8 socket.pause() 暂停数据读出。即,“数据”事件将不会被发射。有用的节流回到上载
9 socket.resume() 调用 pause() 后读取暂停
10 socket.setTimeout(timeout[, callback]) 设置套接字不活动的套接字在timeout毫秒后超时。默认 net.Socket 不会超时。
11 socket.setNoDelay([noDelay]) 禁用Nagle算法。默认情况下TCP连接使用Nagle算法,它们在发送之前关闭缓存数据。noDelay设置true时每次socket.write()被调用将立即释放掉的数据。noDelay 默认值是true.
12 socket.setKeepAlive([enable][, initialDelay]) 启用/禁用保持有效功能, 可选设置初始延迟后,第一存活探测被发送在空闲插槽。 enable 默认值是 false.
13 socket.address() 返回绑定地址, 报告操作系统的地址系列名称和套接字的端口。 返回一个对象有三个属性,例如{ port: 12346, family: 'IPv4', address: '127.0.0.1' }.
14 socket.unref() 套接字上调用unref 将允许程序退出,如果这是事件系统中唯一的活动套接字。如果套接字已经unrefd再次调用unref 将不会起作用。
15 socket.ref() 与unref相反,调用ref在unrefd套接字之前不会让程序退出,如果它是唯一剩下的socket(默认行为)。如果套接字refd调用再次ref将没有起任何作用。

示例

创建一个js文件名为server.js 在 D:\>yiibai_worksp\nodejs

File: server.js

var net = require('net');
var server = net.createServer(function(connection) { 
   console.log('client connected');
   connection.on('end', function() {
      console.log('client disconnected');
   });
   connection.write('Welcome to Yiibai Yiibai!\r\n');
   connection.pipe(connection);
});
server.listen(8080, function() { 
  console.log('server is listening');
});

现在运行server.js看到的结果:

D:\yiibai_worksp\nodejs>node server.js

验证输出。

server is listening

创建一个js文件名为client.js 在 C:\>Nodejs_WorkSpace.

File: client.js

var net = require('net');
var client = net.connect({port: 8080}, function() {
   console.log('connected to server!');  
});
client.on('data', function(data) {
   console.log(data.toString());
   client.end();
});
client.on('end', function() { 
   console.log('disconnected from server');
});

现在启动另一个命令行窗口,运行 client.js 看到结果:

D:\yiibai_worksp\nodejs>node client.js

验证输出:

connected to server!
Welcome to Yiibai Yiibai!

disconnected from server

验证,其中server.js运行输出在终端上如下:

server is listening
client connected
client disconnected

Node.js - DNS 模块

dns 模块是用来做实际的DNS查找,以及使用底层操作系统的名称解析功能..它提供了一个异步网络包装。dns模块可以使用以下语法导入。

var dns = require("dns")

方法

Sr. No. 方法 描述
1 dns.lookup(hostname[, options], callback) 解析主机名(如“google.com”)进入第一找到A(IPv4)或AAAA(IPv6)纪录。选项可以是对象或整数。如果未提供选项,那么IP v4和v6的地址都是有效的。如果选项是整数,那么它必须是4或6
2 dns.lookupService(address, port, callback) 使用getnameinfo会解析给定的地址和端口成一个主机名和服务
3 dns.resolve(hostname[, rrtype], callback) 解析主机名(如“google.com”)到由rrtype指定的记录类型的数组
4 dns.resolve4(hostname, callback) 与 dns.resolve() 相同, 但仅限于IPv4的查询(A记录)。地址是IPv4地址的数组(如,['74.125.79.104', '74.125.79.105', '74.125.79.106'])
5 dns.resolve6(hostname, callback) 与dns.resolve4()相同,除了IPv6的查询(一个AAAA查询)
6 dns.resolveMx(hostname, callback) 与dns.resolve()相同,但仅限于邮件交换查询(MX记录)
7 dns.resolveTxt(hostname, callback) 与 dns.resolve()相同,但仅限于文本查询(TXT记录)。地址是文本纪录,以供主机的2-D数组 (如, [ ['v=spf1 ip4:0.0.0.0 ', '~all' ] ]). 每个子阵列包含一个记录的TXT块。根据使用的情况,可以任意连接在一起或分别处理
8 dns.resolveSrv(hostname, callback) 与dns.resolve()相同,但仅用于服务记录(SRV记录)。 地址是一个SRV记录的数组提供主机名。SRV记录属性包括优先级,权重,端口和名称(e.g., [{'priority': 10, 'weight': 5, 'port': 21223, 'name': 'service.example.com'}, ...]).
9 dns.resolveSoa(hostname, callback) 与 dns.resolve() 相同, 但仅限于认证记录查询开始(SOA记录)
10 dns.resolveNs(hostname, callback) 与 dns.resolve() 相同, 但只针对域名服务器记录(NS记录)。地址是可用于主机的名称服务器记录的数组 (, ['ns1.example.com', 'ns2.example.com']).
11 dns.resolveCname(hostname, callback) 与 dns.resolve()相同, 但仅限于规范名称记录(CNAME记录)。地址是的规范名称记录可用于主机的数组 (如, ['bar.example.com']).
12 dns.reverse(ip, callback) Reverse resolves an ip address to an array of hostnames.
13 dns.getServers() Returns an array of IP addresses as strings that are currently being used for resolution.
14 dns.setServers(servers) Given an array of IP addresses as strings, set them as the servers to use for resolving.

rrtypes

以下是所使用dns.resolve()方法的有效rrtypes的列表

  • A - IPV4 地址(默认)

  • AAAA - IPV6 地址

  • MX - 邮件交换记录

  • TXT - 文字记录

  • SRV - SRV记录

  • PTR - 用于逆向IP查找

  • NS - 域名服务器记录

  • CNAME - 规范名称记录

  • SOA - 规范记录的开始

错误代码

每个DNS查询可以返回下列错误代码之一:

  • dns.NODATA - DNS服务器返回回答不含数据

  • dns.FORMERR - DNS服务器要求查询是misformatted

  • dns.SERVFAIL - DNS服务器返回的一般故障

  • dns.NOTFOUND - 域名未找到

  • dns.NOTIMP - DNS服务器不执行请求操作

  • dns.REFUSED - DNS服务器拒绝查询

  • dns.BADQUERY - 非格式化的DNS查询

  • dns.BADNAME - 非格式化的主机名

  • dns.BADFAMILY - 不支持的地址族

  • dns.BADRESP - Misformatted DNS回复

  • dns.CONNREFUSED - 无法联系DNS服务器

  • dns.TIMEOUT - 超时在联系DNS服务器的时候

  • dns.EOF - 文件的结尾

  • dns.FILE - 读取文件错误

  • dns.NOMEM - 内存不足

  • dns.DESTRUCTION - 信道被销毁

  • dns.BADSTR - 非法格式化字符串

  • dns.BADFLAGS - 非法标志指定

  • dns.NONAME - 给定主机名不是数字

  • dns.BADHINTS - 非法提示标志指定

  • dns.NOTINITIALIZED - c-ares库初始化尚未执行

  • dns.LOADIPHLPAPI - 加载 iphlpapi.dll 错误

  • dns.ADDRGETNETWORKPARAMS - 找不到GetNetworkParams函数

  • dns.CANCELLED - DNS查询取消

示例

创建一个js文件名为test.js 在 D:\>yiibai_worksp\nodejs

File: test.js

var dns = require('dns');

dns.lookup('www.gitbook.net', function onLookup(err, address, family) {
   console.log('address:', address);
   dns.reverse(address, function (err, hostnames) {
   if (err) {
      console.log(err.stack);
   }

   console.log('reverse for ' + address + ': ' + JSON.stringify(hostnames));
});  
});

现在运行test.js看到的结果:

D:\yiibai_worksp\nodejs> node test.js

验证输出:

address: 112.124.103.85
Error: getHostByAddr ENOTFOUND 112.124.103.85
    at errnoException (dns.js:44:10)
    at Object.onresolve [as oncomplete] (dns.js:199:19)
reverse for 112.124.103.85: undefined

Node.js - Domain模块

domain 模块是用于拦截未处理的错误。这些未处理的错误可以使用内部绑定或外部绑定来拦截。如果错误都那么不处理,那么Node应用会崩溃。

  • 内部绑定 - 错误发生执行代码是在一个域的run方法。

  • 外部绑定 - 使用它的add方法将错误发生明确地加入到域。

domain(域)模块可以使用以下语法导入。

var domain = require("domain")

域模块的Domain类是用来提供路由错误以及未捕获异常活动域对象的功能。 这是一个EventEmitter的子类。为了处理它捕获错误,监听它的错误事件。它使用如下的语法创建:

var domain = require("domain");
var domain1 = domain.create();

方法

Sr. No. 方法 描述
1 domain.run(function) 运行所提供的函数在域的上下文中,隐式绑定在这种情况下创建的所有事件发射器,定时器和低级请求,这是使用域的最基本的方法
2 domain.add(emitter) 显式地将一个发射器到域。如果由发射器事件处理程序抛出一个错误,或者,如果发射器发出一个错误事件,它会被路由到该域的错误事件,就如隐式绑定
3 domain.remove(emitter) 与domain.add(emitter)相反。从指定的发射器处理删除域
4 domain.bind(callback) 返回的函数将被围绕包装提供回调函数。当返回的函数被调用时,被抛出的任何错误都将被路由到该域的错误事件
5 domain.intercept(callback) 此方法几乎是domain.bind(callback)相同, 然而,除了捕获抛出错误,它也将截取Error 对象发送作为第一个参数到函数。
6 domain.enter() 输入是一种用于运行,绑定,并拦截方法来设置活动域管道。它设置domain.active和process.domain到域,并隐式地推到域,域管理模块域栈(详见domain.exit()详细信息,域名栈)。调用以进入分隔链开始绑定到域异步调用和I/O操作。
7 domain.exit() exit方法退出当前域,它弹出关闭域堆栈。任何时间执行将要切换到不同链的异步调用的上下文中,重要的是要确保当前域退出。调用以退出分隔末尾或者中断链绑定到一个域的异步调用和I/O操作。
8 domain.dispose() 一旦处理被调用,该域将不再使用回调绑定到通过运行,绑定或拦截该域。以及一个dispose事件发射

属性

Sr.No. 属性 描述
1 domain.members 定时器和事件发射器数组已明确添加到域

示例

创建一个js文件名为test.js 在 D:\>yiibai_worksp\nodejs

File: test.js

var EventEmitter = require("events").EventEmitter;
var domain = require("domain");

var emitter1 = new EventEmitter();

//Create a domain
var domain1 = domain.create();

domain1.on('error', function(err){
   console.log("domain1 handled this error ("+err.message+")");
});

//explicit binding 
domain1.add(emitter1);

emitter1.on('error',function(err){
   console.log("listener handled this error ("+err.message+")");
});

emitter1.emit('error',new Error('To be handled by listener'));

emitter1.removeAllListeners('error');

emitter1.emit('error',new Error('To be handled by domain1'));

var domain2 = domain.create();

domain2.on('error', function(err){
   console.log("domain2 handled this error ("+err.message+")");
});

//implicit binding
domain2.run(function(){
   var emitter2 = new EventEmitter();
   
   emitter2.emit('error',new Error('To be handled by domain2'));   
});


domain1.remove(emitter1);

emitter1.emit('error',new Error('Converted to exception. System will crash!'));

现在运行test.js看到的结果:

D:\yiibai_worksp\nodejs>node test.js

验证输出:

listener handled this error (To be handled by listener)
domain1 handled this error (To be handled by domain1)
domain2 handled this error (To be handled by domain2)

events.js:72
        throw er; // Unhandled 'error' event
              ^
Error: Converted to exception. System will crash!
    at Object.<anonymous> (C:\Nodejs_WorkSpace\test.js:42:23)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Function.Module.runMain (module.js:497:10)
    at startup (node.js:119:16)
    at node.js:929:3

Node.js - Web模块

Web服务器简介

Web服务器是用于处理使用HTTP协议请求并返回网页作为响应到客户端的软件应用程序。 Web服务器通常提供HTML文档并伴随着图片,样式表和脚本。大多数Web服务器还支持使用脚本语言作为服务器端脚本,或重定向到从数据库中获取数据的执行特定任务的应用程序服务器,执行复杂的逻辑等,Web服务器然后返回应用服务器到客户端输出。

Apache web服务器是最常见的一个网络服务器。它是一个开源项目。

路径定位过程

Web服务器映射使用URL,统一资源定位符的文件的路径。它可以是一个本地文件系统或一个外部/内部程序。例如:

客户端使用的浏览器发起一个URL请求:http://www.test-example-site.com/website/index.htm.

浏览器发起的一个请求是:

GET /website/index.htm HTTP /1.1
HOST www.test-example-site.com

Web服务器将追加路径到根目录。举个例子:根目录是home/www,那么实际路径将被转换为 home/www/website/index.htm.

Web网络架构介绍

Web应用程序的使用分为四层:

Web Architecture
  • Client - 此层是由Web浏览器,移动浏览器或应用程序,它可以发起HTTP请求到服务器。

  • Server - 这一层包括可拦截发出的客户端请求,并将其传递到由Web服务器响应。

  • Business - 这一层由应用程序服务器组成,它通过利用Web服务器执行动态任务。这一层交互通过数据库或某些外部程序数据层。

  • Data - 此层由数据库或任何来源的数据。

使用Node创建Web服务器

使用http.createServer方法创建HTTP服务器。通过它一个函数带参数请求和响应。编写一个示例实现返回一个请求页面。通过在8081端口上监听。

创建一个js文件名为server.js 在 D:\>yiibai_worksp\nodejs

File: server.js

//http module is required to create a web server
var http = require('http');
//fs module is required to read file from file system
var fs = require('fs');
//url module is required to parse the URL passed to server
var url = require('url');

//create the server
http.createServer(function (request, response) {  
   //parse the pathname containing file name
   var pathname = url.parse(request.url).pathname;
   //print the name of the file for which request is made.
   //if url is http://localhost:8081/test.htm then
   //pathname will be /test.html
   console.log("Request for " + pathname + " received.");
   //read the requested file content from file system
   fs.readFile(pathname.substr(1), function (err, data) {
      //if error occured during file read
      //send a error response to client
      //that web page is not found.
      if (err) {
         console.log(err.stack);
         // HTTP Status: 404 : NOT FOUND
         // Content Type: text/plain
         response.writeHead(404, {'Content-Type': 'text/html'});
      }else{	
         //Page found	  
         // HTTP Status: 200 : OK
         // Content Type: text/plain
         response.writeHead(200, {'Content-Type': 'text/html'});	
         // write the content of the file to response body
         response.write(data.toString());		
      }
      // send the response body 
      response.end();
   });   
}).listen(8081);
// console will print the message
console.log('Server running at http://127.0.0.1:8081/');

创建一个名为 test.html 的 html 文件在 D:\>yiibai_worksp\nodejs

File: test.html

<html>
<head>
<title>Sample Page</title>
</head>
<body>
    Hello World!
</body>
</html>

现在运行server.js看到的结果:

D:\yiibai_worksp\nodejs>node server.js

验证输出。服务器已经启动

Server running at http://127.0.0.1:8081/

发出请求到Node.js服务器

在任何浏览器中打开以下网址:http://127.0.0.1:8081/test.html,看看下面的结果。


验证服务器端的输出

Server running at http://127.0.0.1:8081/
Request for /test.html received.

使用Node创建Web客户端

Web客户端可以使用http模块来创建。参见下面的例子:

创建一个名为client.js的js文件在 D:\>yiibai_worksp\nodejs

File: client.js

//http module is required to create a web client
var http = require('http');

//options are to be used by request 
var options = {
   host: 'localhost',
   port: '8081',
   path: '/test.html'  
};


//callback function is used to deal with response
var callback = function(response){
   // Continuously update stream with data
   var body = '';
   response.on('data', function(data) {
      body += data;
   });
   response.on('end', function() {
      // Data received completely.
      console.log(body);
   });
}
//make a request to the server
var req = http.request(options, callback);
req.end();

现在运行client.js在不同的终端命令行,也就是在server.js以外终端上看到的结果:

D:\yiibai_worksp\nodejs>node client.js

验证输出:

<html>
<head>
<title>Sample Page</title>
</head>
<body>
    Hello World!
</body>
</html>

验证服务器端的输出

Server running at http://127.0.0.1:8081/
Request for /test.html received.
Request for /test.html received.

Node.js - Express应用程序

Express 概述

Express JS是用于创建Node JS的Web应用程序非常流行的Web应用程序框架。它提供了一个集成的环境便于快速开发基于Node的Web应用程序。Express框架是基于连接的中间件引擎并使用Jade HTML模板框架来做HTML模板。以下是一些Express 框架的核心功能:

  • 允许设立中间件来响应HTTP请求

  • 定义了用于执行基于HTTP方法和URL不同作用的路由表

  • 允许动态渲染基于参数传递给模板的HTML页面

安装Express

首先,使用NPM安装Express框架到全局,以便它可以使用Node终端来创建Web应用程序。

D:\yiibai_worksp\nodejs> npm install express -g

当npm完成下载,可以通过查看内容来验证,内容在 <user-directory>/npm/node_modules. 或输入以下命令:

D:\yiibai_worksp\nodejs> npm ls -g

将看到以下的输出:

C:\Documents and Settings\Administrator\Application Data\npm
+-- express@4.11.2
  +-- accepts@1.2.3
  | +-- mime-types@2.0.8
  | | +-- mime-db@1.6.1
  | +-- negotiator@0.5.0
  +-- content-disposition@0.5.0
  +-- cookie@0.1.2
  +-- cookie-signature@1.0.5
  +-- debug@2.1.1
  | +-- ms@0.6.2
  +-- depd@1.0.0
  +-- escape-html@1.0.1
  +-- etag@1.5.1
  | +-- crc@3.2.1
  +-- finalhandler@0.3.3
  +-- fresh@0.2.4
  +-- media-typer@0.3.0
  +-- merge-descriptors@0.0.2
  +-- methods@1.1.1
  +-- on-finished@2.2.0
  | +-- ee-first@1.1.0
  +-- parseurl@1.3.0
  +-- path-to-regexp@0.1.3
  +-- proxy-addr@1.0.6
  | +-- forwarded@0.1.0
  | +-- ipaddr.js@0.1.8
  +-- qs@2.3.3
  +-- range-parser@1.0.2
  +-- send@0.11.1
  | +-- destroy@1.0.3
  | +-- mime@1.2.11
  | +-- ms@0.7.0
  +-- serve-static@1.8.1
  +-- type-is@1.5.6
  | +-- mime-types@2.0.8
  |   +-- mime-db@1.6.1
  +-- utils-merge@1.0.0
  +-- vary@1.0.0

Express生成器

现在使用NPM安装express生成器。express生成器用于使用express命令创建应用程序框架。

D:\yiibai_worksp\nodejs> npm install express-generator -g

将看到以下的输出:

D:\yiibai_worksp\nodejs>npm install express-generator -g
C:\Users\Administrator\AppData\Roaming\npm\express -> C:\Users\Administrator\AppData\Roaming\npm\node_modules\express-generator\bin\expressexpress-generator@4.13.1 C:\Users\Administrator\AppData\Roaming\npm\node_modules
\express-generator
+-- sorted-object@1.0.0
+-- commander@2.7.1
+-- mkdirp@0.5.1 (minimist@0.0.8)

Hello world 示例

现在使用下面的命令创建一个示例应用程序 firstApplication:

D:\yiibai_worksp\nodejs> express firstApplication

将看到以下的输出:

   create : firstApplication
   create : firstApplication/package.json
   create : firstApplication/app.js
   create : firstApplication/public
   create : firstApplication/public/javascripts
   create : firstApplication/public/images
   create : firstApplication/public/stylesheets
   create : firstApplication/public/stylesheets/style.css
   create : firstApplication/routes
   create : firstApplication/routes/index.js
   create : firstApplication/routes/users.js
   create : firstApplication/views
   create : firstApplication/views/index.jade
   create : firstApplication/views/layout.jade
   create : firstApplication/views/error.jade
   create : firstApplication/bin
   create : firstApplication/bin/www

   install dependencies:
     $ cd firstApplication && npm install

   run the app:
     $ DEBUG=firstApplication:* ./bin/www

进入 firstApplication 文件夹并使用下面的命令来安装 firstApplication 的依赖关系:

D:\yiibai_worksp\nodejs\firstApplication> npm install

将看到以下的输出:

debug@2.1.2 node_modules\debug
+-- ms@0.7.0

cookie-parser@1.3.4 node_modules\cookie-parser
+-- cookie-signature@1.0.6
+-- cookie@0.1.2

morgan@1.5.1 node_modules\morgan
+-- basic-auth@1.0.0
+-- depd@1.0.0
+-- on-finished@2.2.0 (ee-first@1.1.0)

serve-favicon@2.2.0 node_modules\serve-favicon
+-- ms@0.7.0
+-- fresh@0.2.4
+-- parseurl@1.3.0
+-- etag@1.5.1 (crc@3.2.1)

jade@1.9.2 node_modules\jade
+-- character-parser@1.2.1
+-- void-elements@2.0.1
+-- commander@2.6.0
+-- mkdirp@0.5.0 (minimist@0.0.8)
+-- transformers@2.1.0 (promise@2.0.0, css@1.0.8, uglify-js@2.2.5)
+-- with@4.0.1 (acorn-globals@1.0.2, acorn@0.11.0)
+-- constantinople@3.0.1 (acorn-globals@1.0.2)

express@4.12.2 node_modules\express
+-- merge-descriptors@1.0.0
+-- cookie-signature@1.0.6
+-- methods@1.1.1
+-- cookie@0.1.2
+-- fresh@0.2.4
+-- utils-merge@1.0.0
+-- range-parser@1.0.2
+-- escape-html@1.0.1
+-- parseurl@1.3.0
+-- vary@1.0.0
+-- content-type@1.0.1
+-- finalhandler@0.3.3
+-- serve-static@1.9.1
+-- content-disposition@0.5.0
+-- path-to-regexp@0.1.3
+-- depd@1.0.0
+-- qs@2.3.3
+-- on-finished@2.2.0 (ee-first@1.1.0)
+-- etag@1.5.1 (crc@3.2.1)
+-- proxy-addr@1.0.6 (forwarded@0.1.0, ipaddr.js@0.1.8)
+-- send@0.12.1 (destroy@1.0.3, ms@0.7.0, mime@1.3.4)
+-- accepts@1.2.4 (negotiator@0.5.1, mime-types@2.0.9)
+-- type-is@1.6.0 (media-typer@0.3.0, mime-types@2.0.9)

body-parser@1.12.0 node_modules\body-parser
+-- content-type@1.0.1
+-- bytes@1.0.0
+-- raw-body@1.3.3
+-- depd@1.0.0
+-- qs@2.3.3
+-- iconv-lite@0.4.7
+-- on-finished@2.2.0 (ee-first@1.1.0)
+-- type-is@1.6.0 (media-typer@0.3.0, mime-types@2.0.9)

在这里,express生成器创建了一个完整的应用程序结构,可以验证在 firstApplication 文件夹中(Nodejs_WorkSpace文件夹下面)里创建的文件夹/文件:

.
+-- app.js
+-- bin
|   +-- www
+-- package.json
+-- public
|   +-- images
|   +-- javascripts
|   +-- stylesheets
|       +-- style.css
+-- routes
|   +-- index.js
|   +-- users.js
+-- views
    +-- error.jade
    +-- index.jade
    +-- layout.jade
  • package.json 是应用程序描述符文件包含在依赖项列表中,应用程序使用 Node 的其他属性

  • app.js 包含了服务器的初始化代码

  • bin是用于存储在生产模式下的应用程序

  • public用于存储图像,样式表和JavaScript文件

  • routes 包含路由处理程序

  • views 包含HTML模板来生成Web应用各种视图

第一个应用程序

app.js 是基于 express 的应用核心引擎。让我们更新默认app.js包括端口信息,并使用它来创建一个服务器。添加以下行到app.js :

//set the server port
app.set('port', process.env.PORT || 3000);

//create the server 
http.createServer(app).listen(app.get('port'), function(){
  console.log('Express server listening on port ' + app.get('port'));
});

更新 app.js

以下是app.js文件的全部内容

更新 app.js 文件的内容在 D:\>yiibai_worksp\nodejs\firstApplication.

File: app.js

var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var http = require('http');

var routes = require('./routes/index');
var users = require('./routes/users');

var app = express();

// view engine setup
app.set('port', process.env.PORT || 8891);
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

// uncomment after placing your favicon in /public
//app.use(favicon(__dirname + '/public/favicon.ico'));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', routes);
app.use('/users', users);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
   var err = new Error('Not Found');
   err.status = 404;
   next(err);
});

// error handlers

// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
   app.use(function(err, req, res, next) {
      res.status(err.status || 500);
      res.render('error', {
         message: err.message,
         error: err
      });
   });
}

// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
   res.status(err.status || 500);
   res.render('error', {
      message: err.message,
      error: {}
   });
});

http.createServer(app).listen(app.get('port'), function(){
  console.log('Express server listening on port ' + app.get('port'));
});

module.exports = app;

现在运行app.js看到的结果:

D:\yiibai_worksp\firstApplication>node app

验证输出,服务器已经启动

Express server listening on port 8891

发出一个请求到firstApplication

在浏览器打开网址:http://localhost:8891/,看看下面的结果:

 

再次在浏览器打开网址:http://localhost:8891/users,看看下面的结果:


基本的路由

在app.js下面的代码绑定两个Route处理。

var routes = require('./routes/index');
var users = require('./routes/users');
...
app.use('/', routes);
app.use('/users', users);
  • routes - 路由(index.js), 路由处理程序处理所有请求,主页发出通过 localhost:8891

  • users - users (users.js), 路由处理来处理所有发出的请求 /users 通过 localhost:3000/users

以下是对应折代码:D:\>yiibai_worksp\nodejs\firstApplication\routes\index.js是由express生成器创建。

var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { title: 'Express' });
});

module.exports = router;

当Node服务器接收到一个主页的请求,express路由器渲染索引页是使用模板index.jade同时传递参数title的值为'Express'。以下是模板代码内容:D:\>yiibai_worksp\nodejs\firstApplication\views\index.jade

extends layout

block content
  h1= title
  p Welcome to #{title}

Node.js - 加强第一个应用程序

概述

在这篇文章中,我们将加强Express第一个应用程序,创建 Express JS 应用程序:

  • 显示所有用户的列表

  • 显示一个特定的用户的细节

  • 添加新的用户的详细信息

第1步:创建一个基于JSON数据库

首先,让我们创建一个用户示例JSON基础数据库。

创建一个名为 user.json 在一个 JSON 文件于 D:\>yiibai_worksp\nodejs\firstApplication

File: user.json

{
   "user1" : {
      "name" : "mahesh",
	  "password" : "password1",
	  "profession" : "teacher"
   },
   "user2" : {
      "name" : "suresh",
	  "password" : "password2",
	  "profession" : "librarian"
   },
   "user3" : {
      "name" : "newuser",
	  "password" : "password3",
	  "profession" : "clerk"
   }
}

第2步:创建特定的用户Jade视图

在创建一个 user 目录在 D:\>yiibai_worksp\node\firstApplication\views 如下视图:index.jade - 查看显示所有用户的列表。

  • new.jade - 视图显示一个表单来添加一个新用户。

  • profile.jade - 查看显示的用户的细节

创建一个index.jade在 D:\>yiibai_worksp\nodejs\firstApplication\views\users.

File: index.jade

h1 Users

p 
   a(href="/users/new/") Create new user
ul
   - for (var username in users) {
   li 
      a(href="/users/" + encodeURIComponent(username))= users[username].name
   - };

创建一个文件 new.jade 在 D:\>yiibai_worksp\nodejs\firstApplication\views\users.

File: new.jade

h1 New User

form(method="POST" action="/Users/addUser")
	P 
		label(for="name") Name

		input#name(name="name")
	P 
		label(for="password") Password

		input#name(name="password")
	P 
		label(for="profession") Profession

		input#name(name="profession")
	P
		input(type="submit", value="Create")

创建一个文件 profile.jade 在 D:\>yiibai_worksp\nodejs\firstApplication\views\users.

File: profile.jade

h1 Name: #{user.name}

h2 Profession: #{user.profession}

第3步:更新用户路由处理 users.js

更新文件 users.js 在 D:\>yiibai_worksp\nodejs\firstApplication\routes.

File: users.js

var express = require('express');
var router = express.Router();

var users = require('../users.json');
/* GET users listing. */
router.get('/', function(req, res) {
   res.render('users/index', { title: 'Users',users:users });
});

/* Get form to add a new user*/
router.get('/new', function(req, res) {
   res.render('users/new', { title: 'New User'});
});

/* Get detail of a new user */
router.get('/:name', function(req, res, next) {
   var user = users[req.params.name] 
   if(user){
      res.render('users/profile', { title: 'User Profile', user:user});
   }else{
      next();
   }   
});

/* post the form to add new user */
router.post('/addUser', function(req, res, next) {   
   if(users[req.body.name]){
      res.send('Conflict', 409);
   }else{
     users[req.body.name] = req.body;
	 res.redirect('/users/');
   }   
});

module.exports = router;

现在运行app.js看到的结果:

D:\yiibai_worksp\nodejs\firstApplication>node app

验证输出,服务器已经启动

Express server listening on port 3000

发出请求到firstApplication得到所有用户的列表。在浏览器打开网址:http://localhost:8891/users,看看下面的结果。 


点击创建新的用户链接看到表单。


提交表单并查看更新列表。


点击查看用户的详细。


可以检查服务器状态如下:

D:\yiibai_worksp\nodejs\firstApplication>node app
Express server listening on port 3000
GET /users/ 200 809.161 ms - 201
GET /users/new/ 304 101.627 ms - -
GET /users/new/ 304 33.496 ms - -
POST /Users/addUser 302 56.206 ms - 70
GET /users/ 200 43.548 ms - 245
GET /users/naresh 200 12.313 ms - 47

Node.js - Restful API

什么是REST架构?

REST表示代表状态传输。REST是基于Web标准的体系结构并使用HTTP协议。它围绕着资源,其中每一个组件是资源,资源是由一个共同的接口使用HTTP的标准方法获得。REST首先是由Roy Fielding在2000年推出。

在REST架构中,REST服务器只是提供获取资源,REST客户端访问和修改的资源。这里每个资源由URI标识的/全局ID。REST使用不同方式表示资源,如文本,JSON,XML。 JSON是最流行的一种。

HTTP 方法

以下四个HTTP方法通常用在基于的REST架构。

  • GET - 提供资源的只读访问

  • PUT - 用于创建一个新的资源

  • DELETE - 用于删除资源

  • POST - 用于更新现有的一个资源或创建新的资源

RESTful Web服务介绍

Web服务是用于交换应用程序或系统之间的数据开放的协议和标准的集合。写在各种编程语言的软件应用, 并在各种平台上运行可以使用Web服务以类似的进程间通信的方式在一台计算机上,通过计算机网络交换数据,如互联网。这种互操作性(如Java和Python,或Windows和Linux应用程序之间),是由于使用了开放标准。

基于REST架构的Web服务称为RESTful Web服务。这些Web服务使用HTTP方法来实现REST架构的概念。RESTful Web服务通常定义的URI,统一资源标识符的服务,提供资源的表示,如JSON,并设置HTTP方法。

为一个库创建RESTful

现在,我们将加强express示例应用程序,以下创建一个Web服务来用户管理:

Sr. No. URI HTTP 方法 POST body 结果
1 /users/ GET empty 显示所有用户的列表
2 /users/addUser POST JSON String 添加新的用户的详细信息
3 /users/:id GET empty 用户的查看详细

获取所有用户信息

首先,让我们更新用户的样本基于JSON的数据库。

更新JSON文件名为user.json在 D:\>yiibai_worksp\nodejs\firstApplication.

File: user.json

{
   "user1" : {
      "name" : "mahesh",
	  "password" : "password1",
	  "profession" : "teacher",
	  "id": 1
   },
   "user2" : {
      "name" : "suresh",
	  "password" : "password2",
	  "profession" : "librarian",
	  "id": 2
   },
   "user3" : {
      "name" : "ramesh",
	  "password" : "password3",
	  "profession" : "clerk",
	  "id": 3
   }
}

当客户端发送GET请求/用户,服务器应该发送一个包含所有用户的响应。更新用户的路由处理,users.js

更新 users.js  D:\>yiibai_worksp\nodejs\firstApplication\routes.

File: users.js

/* GET users listing. */
router.get('/', function(req, res) {
   res.send({ title: 'Users',users:users });
});

添加新的用户的详细信息

当客户端发送一个POST请求 /users/addUser 包含JSON字符串,服务器应该发送一个响应声明状态。更新用户的路由处理users.js

更新 users.js 在 D:\>Nodejs_WorkSpace\firstApplication\routes.

File: users.js

/*add a user*/
router.post('/addUser', function(req, res, next) { 
    var body = '';
    req.on('data', function (data) {
        body += data;
    });
    req.on('end', function () {        
	   var json = JSON.parse(body);	   
       users["user"+json.id] = body;
	   res.send({ Message: 'User Added'});      
    });   	   
});

显示新用户的详细信息

当客户端发送GET请求到/users并带有id,服务器响应返回包含该用户的详细信息。更新用户的路由处理程序:users.js

更新 users.js 在 D:\>yiibai_worksp\nodejs\firstApplication\routes,如下:

File: users.js

router.get('/:id', function(req, res, next) {  
   var user = users["user" + req.params.id] 
   if(user){
      res.send({ title: 'User Profile', user:user});
   }else{
      res.send({ Message: 'User not present'});
   }   
});

完整的users.js 代码

File: users.js

var express = require('express');
var router = express.Router();

var users = require('../users.json');
/* GET users listing. */
router.get('/', function(req, res) {
   res.send({ title: 'Users',users:users });
});

router.get('/:id', function(req, res, next) {
   console.log(req.params)
   var user = users["user" + req.params.id] 
   if(user){
      res.send({ title: 'User Profile', user:user});
   }else{
      res.send({ Message: 'User not present'});
   }   
});

router.post('/addUser', function(req, res, next) { 
    var body = '';
    req.on('data', function (data) {
        body += data;
    });
    req.on('end', function () {        
	   var json = JSON.parse(body);	   
       users["user"+json.id] = body;
	   res.send({ Message: 'User Added'});      
    });   	   
});

module.exports = router;

输出

我们使用一个Chrome浏览器的扫插件:Postman, 来测试我们编写的 webservices,安装Postman需要到以下网址下载(可能需要翻墙):https://chrome.google.com/webstore/detail/postman-interceptor/aicmkgpgakddgnaphhhpliifpcfhicfo 安装好这个插件后,打开后显示如下图:

 

现在运行app.js看到的结果:

D:\yiibai_worksp\nodejs\firstApplication> node app

验证输出。服务器已经启动

Express server listening on port 8891

提出一个请求到firstApplication获取所有用户的信息列表。把 http://localhost:8891/users 到POSTMAN 地址栏中,并使用GET请求,看到下面的结果。

发出一个请求到 firstApplication 以添加一个新用户。把 http://localhost:8891/users/addUser 放入到POSTMAN中并使用POST方式提交,看看下面的结果。

添加JSON数据格式,并选择POST方法。

{"name":"rakesh","password":"password4","profession":"teacher","id":5}
 
点右侧的“Send”按钮,返回结果如下:
 

发出一个请求 firstApplication 获得用户。把 http://localhost:8891/users/1 在POSTMAN中找打开并用GET请求,看看下面的结果。

Node.js - 缩放应用

Node是在单线程模式下运行,它使用事件驱动的模式来处理并发。它还有助于创建子进程来利用并行处理在多核CPU的系统。

子进程始终有三个流child.stdin,child.stdout和child.stderr ,可以与父进程共享标准输入输出流。它们能够通过一个单独的流对象管道输送。

主要有三种方法来创建子进程。

  • exec - child_process.exec方法在shell/控制台运行一个命令并缓冲输出。

  • spawn - child_process.spawn由给定的命令启动一个新的进程

  • fork - child_process.fork方法是spawn() 创建Node进程的一个特例。

exec() 方法

child_process.exec方法在shell运行一个命令并缓冲输出。它签名如下:

child_process.exec(command[, options], callback)
  • command String要运行的命令,用空格分隔参数

  • options 对象

    • cwd 当前工作目录子进程的字符串

    • env 对象环境键值对

    • encoding 字符串(缺省:'utf8'')

    • shell 字符串Shell来执行(默认命令:在UNIX上在“/bin/sh”,在Windows为cmd.exe“,在shell中应该知道在Windows或UNIX/S/C-c开关。在Windows中,命令行解析应与cmd.exe兼容)

    • timeout 数字(默认值: 0)

    • maxBuffer 数字(默认值: 200*1024)

    • killSignal 字符串(默认值: 'SIGTERM')

    • uid 数字设置进程的用户身份

    • gid 数字设置进程的组标识

  • callback 当进程终止函数调用输出

    • error Error

    • stdout Buffer

    • stderr Buffer

  • Return:ChildProcess对象

exec() 返回一个缓冲器的最大尺寸,并等待该进程结束,并试图在一次返回所有缓冲的数据

示例

创建两个JS文件名为worker.js和master.js在 D:\>yiibai_worksp\nodejs

File: worker.js

console.log("Child Process "+ process.argv[2] +" executed." );

File: master.js

const fs = require('fs');
const child_process = require('child_process');

for(var i=0; i<3; i++) {
   var workerProcess = child_process.exec('node worker.js '+i,
      function (error, stdout, stderr) {
         if (error) {
            console.log(error.stack);
            console.log('Error code: '+error.code);
            console.log('Signal received: '+error.signal);
         }
         console.log('stdout: ' + stdout);
         console.log('stderr: ' + stderr);
      });

      workerProcess.on('exit', function (code) {
      console.log('Child process exited with exit code '+code);
   });
}

现在运行master.js看到的结果:

D:\yiibai_worksp\nodejs> node master.js

验证输出。服务器已经启动

Child process exited with exit code 0
stdout: Child Process 1 executed.

stderr:
Child process exited with exit code 0
stdout: Child Process 0 executed.

stderr:
Child process exited with exit code 0
stdout: Child Process 2 executed.

spawn() 方法

child_process.spawn方法由给定的命令启动一个新的进程。它具有以下签名

child_process.spawn(command[, args][, options])
  • command 字符串 - 要运行的命令

  • args 字符串参数数组列表

  • options 对象

    • cwd 当前工作目录子进程的字符串

    • env 对象环境键值对

    • stdio 数组|子字符串的标准输入输出配置

    • customFds Array不推荐文件描述符由子进程使用标准输入输出

    • detached boolean值 - 子进程是一个进程组头进程

    • uid 数字设置进程的用户身份

    • gid Number设置进程的组标识

  • Return: ChildProcess对象

spawn() 返回流(标准输出与标准错误),并会使用当该过程返回大数据量。spawn()即开始接收到响应进程开始执行。

示例

创建两个JS文件名为worker.js和master.js在  D:\>yiibai_worksp\nodejs

File: worker.js

console.log("Child Process "+ process.argv[2] +" executed." );

File: master.js

const fs = require('fs');
const child_process = require('child_process');
 
for(var i=0; i<3; i++) {
   var workerProcess = child_process.spawn('node', ['worker.js', i]);

   workerProcess.stdout.on('data', function (data) {
      console.log('stdout: ' + data);
   });

   workerProcess.stderr.on('data', function (data) {
      console.log('stderr: ' + data);
   });

   workerProcess.on('close', function (code) {
      console.log('child process exited with code ' + code);
   });
}

现在运行master.js看到的结果:

D:\yiibai_worksp\nodejs> node master.js

验证输出。服务器已经启动

stdout: Child Process 0 executed.

child process exited with code 0
stdout: Child Process 2 executed.

child process exited with code 0
stdout: Child Process 1 executed.

child process exited with code 0

fork 方法

child_process.fork方法是spawn() 的一个特例用来创建Node进程。它具有以下签名

child_process.fork(modulePath[, args][, options])
  • modulePath 字符串- 模块运行所在的子进程

  • args 字符串参数数组列表

  • options 对象

    • cwd 当前工作目录子进程的字符串

    • env 对象环境键值对

    • execPath 字符串可执行用于创建子进程

    • execArgv 传递给程序的可执行字符串参数数组列表(默认值:process.execArgv)

    • silent Boolean如果为true,stdin, stdout, 和 stderr 的标准错误将被输送到父进程,否则它们将继承自父,详情参见“管道”和“继承” 选项spawn()的标准输入输出(默认为false)

    • uid 数字设置进程的用户身份

    • gid 数字设置进程的组标识

  • Return: ChildProcess 对象

fork返回对象有内置的通信信道,除了具有正常的ChildProcess实例的所有方法。

示例

创建两个JS文件名为 worker.js 和 master.js 在 D:\>yiibai_worksp\nodejs

File: worker.js

console.log("Child Process "+ process.argv[2] +" executed." );

File: master.js

const fs = require('fs');
const child_process = require('child_process');
 
for(var i=0; i<3; i++) {
   var worker_process = child_process.fork("worker.js", [i]);	

   worker_process.on('close', function (code) {
      console.log('child process exited with code ' + code);
   });
}

现在运行master.js看到的结果:

D:\yiibai_worksp> node master.js

验证输出。服务器已经启动

Child Process 0 executed.
Child Process 1 executed.
Child Process 2 executed.
child process exited with code 0
child process exited with code 0
child process exited with code 0