Node.js开发Web后台服务
发布日期:2021-05-09 00:51:57 浏览次数:17 分类:博客文章

本文共 31677 字,大约阅读时间需要 105 分钟。

一、简介

Node.js 是一个基于Google Chrome V8 引擎的 JavaScript 运行环境。Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型,使其轻量又高效。Node.js 的包管理器 npm,是全球最大的开源库生态系统。

能方便地搭建响应速度快、易于扩展的网络应用,Node.js 使用事件驱动, 非阻塞I/O 模型而得以轻量和高效,非常适合在分布式设备上运行的数据密集型的实时应用。

官网:

中文:、

API:

简单说Node.js就是运行在服务器端的JavaScript,是现在流行的语言中能同时运行在前端与后台的程序语言,你可以把JavaScript想像成Java与C#。相关技术:

数据库:MongoDB,非关系型数据库,NoSQL(Not only SQL)

MVC框架:AngularJS

Web服务器:Express

模板引擎:jade、ejs、htmljs、swig、hogan.js

二、搭建Node.js开发环境

2.1、安装Node.js

Node.js一步一步按提示安装即可,如果安装失败就手动安装,将Node.js的安装位置配置到环境变量的path中。

安装完成后启动命令行,测试:

2.2、安装IDE开发Node.js插件

如果不使用IDE开发项目效率较低,在很多主流的集成开发环境(IDE)中都可以安装插件支持Node.js开发,如Eclipse,这里我们以HBuilder为例:

启动HBuilder->工具->插件安装

安装成功后就可以新建Node.js项目了:

这里选择Hello World,新建好的项目如下:

hello-world-server.js文件就是一个简单的web服务器,右键选择“运行方式”->"Node Application"

控制台提示“Server running at http://127.0.0.1:1337/”在浏览器查看的效果如下:

三、第一个Node.js程序

在上面的示例中,我们是通过IDE完成编译与运行的,其实手动运行也可以,比如编写一段代码如下:

server.js

//依赖一个http模块,相当于java中的import,与C#中的usingvar http = require('http');//创建一个服务器对象server = http.createServer(function (req, res) {//设置请求成功时响应头部的MIME为纯文本res.writeHeader(200, {"Content-Type": "text/plain"});//向客户端输出字符res.end("Hello World\n");});//让服务器监听本地8000端口开始运行server.listen(8000,'127.0.0.1');console.log("server is runing at 127.0.0.1:8000");

在node环境下解释运行:

运行结果:

引入 required 模块:我们可以使用 require 指令来载入 Node.js 模块。

创建服务器:服务器可以监听客户端的请求,类似于TomCat、IIS、Apache 、Nginx 等 HTTP 服务器。
接收请求与响应请求 服务器很容易创建,客户端可以使用浏览器或终端发送 HTTP 请求,服务器接收请求后返回响应数据。

第一行请求(require)Node.js 自带的 http 模块,并且把它赋值给 http 变量。

接下来我们调用 http 模块提供的函数: createServer 。这个函数会返回 一个对象,这个对象有一个叫做 listen 的方法,这个方法有一个数值参数, 指定这个 HTTP 服务器监听的端口号。

四、NPM(Node.js包管理器)

NPM是随同NodeJS一起安装的包管理工具,能解决NodeJS代码部署上的很多问题,常见的使用场景有以下几种:

a)、允许用户从NPM服务器下载别人编写的第三方包到本地使用。
b)、允许用户从NPM服务器下载并安装别人编写的命令行程序到本地使用。
c)、允许用户将自己编写的包或命令行程序上传到NPM服务器供别人使用。

官网:

4.1、查看npm版本

由于新版的nodejs已经集成了npm,所以之前npm也一并安装好了。同样可以通过输入 "npm -v" 来测试是否成功安装。命令如下,出现版本提示表示安装成功:

4.2、升级npm

如果你安装的是旧版本的 npm,可以很容易得通过 npm 命令来升级

npm install npm -g

 

4.3、安装模块

npm install <Module Name> -参数

如果带参数-g表示全局安装,否则只是安装到某个目录下。

以下实例,我们使用 npm 命令安装常用的 Node.js web框架模块 express

4.4、卸载模块

我们可以使用以下命令来卸载 Node.js 模块。

npm uninstall <Module Name>

如先使用安装指令安装bootstrap:

npm install bootstrap

再使用卸载指令删除模块:

npm uninstall bootstrap

可以到 /node_modules/ 目录下查看包是否还存在

4.5、模块列表

使用模块列表命令可以方便的看到当前项目中依赖的包:

npm ls

4.6、更新模块

我们可以使用以下命令更新模块:

npm update 模块名称
npm up -g 模块名称

4.7、搜索模块

npm search 模块名称

4.8、NPM 常用命令

除了本章介绍的部分外,NPM还提供了很多功能,package.json里也有很多其它有用的字段。

除了可以在npmjs.org/doc/查看官方文档外,这里再介绍一些NPM常用命令。
NPM提供了很多命令,例如install和publish,使用npm help可查看所有命令。
NPM提供了很多命令,例如install和publish,使用npm help可查看所有命令。
使用npm help <command>可查看某条命令的详细帮助,例如npm help install。
在package.json所在目录下使用npm install . -g可先在本地安装当前命令行程序,可用于发布前的本地测试。
使用npm update <package>可以把当前目录下node_modules子目录里边的对应模块更新至最新版本。
使用npm update <package> -g可以把全局安装的对应命令行程序更新至最新版。
使用npm cache clear可以清空NPM本地缓存,用于对付使用相同版本号发布新版本代码的人。
使用npm unpublish <package>@<version>可以撤销发布自己发布过的某个版本代码。

4.9、更换NPM 镜像

因为npm的服务器在国外,在网络状态不好的情况下引入一个模块会因为网络延迟而失败,可以更换成国内速度更快的镜像服务器,这里以使用淘宝 NPM 镜像()为例:

npm install -g cnpm --registry=https://registry.npm.taobao.org

这样就可以使用 cnpm 命令来安装模块了:

$ cnpm install [name]

这是一个完整 npmjs.org 镜像,你可以用此代替官方版本(只读),同步频率目前为 10分钟 一次以保证尽量与官方服务同步。

如是安装失败,可以试试:

alias cnpm="npm --registry=https://registry.npm.taobao.org \--cache=$HOME/.npm/.cache/cnpm \--disturl=https://npm.taobao.org/dist \--userconfig=$HOME/.cnpmrc"# Or alias it in .bashrc or .zshrc$ echo '\n#alias for cnpm\nalias cnpm="npm --registry=https://registry.npm.taobao.org \  --cache=$HOME/.npm/.cache/cnpm \  --disturl=https://npm.taobao.org/dist \  --userconfig=$HOME/.cnpmrc"' >> ~/.zshrc && source ~/.zshrc

4.10、package.json

4.10.1、概述

每个项目的根目录下面,一般都有一个package.json文件,定义了这个项目所需要的各种模块,以及项目的配置信息(比如名称、版本、许可证等元数据)。npm install命令根据这个配置文件,自动下载所需的模块,也就是配置项目所需的运行和开发环境。

下面是一个最简单的package.json文件,只定义两项元数据:项目名称和项目版本。

{  "name" : "xxx",  "version" : "0.0.0",}

package.json文件就是一个JSON对象,该对象的每一个成员就是当前项目的一项设置。比如name就是项目名称,version是版本(遵守“大版本.次要版本.小版本”的格式)。

下面是一个更完整的package.json文件。

{    "name": "Hello World",    "version": "0.0.1",    "author": "张三",    "description": "第一个node.js程序",    "keywords":["node.js","javascript"],    "repository": {        "type": "git",        "url": "https://path/to/url"    },    "license":"MIT",    "engines": {"node": "0.10.x"},    "bugs":{"url":"http://path/to/bug","email":"bug@example.com"},    "contributors":[{"name":"李四","email":"lisi@example.com"}],    "scripts": {        "start": "node index.js"    },    "dependencies": {        "express": "latest",        "mongoose": "~3.8.3",        "handlebars-runtime": "~1.0.12",        "express3-handlebars": "~0.5.0",        "MD5": "~1.2.0"    },    "devDependencies": {        "bower": "~1.2.8",        "grunt": "~0.4.1",        "grunt-contrib-concat": "~0.3.0",        "grunt-contrib-jshint": "~0.7.2",        "grunt-contrib-uglify": "~0.2.7",        "grunt-contrib-clean": "~0.5.0",        "browserify": "2.36.1",        "grunt-browserify": "~1.3.0",    }}

下面详细解释package.json文件的各个字段。

成熟示例:

{  "_args": [    [      {        "raw": "md5@^2.2.1",        "scope": null,        "escapedName": "md5",        "name": "md5",        "rawSpec": "^2.2.1",        "spec": ">=2.2.1 <3.0.0",        "type": "range"      },      "E:\\NF\\vue\\demos\\demo02"    ]  ],  "_from": "md5@>=2.2.1 <3.0.0",  "_id": "md5@2.2.1",  "_inCache": true,  "_location": "/md5",  "_nodeVersion": "4.4.5",  "_npmOperationalInternal": {    "host": "packages-16-east.internal.npmjs.com",    "tmp": "tmp/md5-2.2.1.tgz_1472679629604_0.48944878415204585"  },  "_npmUser": {    "name": "pvorb",    "email": "paul@vorba.ch"  },  "_npmVersion": "3.9.3",  "_phantomChildren": {},  "_requested": {    "raw": "md5@^2.2.1",    "scope": null,    "escapedName": "md5",    "name": "md5",    "rawSpec": "^2.2.1",    "spec": ">=2.2.1 <3.0.0",    "type": "range"  },  "_requiredBy": [    "#USER",    "/"  ],  "_resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz",  "_shasum": "53ab38d5fe3c8891ba465329ea23fac0540126f9",  "_shrinkwrap": null,  "_spec": "md5@^2.2.1",  "_where": "E:\\NF\\vue\\demos\\demo02",  "author": {    "name": "Paul Vorbach",    "email": "paul@vorba.ch",    "url": "http://paul.vorba.ch"  },  "bugs": {    "url": "https://github.com/pvorb/node-md5/issues"  },  "contributors": [    {      "name": "salba"    }  ],  "dependencies": {    "charenc": "~0.0.1",    "crypt": "~0.0.1",    "is-buffer": "~1.1.1"  },  "description": "js function for hashing messages with MD5",  "devDependencies": {    "mocha": "~2.3.4"  },  "directories": {},  "dist": {    "shasum": "53ab38d5fe3c8891ba465329ea23fac0540126f9",    "tarball": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz"  },  "gitHead": "5536a25dbe856b25d9963fd47da5eb4e1bca4250",  "homepage": "https://github.com/pvorb/node-md5#readme",  "license": "BSD-3-Clause",  "main": "md5.js",  "maintainers": [    {      "name": "coolaj86",      "email": "coolaj86@gmail.com"    },    {      "name": "pvorb",      "email": "paul@vorba.ch"    }  ],  "name": "md5",  "optionalDependencies": {},  "readme": "ERROR: No README data found!",  "repository": {    "type": "git",    "url": "git://github.com/pvorb/node-md5.git"  },  "scripts": {    "test": "mocha"  },  "tags": [    "md5",    "hash",    "encryption",    "message digest"  ],  "version": "2.2.1"}
View Code
{  "_args": [    [      {        "raw": "charenc@~0.0.1",        "scope": null,        "escapedName": "charenc",        "name": "charenc",        "rawSpec": "~0.0.1",        "spec": ">=0.0.1 <0.1.0",        "type": "range"      },      "E:\\NF\\vue\\demos\\demo02\\node_modules\\md5"    ]  ],  "_from": "charenc@>=0.0.1 <0.1.0",  "_id": "charenc@0.0.2",  "_inCache": true,  "_location": "/charenc",  "_nodeVersion": "4.4.5",  "_npmOperationalInternal": {    "host": "packages-12-west.internal.npmjs.com",    "tmp": "tmp/charenc-0.0.2.tgz_1482450158427_0.9801697849761695"  },  "_npmUser": {    "name": "pvorb",    "email": "paul@vorba.ch"  },  "_npmVersion": "3.9.3",  "_phantomChildren": {},  "_requested": {    "raw": "charenc@~0.0.1",    "scope": null,    "escapedName": "charenc",    "name": "charenc",    "rawSpec": "~0.0.1",    "spec": ">=0.0.1 <0.1.0",    "type": "range"  },  "_requiredBy": [    "/md5"  ],  "_resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz",  "_shasum": "c0a1d2f3a7092e03774bfa83f14c0fc5790a8667",  "_shrinkwrap": null,  "_spec": "charenc@~0.0.1",  "_where": "E:\\NF\\vue\\demos\\demo02\\node_modules\\md5",  "author": {    "name": "Paul Vorbach",    "email": "paul@vorb.de",    "url": "http://vorb.de"  },  "bugs": {    "url": "https://github.com/pvorb/node-charenc/issues"  },  "dependencies": {},  "description": "character encoding utilities",  "devDependencies": {},  "directories": {},  "dist": {    "shasum": "c0a1d2f3a7092e03774bfa83f14c0fc5790a8667",    "tarball": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz"  },  "engines": {    "node": "*"  },  "gitHead": "01d66efb429d0cb242b2dd5af2ce338554fd3e54",  "homepage": "https://github.com/pvorb/node-charenc#readme",  "license": "BSD-3-Clause",  "main": "charenc.js",  "maintainers": [    {      "name": "pvorb",      "email": "paul@vorb.de"    }  ],  "name": "charenc",  "optionalDependencies": {},  "readme": "ERROR: No README data found!",  "repository": {    "type": "git",    "url": "git://github.com/pvorb/node-charenc.git"  },  "scripts": {},  "tags": [    "utf8",    "binary",    "byte",    "string"  ],  "version": "0.0.2"}
View Code

 

4.10.2、scripts字段

scripts指定了运行脚本命令的npm命令行缩写,比如start指定了运行npm run start时,所要执行的命令。

下面的设置指定了npm run preinstallnpm run postinstallnpm run startnpm run test时,所要执行的命令。

"scripts": {    "preinstall": "echo here it comes!",    "postinstall": "echo there it goes!",    "start": "node index.js",    "test": "tap test/*.js"}

4.10.3、dependencies字段,devDependencies字段

dependencies字段指定了项目运行所依赖的模块,devDependencies指定项目开发所需要的模块。

它们都指向一个对象。该对象的各个成员,分别由模块名和对应的版本要求组成,表示依赖的模块及其版本范围。

{  "devDependencies": {    "browserify": "~13.0.0",    "karma-browserify": "~5.0.1"  }}

对应的版本可以加上各种限定,主要有以下几种:

  • 指定版本:比如1.2.2,遵循“大版本.次要版本.小版本”的格式规定,安装时只安装指定版本。
  • 波浪号(tilde)+指定版本:比如~1.2.2,表示安装1.2.x的最新版本(不低于1.2.2),但是不安装1.3.x,也就是说安装时不改变大版本号和次要版本号。
  • 插入号(caret)+指定版本:比如?1.2.2,表示安装1.x.x的最新版本(不低于1.2.2),但是不安装2.x.x,也就是说安装时不改变大版本号。需要注意的是,如果大版本号为0,则插入号的行为与波浪号相同,这是因为此时处于开发阶段,即使是次要版本号变动,也可能带来程序的不兼容。
  • latest:安装最新版本。

package.json文件可以手工编写,也可以使用npm init命令自动生成。

$ npm init

这个命令采用互动方式,要求用户回答一些问题,然后在当前目录生成一个基本的package.json文件。所有问题之中,只有项目名称(name)和项目版本(version)是必填的,其他都是选填的。

有了package.json文件,直接使用npm install命令,就会在当前目录中安装所需要的模块。

$ npm install

如果一个模块不在package.json文件之中,可以单独安装这个模块,并使用相应的参数,将其写入package.json文件之中。

$ npm install express --save$ npm install express --save-dev

上面代码表示单独安装express模块,--save参数表示将该模块写入dependencies属性,--save-dev表示将该模块写入devDependencies属性。

示例:

 package.json

{  "name": "demo001",  "version": "1.1.2",  "description": "vueclidemo",  "main": "index.js",  "scripts": {    "test": "echo \"Error: no test specified\" && exit 1",    "start": "node index.js"  },  "keywords": [    "vue",    "vue-cli"  ],  "author": "zhangguo",  "license": "MIT",  "repository": {    "type": "git",    "url": "https://git.dev.tencent.com/zhangguo5/vuedemo001.git"  },  "dependencies": {    "md5": "^2.2.1"  },  "devDependencies": {    "lodash": "^4.17.15"  }}

index.js

var md5=require("md5");var _=require("lodash");function Cat(){    console.log(md5("Hello Cat!"));}Cat();var users = [    { 'user': 'fred',   'age': 48 },    { 'user': 'barney', 'age': 36 },    { 'user': 'fred',   'age': 40 },    { 'user': 'barney', 'age': 34 }  ]; var result=_.sortBy(users,['user','age']); console.log(result);

运行结果:

4.10.4、peerDependencies

有时,你的项目和所依赖的模块,都会同时依赖另一个模块,但是所依赖的版本不一样。比如,你的项目依赖A模块和B模块的1.0版,而A模块本身又依赖B模块的2.0版。

大多数情况下,这不构成问题,B模块的两个版本可以并存,同时运行。但是,有一种情况,会出现问题,就是这种依赖关系将暴露给用户。

最典型的场景就是插件,比如A模块是B模块的插件。用户安装的B模块是1.0版本,但是A插件只能和2.0版本的B模块一起使用。这时,用户要是将1.0版本的B的实例传给A,就会出现问题。因此,需要一种机制,在模板安装的时候提醒用户,如果A和B一起安装,那么B必须是2.0模块。

peerDependencies字段,就是用来供插件指定其所需要的主工具的版本。

{  "name": "chai-as-promised",  "peerDependencies": {    "chai": "1.x"  }}

上面代码指定,安装chai-as-promised模块时,主程序chai必须一起安装,而且chai的版本必须是1.x。如果你的项目指定的依赖是chai的2.0版本,就会报错。

注意,从npm 3.0版开始,peerDependencies不再会默认安装了。

4.10.5、bin字段

bin项用来指定各个内部命令对应的可执行文件的位置。

"bin": {  "someTool": "./bin/someTool.js"}

上面代码指定,someTool 命令对应的可执行文件为 bin 子目录下的 someTool.js。Npm会寻找这个文件,在node_modules/.bin/目录下建立符号链接。在上面的例子中,someTool.js会建立符号链接npm_modules/.bin/someTool。由于node_modules/.bin/目录会在运行时加入系统的PATH变量,因此在运行npm时,就可以不带路径,直接通过命令来调用这些脚本。

因此,像下面这样的写法可以采用简写。

scripts: {    start: './node_modules/someTool/someTool.js build'}// 简写为scripts: {    start: 'someTool build'}

所有node_modules/.bin/目录下的命令,都可以用npm run [命令]的格式运行。在命令行下,键入npm run,然后按tab键,就会显示所有可以使用的命令。

4.10.6、main字段

main字段指定了加载的入口文件,require('moduleName')就会加载这个文件。这个字段的默认值是模块根目录下面的index.js

4.10.7、config 字段

config字段用于添加命令行的环境变量。

下面是一个package.json文件。

{  "name" : "foo",  "config" : { "port" : "8080" },  "scripts" : { "start" : "node server.js" }}

然后,在server.js脚本就可以引用config字段的值。

http  .createServer(...)  .listen(process.env.npm_package_config_port)

用户执行npm run start命令时,这个脚本就可以得到值。

$ npm run start

用户可以改变这个值。

$ npm config set foo:port 80

4.10.8、browser字段

browser指定该模板供浏览器使用的版本。Browserify这样的浏览器打包工具,通过它就知道该打包那个文件。

"browser": {  "tipso": "./node_modules/tipso/src/tipso.js"},

4.10.9、engines 字段

engines字段指明了该模块运行的平台,比如 Node 的某个版本或者浏览器。

{ "engines" : { "node" : ">=0.10.3 <0.12" } }

该字段也可以指定适用的npm版本。

{ "engines" : { "npm" : "~1.0.20" } }

4.10.10、man字段

man用来指定当前模块的man文档的位置。

"man" :[ "./doc/calc.1" ]

4.10.11、preferGlobal字段

preferGlobal的值是布尔值,表示当用户不将该模块安装为全局模块时(即不用–global参数),要不要显示警告,表示该模块的本意就是安装为全局模块。

4.10. 12、style字段

style指定供浏览器使用时,样式文件所在的位置。样式文件打包工具parcelify,通过它知道样式文件的打包位置。

"style": [  "./node_modules/tipso/src/tipso.css"]

五、Express

Express 是一个简洁而灵活的 node.js Web应用框架, 提供了一系列强大特性帮助你创建各种 Web 应用,和丰富的 HTTP 工具。

使用 Express 可以快速地搭建一个完整功能的网站。使用Node.js作为AngularJS开发Web服务器的最佳方式是使用Express模块。

Express官网:

Express4.x API:

5.2、Express框架核心特性

可以设置中间件来响应 HTTP 请求。

定义了路由表用于执行不同的 HTTP 请求动作。

可以通过向模板传递参数来动态渲染 HTML 页面。

丰富的 HTTP 快捷方法和任意排列组合的 Connect 中间件,让你创建健壮、友好的 API 变得既快速又简单。

Express 不对 Node.js 已有的特性进行二次抽象,我们只是在它之上扩展了 Web 应用所需的基本功能。

5.3、安装 Express

安装 Express 并将其保存到依赖列表中:

npm install express --save

以上命令全局安装express。也可安装时指定安装中间件。

body-parser - node.js 中间件,用于处理 JSON, Raw, Text 和 URL 编码的数据。

cookie-parser - 这就是一个解析Cookie的工具。通过req.cookies可以取到传过来的cookie,并把它们转成对象。

multer - node.js 中间件,用于处理 enctype="multipart/form-data"(设置表单的MIME编码)的表单数据。

$ npm install body-parser --save

$ npm install cookie-parser --save
$ npm install multer --save

默认这些模块都已经添加。

5.4、第一个Express框架实例

接下来我们使用 Express 框架来输出 "Hello World"。

以下实例中我们引入了 express 模块,并在客户端发起请求后,响应 "Hello World" 字符串。

创建一个目录,如Project,进入命令行:

使用npm install express 导入express模块。

在目录下创建hello.js文件,如下所示:

//引入express模块var express = require('express');//创建一个app对象,类似一个web 应用(网站)var app = express();//接受指定路径的请求,指定回调函数app.get('/', function (req, res){res.send('Hello World');});//创建一个web服务器,可以认为就是web服务器对象//监听8081端口,当监听成功时回调var server = app.listen(8081, function () {   var host = server.address().address;  //地址   var port = server.address().port;  //端口    console.log("应用实例,访问地址为 http://%s:%s", host, port);});})

使用node执行js:

运行结果:

5.5、使用Nodeclipse开发Express项目

如果直接使用记事本效率会不高,nodeclipse插件可以方便的创建一个Express项目,步骤如下:

创建好的项目如下:

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 index = require('./routes/index');var users = require('./routes/users');var app = express();//指定视图引擎为ejsapp.set('views', path.join(__dirname, 'views'));app.set('view engine', 'ejs');// uncomment after placing your favicon in /public//app.use(favicon(path.join(__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('/', index);app.use('/users', users);// catch 404 and forward to error handlerapp.use(function(req, res, next) {  var err = new Error('Not Found');  err.status = 404;  next(err);});// error handlerapp.use(function(err, req, res, next) {  // set locals, only providing error in development  res.locals.message = err.message;  res.locals.error = req.app.get('env') === 'development' ? err : {};  // render the error page  res.status(err.status || 500);  res.render('error');});module.exports = app;

bin\www是web服务器:

#!/usr/bin/env node/** * 依赖模块,导入 */var app = require('../app');var debug = require('debug')('nodejsexpress:server');var http = require('http');/** * 从上下文环境中获得监听端口,如果空则3000 */var port = normalizePort(process.env.PORT || '3000');app.set('port', port);/** * 创建Web服务器 */var server = http.createServer(app);/** * 开始监听 */server.listen(port);server.on('error', onError);  //指定发生错误时的事件server.on('listening', onListening);  //当监听成功时的回调/** * 规范化端口 */function normalizePort(val) {  var port = parseInt(val, 10);  if (isNaN(port)) {    // named pipe    return val;  }  if (port >= 0) {    // port number    return port;  }  return false;}/** *错误事件监听 */function onError(error) {  if (error.syscall !== 'listen') {    throw error;  }  var bind = typeof port === 'string'    ? 'Pipe ' + port    : 'Port ' + port;  //错误处理  switch (error.code) {    case 'EACCES':      console.error(bind + ' requires elevated privileges');      process.exit(1);  //结束程序      break;    case 'EADDRINUSE':      console.error(bind + ' is already in use');      process.exit(1);      break;    default:      throw error;  }}/** * 当用户访问服务器成功时的回调 */function onListening() {  var addr = server.address();  var bind = typeof addr === 'string'    ? 'pipe ' + addr    : 'port ' + addr.port;  debug('Listening on ' + bind);}

routers/index.js路由,有点类似控制器或Servlet:

var express = require('express');var router = express.Router();/* 获得首页 */router.get('/', function(req, res, next) {  res.render('index', { title: 'Express' });});module.exports = router;

views/index.ejs首页视图:

      <%= title %>    

<%= title %>

Welcome to <%= title %>

在www上右键选择“运行方式”->“Node Application”运行结果:

5.5.1、request对象

Request 对象 - request 对象表示 HTTP 请求,包含了请求查询字符串,参数,内容,HTTP 头部等属性。常见属性有:

req.app:当callback为外部文件时,用req.app访问express的实例req.baseUrl:获取路由当前安装的URL路径req.body / req.cookies:获得「请求主体」/ Cookiesreq.fresh / req.stale:判断请求是否还「新鲜」req.hostname / req.ip:获取主机名和IP地址req.originalUrl:获取原始请求URLreq.params:获取路由的parametersreq.path:获取请求路径req.protocol:获取协议类型req.query:获取URL的查询参数串req.route:获取当前匹配的路由req.subdomains:获取子域名req.accepts():检查可接受的请求的文档类型req.acceptsCharsets / req.acceptsEncodings / req.acceptsLanguages:返回指定字符集的第一个可接受字符编码req.get():获取指定的HTTP请求头req.is():判断请求头Content-Type的MIME类型

5.5.2、response对象

Response 对象 - response 对象表示 HTTP 响应,即在接收到请求时向客户端发送的 HTTP 响应数据。常见属性有:

res.app:同req.app一样res.append():追加指定HTTP头res.set()在res.append()后将重置之前设置的头res.cookie(name,value [,option]):设置Cookieopition: domain / expires / httpOnly / maxAge / path / secure / signedres.clearCookie():清除Cookieres.download():传送指定路径的文件res.get():返回指定的HTTP头res.json():传送JSON响应res.jsonp():传送JSONP响应res.location():只设置响应的Location HTTP头,不设置状态码或者close responseres.redirect():设置响应的Location HTTP头,并且设置状态码302res.render(view,[locals],callback):渲染一个view,同时向callback传递渲染后的字符串,如果在渲染过程中有错误发生next(err)将会被自动调用。callback将会被传入一个可能发生的错误以及渲染后的页面,这样就不会自动输出了。res.send():传送HTTP响应res.sendFile(path [,options] [,fn]):传送指定路径的文件 -会自动根据文件extension设定Content-Typeres.set():设置HTTP头,传入object可以一次设置多个头res.status():设置HTTP状态码res.type():设置Content-Type的MIME类型

 5.5.3、express获取参数有三种方法

req.query 适合 http://localhost:3000/form?num=8888

req.body 适合http://localhost:3000/form,Post请求中的参数
req.params 适合获取form后的num:http://localhost:3000/form/num

(一)、GET

var num = req.query.num;res.send("你获取的get数据为:" + num);

(二)、POST

解析post数据需要用到body-parser

npm install body-parser --save

app.js

var express = require('express');var app = express();//引入body-parservar bodyParser = require('body-parser');app.use(express.static('public'));//需要use的app.use(bodyParser.json()); // for parsing application/jsonapp.use(bodyParser.urlencoded({extended: true})); // for parsing application/x-www-form-urlencoded//获取数据app.post('/form', function(req, res) {var num = req.body.num;res.send("你获取的post数据为:" + num);});//设置监听端口app.listen(3000);

public/test.html

Document

(三)、获取路径参数

app.js

 

var express = require('express');var app = express();//获取数据app.get('/form/:num', function(req, res) {var num = req.params.num;res.send("你获取到form/后的参数:" + num);});//设置监听端口app.listen(3000);

5.6、ejs基础

ejs是一个Express Web应用的模板引擎,在NodeJS开发中可以选择的模板引擎可能是所有Web应用开发中范围最广的,如jade、ejs、htmljs、swig、hogan.js,但ejs是最容易上手的,与jsp,asp,php的原始模板引擎风格很像。

官网:

添加一个product.js路由:

var express = require('express');var router = express.Router();/* 产品 */router.get('/', function(req, res, next) {      var products=[];  products.push({name:"ZTE U880",price:899.8});  products.push({name:"HuWei 荣耀8",price:1899.8});  products.push({name:"iPhone 7 Plus 128G",price:5899.8});    //将product视图与指定的对象渲染后输出到客户端  res.render('product', { title: '天狗商城', pdts:products});});module.exports = router;

在views目录下添加product.ejs视图,这里是一个简单的MVC:

                        <%= title %>                

<%= title %> - 产品列表

<%pdts.forEach(function(pdt,index){%>
<%});%>
序号 名称 价格
<%=index+1%> <%=pdt.name%> <%=pdt.price%>
    <% for(var i=0; i
  • <%=pdts[i].name%>
  • <% } %>

修改app,注册定义好的模块product:

var index = require('./routes/index');var users = require('./routes/users');var pdts = require('./routes/product');var app = express();//指定视图引擎为ejsapp.set('views', path.join(__dirname, 'views'));app.set('view engine', 'ejs');// uncomment after placing your favicon in /public//app.use(favicon(path.join(__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('/', index);app.use('/users', users);app.use('/pdt', pdts);

运行结果:

5.7、lodash

这是一个具有一致接口、模块化、高性能等特性的 JavaScript 工具库。可以非常方便的操作json。

官网:

安装:

npm i -g npm

npm i --save lodash

安装时先用cd切换到当前项目下。

如果浏览器使用可以直接引入:

<script src="lodash.js"></script>

添加lodash依赖:

依赖成功后会在package.json中添加引用:

后台Node.js使用,可以引入模块:

//导入lodash模块var _= require('lodash');var products=[];products.push({name:"ZTE U880",price:899.8});products.push({name:"HuWei 荣耀8",price:1899.8});products.push({name:"iPhone 7 Plus 128G",price:5899.8});//1、取出第一个元素var obj1=_.first(products);console.log(obj1.name);  //ZTE U880//2、取出最后一个元素var obj2=_.last(products);console.log(obj2.name);  //iPhone 7 Plus 128G//3、指定查找条件返回符合条件的索引var obj3=_.findIndex(products,function(obj){    return obj.price>=1000&&obj.name.indexOf("7")>0;});console.log(obj3);  //2//4、指定查找条件返回查找到的对象var obj4=_.find(products,function(obj){    return obj.price>=1000&&obj.name.indexOf("7")>0;});console.log(obj4);  //{ name: 'iPhone 7 Plus 128G', price: 5899.8 }//5、排序var obj5=_.orderBy(products,["price","name"],["desc","asc"]);console.log(obj5); //[ { name: 'iPhone 7 Plus 128G', price: 5899.8 },//{ name: 'HuWei 荣耀8', price: 1899.8 },//{ name: 'ZTE U880', price: 899.8 } ]//6、查找价格为1899.8的产品的keyvar obj6=_.findKey(products,{price:1899.8});console.log(obj6);   //1

API的使用非常简单,但需要注意版本,可以现查现用,API地址:

5.8、参数

5.8.1、URL中的参数占位

Checks route params (req.params), ex: /user/:id

127.0.0.1:3000/index,这种情况下,我们为了得到index,我们可以通过使用req.params得到,通过这种方法我们就可以很好的处理Node中的路由处理问题,同时利用这点可以非常方便的实现MVC模式;

//获得产品根据Idrouter.get('/:id/:category',function(request,res,next){    res.send(request.params.id+","+request.params.category);});

运行结果:

5.8.2、URL中的QueryString

Checks query string params (req.query), ex: ?id=12

127.0.0.1:3000/index?id=12,这种情况下,这种方式是获取客户端get方式传递过来的值,通过使用req.query.id就可以获得,类似于PHP的get方法;

router.get('/:id',function(request,res,next){    res.send("name:"+request.query.name);});

运行结果:

5.8.3、HTTP正文中的参数

 在post请求中获得表单中的数据。

Checks urlencoded body params (req.body), ex: id=

127.0.0.1:300/index,然后post了一个id=2的值,这种方式是获取客户端post过来的数据,可以通过req.body.id获取,类似于PHP的post方法;

页面:

                        <%= title %>                

<%= title %> - 产品列表

<%pdts.forEach(function(pdt,index){%>
<%});%>
序号 名称 价格
<%=index+1%> <%=pdt.name%> <%=pdt.price%>
    <% for(var i=0; i
  • <%=pdts[i].name%>
  • <% } %>

<%if(typeof msg!="undefined"){%> <%=msg%> <%}%>

名称:

价格:

View Code

代码:

router.post('/add',function(request,res,next){    var entity={name:request.body.name,price:request.body.price};    products.push(entity);      //将product视图与指定的对象渲染后输出到客户端      res.render('product', { title: '天狗商城', pdts:products,msg:"添加成功"});});

结果:

5.9、JSON

如果需要Node.js向外提供返回JSON的接口,Express也是非常方便的,可以使用原来在浏览器中使用到的JSON对象,这是一个浏览器内置对象在服务可以直接使用:

将对象序列化成字符:

//对象            var rose={"name":"Rose","weight":"65"};            //序列化成字符串            var str=JSON.stringify(rose);            alert(str);

结果:

反序列化,将字符转换成对象:

//将字符串转换成JavaScript对象            var markStr='{"name":"mark","weight":"188"}';            var mark=JSON.parse(markStr);            alert(mark.name+","+mark.weight);

结果:

Express已经封装了一个json方法,直接调用该方法就可以序列化对象:

/* 产品 */router.get('/rest', function(req, res, next) {  res.json(products);});

运行结果:

 

六、RESTful(表述性状态转移)

REST是英文Representational State Transfer的缩写,中文称之为“表述性状态转移”

基于HTTP协议
是另一种服务架构
传递是JSON、POX(Plain Old XML)而不是SOAP格式的数据
充分利用HTTP谓词(Verb)
侧重数据的传输,业务逻辑交给客户端自行处理

REST是一种分布式服务架构的风格约束,像Java、.Net(WCF、WebAPI)都有对该约束的实现,使URL变得更加有意义,更加简洁明了,如:

http://www.zhangguo.com/products/1 get请求 表示获得所有产品的第1个

http://www.zhangguo.com/products/product post请求 表示添加一个产品

http://www.zhangguo.com/products/1/price get请求 表示获得第1个产品的价格

http://www.zhangguo.com/products/1 delete请求 删除编号为1的产品

REST设计需要遵循的原则

网络上的所有事物都被抽象为资源(resource);
每个资源对应一个唯一的资源标识符(resource identifier);
通过通用的连接器接口(generic connector interface)对资源进行操作;
对资源的各种操作不会改变资源标识符;
所有的操作都是无状态的(stateless)

谓词

GET
表示查询操作,相当于Retrieve、Select操作
POST
表示插入操作,相当于Create,Insert操作
PUT
表示修改操作,相当于Update操作
DELETE
表示删除操作,相当于Delete操作

其它还有:

NodeJS+Express可以很容易的实现REST

application/x-www-form-urlencoded

multipart/form-data

application/json

res.setHeader('Content-Type', 'application/json;charset=utf-8');  

示例代码cars.js:

var express = require('express');var router = express.Router();var _= require('lodash');var cars=[];cars.push({id:201701,name:"BMW",price:190,speed:"210km/h",color:"白色"});cars.push({id:201702,name:"BYD",price:25,speed:"160km/h",color:"红色"});cars.push({id:201703,name:"Benz",price:300,speed:"215km/h",color:"蓝色"});cars.push({id:201704,name:"Honda",price:190,speed:"170km/h",color:"黑色"});cars.push({id:201705,name:"QQ",price:130,speed:"210km/h",color:"白色"});/* Get *//*获得所有汽车*//*url /cars/*/router.get('/', function(req, res, next) {    res.json(cars);});/*Get*//*获得汽车通过id*//*url:/cars/:id  */router.get('/:id', function(req, res, next) {     //从路径中映射参数,转换成数字      var id=parseInt(req.params.id);      var car=_.find(cars,{id:id});      res.json(car);});/*Post*//*添加汽车*//*url:/cars/car  */router.post('/car', function(req, res, next) {      var car=req.body;  //从请求正文中获得json对象      car.id=_.last(cars).id+1;  //将编号修改为最后一辆车的编号+1      cars.push(car);  //将汽车对象添加到集合中      res.json(car);  //将添加成功的车以json的形式返回});/*Put*//*修改汽车*//*url:/cars/car  */router.put('/car', function(req, res, next) {      var car=req.body;  //从请求正文中获得json对象      console.log(req.body);      var index=_.findIndex(cars,{id:parseInt(car.id)});  //根据id获得车在集合中的下标            cars[index]=car;  //替换原对象      //res.json(car);  //将修改后的车以json的形式返回      res.send({status:"success", message:"更新成功!"});  });/*Delete*//*删除汽车*//*url:/cars/:id  */router.delete('/id/:id', function(req, res, next) {      //获得url中的编号参数      var id=parseInt(req.params.id);      var index=_.findIndex(cars,{id:id});  //根据id获得车在集合中的下标      cars.splice(index,1);   //在cars数组中删除下标从index开始的1条数据      res.send({status:"success", message:"删除成功!"});  });module.exports = router;

示例代码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 index = require('./routes/index');var users = require('./routes/users');var pdts = require('./routes/product');var task = require('./routes/task');var cars = require('./routes/cars');var app = express();//指定视图引擎为ejsapp.set('views', path.join(__dirname, 'views'));app.set('view engine', 'ejs');// uncomment after placing your favicon in /public//app.use(favicon(path.join(__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('/', index);app.use('/users', users);app.use('/pdt', pdts);app.use("/task",task);app.use("/cars",cars);// catch 404 and forward to error handlerapp.use(function(req, res, next) {  var err = new Error('Not Found');  err.status = 404;  next(err);});// error handlerapp.use(function(err, req, res, next) {  // set locals, only providing error in development  res.locals.message = err.message;  res.locals.error = req.app.get('env') === 'development' ? err : {};  // render the error page  res.status(err.status || 500);  res.render('error');});module.exports = app;

/* Get */

/*获得所有汽车*/
/*url /cars/*/

/*Get*/

/*获得汽车通过id*/
/*url:/cars/:id */

/*Post*/

/*添加汽车*/
/*url:/cars/car */

 

参数中的json格式一定要使用标准格式,注意引号,注意Content-Type,默认的Content-Type类型是:application/x-www-form-urlencoded

/*Put*/

/*修改汽车*/
/*url:/cars/car */

/*Delete*/

/*删除汽车*/
/*url:/cars/:id */

node.js跨域

修改app.js文件拦截所有的请求,修改头部

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.all('*', function(req, res, next) {    res.header("Access-Control-Allow-Origin", "*");    res.header("Access-Control-Allow-Headers", "content-type");    res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");    res.header("X-Powered-By", ' 3.2.1');    res.header("Content-Type", "application/json;charset=utf-8");    if(req.method == "OPTIONS") {        res.send("200");    } else {        next();    }});

结果:

七、示例下载

git:

git:

八、作业

8.1、请安装好node.js环境,测试版本,在控制台写一个方法用于计算1-100间的所有能被3整除的数,并调用。

8.2、请将8.1中的方法单独存放到一个math.js文件中,同时在math.html页面与node的控制台中调用

8.3、在开发工具IDE中集成node.js开发环境,创建一个node.js项目,向控制台输出“Hello World!”

8.4、使用记事本在c:\根目录下写一个server.js文件实现一个最简单的web服务器,请求时响应当前系统时间。

8.5、将8.4的功能在IDE中完成,请注意端口号不能被占用,如果提示占用错误可以修改端口号为1025-65535之间

8.6、完成一个图书管理的功能,图书包含(编号,名称,作者,图片,价格),实现:

a)、非AJAX的CRUD,使用Node.js+Express+ejs的动态技术。

b)、AJAX的CRUD,使用Node.js+Express+jQuery+HTML技术实现。当然也可以使用Vue2+axios。

c)、使用RestFul风格的服务完成第个作业,get,post,delete,put请。

九、视频

 

上一篇:JavaScript学习总结(三)——闭包、IIFE、原型、函数与对象
下一篇:JavaScript学习总结(二)——延迟对象、跨域、模板引擎、弹出层、AJAX示例

发表评论

最新留言

留言是一种美德,欢迎回访!
[***.207.175.100]2025年05月03日 20时16分47秒