0%

NodeJS-快速上手

Node.js简介

  • Node.js是一个能够在服务器端运行的javaScript的开放源代码,跨平台javaScript运行环境

  • Node采用Google开发的V8引擎运行js代码,使用事件驱动、非阻塞和异步I/o模型等技术来提高性能,可以优化应用程序的传输和规模

  • Node大部分基本模块都用javaScript编写。在Node出现之前js通常作为客户端程序设计语言使用,以Js写出程序常在用户的浏览器上运行

  • 目前,Node已被IBM,Microsoft、Yahoo!、Wakmart、Groupon、SPA、LinkedIn、Rakuten等企业采用

作用

  • 让 JavaScript 成为能够与PHP、Python 等平起平坐的语言
  • 让前端脱离后端,直接通过 JS 写项目

模块化系统

ECMAScript标准缺陷

  • 没有模块系统
  • 标准库较少
  • 没有标准接口
  • 缺乏管理系统

模块化

  • 如果程序设计的规模化达到了一定程度,则必须对其进行模块化
  • 模块化可以有多种形式,单至少应该提供能够讲代码分割为多个源文件的机制
  • CommonJS的模块功能可以帮我们解决该问题

CommonJS 模块规范

  • CommonJs规范的提出,主要是为了弥补当前javascript没有标准的缺陷

  • CommonJS规范为JS制定了一个美好的愿景,希望JS能够在任何地方运行

  • CommonJS对模块的定义十分简单:

    • 模块引用

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      module-1
      /*
      模块化
      优点:解耦 提高代码复用性
      - 在Node中,一个js就是一个模块
      - 在Node中,每一个js文件中的js代码都是独立运行在一个函数中
      而不是全局作用域,一个模块中的变量在其他模块中是无法使用和访问
      */

      console.log("我是module.js-02");
      let y = 2;

      /**
      * 我们通过export来向外部暴露变量和方法
      * 向外部暴露属性或者方法设置为exports的属性即可
      */
      exports.x = "我是module中的x变量";
      exports.y = y;
      exports.fn = function () {
      console.log("我是module的函数");
      };

      module-2
      /*
      引入其他模块
      在node中,通过require()函数来引入外部模块
      require()可以传递一个文件的路径作为参数,node将会自动根据该路径来引入外部模块
      这里路径,如果使用相对路径,必须以.或..开头

      使用require引入模块后会返回一对象,这个对象代表的是我们引入的模块
      */

      var module = require('./02.module');
      console.log(module);
      module.fn();
    • 模块定义

    • 模块标识

      模块化标识就是模块的名字,也就是传递给require()方法的参数,它必符合驼峰命名法的字符串,或者是以.. 开头的相对路径,或者绝对路径

      模块的定义十分简单,接口也十分简洁。每个模块都具有独立的空间,它们互不干扰,在引用时也显得干净利落。

      模块分为两类
      核心模块

      由node引擎提供的模块

      核心模块的标识就是,模块的名字
      文件模块

      由用户自己创建的模块

      文件模块的标识就是文件的路径(绝对路径,相对路径)
      相对路径使用.或..开头

      tip:每一个模块都会被当作一个函数被node执行

      当node在执行模块中的代码时,它会首先在代码的最顶部,添加如下代码

      function (exports, require, module, __filename, __dirname) {

      }

      实际上模块中的代码都是包装在一个函数中执行,并在在函数执行的时候,同时包括5个实参

      exports

      该对象用于将变量或函数暴露到外部

      require

      函数,引入外部的模块

      module

      module代表当前模块本事

      我们既可以使用exports也可以使用module.exports

      __filename

      当前模块的绝对路径

      __dirname

      当前模块所在文件夹的绝对路径

包 package

  • CommonJS的包规范允许我们将一组相关的模块组合到一起,形成一组完整的工具

  • CommonJS的包规范由包结构和包描述文件两个部分组成

  • 包结构

    用于组织包中的各种文件

  • 包描述文件

    描述包的相关信息,以供外部读取分析

包结构

包实际上就是一个压缩文件,解压以后还原为目录。符合规范的目录,应该包含如下文件:

  • package.json 描述文件类似于包的简历(必须)

    包描述文件用于表达非代码相关信息它是一个json格式的文件-package.json,位于包的根目录下,是包的重要组成部分

    package.json字段

  • bin 可执行二进制文件

  • lib js代码

  • doc 文档

  • test 单元测试

NPM(Node Package Manager)

  • CommonJS包规范是理论,NPM是其中一种实践
  • 对于Node而言,NPM帮助其完成了第三方模块的发布,安装和依赖等。借助NPM。Node与第三方模块之间形成了很好的一个生态系统

npm命令

常用

1
2
3
4
5
6
7
8
- npm -v  查看版本
- npm 帮助说明
- npm search 包名 搜索模块包
- npm install/i 包名 在当前目录安装包(全局安卓一般都是安装一些与计算机打交道的工具)
- npm install/i 包名 -g 全局模式安装包
- npm remove/r 包名 删除包
- npm init 初始化模块包
- npm install 包名 --save 安装并添加到模块依赖中

安装包

1
2
3
4
5
npm instal moduleName  [-g]           安装模块,有 -g 或 --global 是全局安装
npm install packagename --save 或 -S --save、-S参数意思是把模块的版本信息保存到dependencies(生产环境依赖)中,即你的package.json文件的dependencies字段中;
npm install packagename --save-dev 或 -D --save-dev 、 -D参数意思是把模块安装到devDependencies(开发环境依赖)中,即你的package.json文件的devDependencies字段中
npm install packagename --save-dev 或 -D --save-optional 、 -O参数意思是把模块安装到optionalDependencies(可选环境依赖)中,即你的package.json文件的optionalDependencies字段中
npm install packagename --save-exact 或 -E --save-exact 、 -E参数的意思是精确的安装指定版本的模块,dependencies字段里每个模块版本号前面的 ^ 不见

更新包

1
2
3
4
5
6
npm outdated  [-g] :     列出所有已经过时了的模块
npm update [-g] : 更新已经安装的模块(或全局的模块)
npm update packageName : 更新某个模块
npm update packName @version [options] 更新到指定版本, 带上原来安装的参数
手动修改package.json中依赖包版本,执行npm install --force,强制从远程下载所有包更新本地包
再次 执行 npm install packageName , 覆盖安装到最新版本

npm-check 一个包 来检查呢npm 依赖包是否有更新,错误,不再使用等

1
2
npm install npm-check -g
npm-check -u -g

卸载

1
2
npm uninstall packagename [options]       卸载已经安装的模块,options参数与安装时候一样
npm remove 、npm rm、npm r 、 npm un 、 npm unlink 这几个命令功能和npm uninstall基本一样

发布包

1
2
npm publish        把在一个package.json文件中定义的模块发布到注册表
npm unpublish myModule 取消发布您已发布到注册表的一个模块(在某些情况下,还需使用 --force 选项)

查看安装包的信息

1
2
3
4
5
6
7
8
npm list   [-g] 、 npm ll [-g] 、 npm la 、 npm ls   查看所有已经安装的模块详情
npm info packageName (显示包的信息)
npm ls packageName 查看本地安装包的版本号,empty表示没有安装过
npm ls packageName -g 查看全局安装的包的版本号,empty表示没有安装过
npm list packagename 查看某个模块的版本号
npm view packageName 显示模块的详细信息
npm view packageName version 查看某个包的最新版本号
npm view packageName versions 查看某个包的所有版本号

npm源管理

  • 明确:通过npm命令下载会到国外服务器获取
  • 缺点:下载速度慢
  • 解决:切换国内服务器
  • 简介:nrm是资源管理工具,可以切换国内服务器下载
1
2
3
4
安装:npm install nrm -g
查看:nrm ls (注:单词list缩写,查看可用服务器)
切换:nrm use 服务器名
测速:nrm test

“模块”(Modules)和”包”(Packages)的区别

  1. A module is any file or directory that can be loaded by Node.js’ require().
  • 模块可以是任何一个文件或目录(目录下可以有很多个文件),只要能被node.js通过require()即可。
  1. A package is a file or directory that is described by a package.json. This can happen in a bunch of different ways!
  • 包是一个文件或目录(目录下可以有多个文件)必须有一个package.json文件来描述,就可以是一个包。

node.js 错误调试:

  1. 当开启服务后,在浏览器中输入地址,如果出现浏览问题,首先要先看 服务器控制台是否报错。如果报错,直接根据服务器报错进行排错。

  2. 打开浏览器开发者工具中的 “网络” 部分,查看请求是否成功发出去了

  • 看一下请求报文是不是和我们想的一样
  • 响应状态码

npm 包自定义发布

1.创建工程,初始化后,修改 package.json 文件

2.打开 npmjs.com 注册账号

3.本地登录提交(切记提交自己的包必须切换到国外服务器,下载后再切换回来)

自定义发布

4.测试 执行命令 npm install ${your npm package name}

npm 版本号形式

npm 版本号形式: X.Y.Z

.X 主版本号:当你做了不兼容的 API 修改,
Y. 次版本号:当你做了向下兼容的功能性新增,
Z. 修订号:当你做了向下兼容的问题修正。

版本号前的符号说明

1.没有任何符号

1
1.0.0

完全百分百匹配,当前库/项目必须使用当前版本号,如果和其他依赖使用了相同库不同版本,会在库的文件夹下建立一个 node_modules 文件夹存放它需要依赖的版本文件。
2.> (大于)
必须大于某个版本

1
>1.0.0 ,可以使用 1.0.1、1.1.1 、2.0.0 的版本。

3.>=(大于等于)
必须大于或等于某个版本号

1
>=1.0.0 ,可以使用 1.0.0、1.1.1 、2.0.0 的版本。

4.<(小于)
必须小于某个版本

1
<2.0.0 ,可以使用 1.0.1、1.1.1 、1.1.9 的版本。

5.<=(小于等于)
必须小于或等于某个版本

1
<=2.0.0 ,可以使用 1.0.1、1.1.1 、2.0.0 的版本。

6.~
不改变主版本号和次版本号,修订号可以随意更改

1
~2.0.0 ,可以使用 2.0.0、2.0.2 、2.0.9 的版本。

7.^
不改变主版本号(主版本号非0),此版本号和修订号可以随意更改

1
^2.0.0 ,可以使用 2.0.1、2.2.2 、2.9.9 的版本。

8.x
x位置即后的位置可以随意(不用在主版本号上)。
例如 1.x 可以使用 1.0.1 、1.1.1 、1.9.1的版本。
例如 2.1.x 可以使用 2.1.1、2.1.9的版本

9.*
*表示任意版本 对版本没有限制, 一般不用

1
"base": "*"

10.-

1
"base": "1.0.1-1.5.9"

1.0.1-1.5.9 可以使用 1.0.1到1.5.9之间的任意版本

Buffer 模块

  • 从结构上看Buffer非常像一个数组,它的元素为16进制的两位数
  • 实际上一个元素就标识内存中的一个字节。
  • 世界上Buffer中的内存不是通过javaScript分配的,而是底层的C++申请的。
  • 也就是我们可以直接通过Buffer来创建内存中的空间
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
/*
Buffer(缓冲区)
- Buffer的结构和数组很像,操作方法也和数组相似
- js数组中不能存储二进制文件,而Buffer就是专门用于存储二进制文件的数组
- 使用Buffer不需要引入模块,直接使用即可
- 在Buffer中存储的都是二进制数据,但是显示时都是以16进制的形式显示
Buffer中的每一个元素的大小是从00 -ff 0 -255
00000000 - 11111111
计算机中 一个0或一个1 我们称之为1位(bit)
8bit =1byte(字节)
1024bit = 1kb
1024kb = 1mb
1024mb = 1gb
1024gb = 1tb

- Buffer的大小一旦确定,则不能修改,Buffer实际上是对内存的直接操作
- Buffer.toString()直接将数据转换成字符串
*/

var str = "Hello";

// 将字符串保存到Buffer中
// var buffer1 = Buffer.from(str);
// // 一个中文占用3个字节
// console.log(buffer1.length); // 占用内存大小
// console.log(str.length); // 字符串的长度
// console.log(buffer1);

// 创建一个指定大小的Buffer
// Buffer构造函数是不推荐使用的
// var buffer = Buffer.alloc(10);
// // 通过索引,来操作buf中元素
// buffer[0] = 88;
// buffer[1] = 255;
// buffer[2] = 0xaa;

// 只要数组在控制台或者页面输出一定都是100进制
// console.log(buffer[2].toString(16));
// console.log(buffer.length);
//
// for (var i = 0; i < buffer.length; i++) {
// console.log(buffer[i]);
// }

// Buffer.allocUnsafe(size)创建指定大小的Buffer,但是buffer中可能含有敏感数据
// var buffer = Buffer.allocUnsafe(10);
// console.log(buffer);


var buffer = Buffer.from("我是一段文本数据");

console.log(buffer.toString());

fs 模块

  • 在Node中,与文件系统的交互是非常重要的,服务器的本质就将本地的文件发送给远程的客户端

  • Node通过fs模块来和文件系统进行交互

  • 该模块提供了一些标准文件访问API来打开、读取、写入文件、以及与其交互

  • 要使用fs模块,首先需要对其进行加载

    1
    const fs = require("fs");

同步和异步调用

  • fs模块中所有的操作都有两种形式可供选择同步异步
  • 同步文件系统会阻塞程序的执行,也就是除非操作完毕,否则不会向下执行代码
  • 异步文件系统不会阻塞程序的执行,而是在操作完成时,通过回调函数将结果返回

同步写入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/*
文件系统(File System)
- 文件系统简单来说就是通过Node来操作系统中的文件
- 使用文件系统,需要引入fs模块,fs是核心模块,直接引入不需要下载
同步文件写入
文件写入
- 手动操作步骤
1.打开文件
fs.openSync(path,flags[,mode])
- path 要打开的文件路径
- flags 打开文件要做的操作类型
r 只读的
w 可写的
- mode 设置文件操作权限,一般不传
- 该方法会返回一个文件的描述作为结果,我们可以通过描述符来对文件进行各种操作
2.向文件写入数据
fs.writeSync(fd, string[, position[, encoding]])
- fd 文件描述,需要传递要写入的文件描述符
- string 要写入的内容
- position 写入起始位置(一般不传)
- encoding 写入编码(一般不传)

3.保存并关闭文件

*/
var fs = require("fs");
var fd = fs.openSync("hello.txt", "w");
// 向文件中写入内容
fs.writeSync(fd,"今天的天气真不错~~~~");
// console.log(fd);

// 关闭文件
fs.closeSync(fd);

异步写入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/*
异步文件写入
fs.open(path[, flags[, mode]], callback)
- 用于打开一个文件
- 异步调用的方法,结果都是通过回调函数返回
- 回调函数两个参数:
err 错误对象,如果没有错误则为null
fd 文件描述符
*/

// 引入fs模块
var fs = require('fs');
// 打开文件
fs.open("hello2.txt", "w", function (err, fd) {
// 判断是否出错
if (!err) {
console.log(fd);
// 如果没有错误直接执行写入操作
fs.write(fd, "我是异步写入的内容~~~", function (err) {
if (!err) {
console.log("写入成功")
}
// 关闭文件
fs.close(fd, function (err) {
if (!err) {
console.log("fs操作关闭");
}
})
})
} else {
console.log(err);
}
});

简单文件写入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/*
fs.writeFile(file, data[, options], callback) 异步
fs.writeFileSync(file, data[, options]) 同步
- file 要操作的文件路径
- data 要写入的数据
- options 选项可以对写入进行一些设置
- callback 写入完成以后执行的函数
*/

// 引入fs模块
var fs = require('fs');

fs.writeFile("hello3.txt", "这是writeFile写入的内容",{flag:"w"}, function (err) {
if (!err) {
console.log("写入成功");
}else {
console.log(err);
}
});

流式文件写入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/*
同步、异步、简单文件写入都不合适大文件的写入,性能较差,容易导致内存溢出
*/
var fs = require('fs');

// 流式文件写入
// 创建一个可写流
/**
* fs.createWriteStream(path[, options])
* - 可以用来创建一个可写流
* - path,文件路径
* - options 配置的参数
*/

var ws = fs.createWriteStream('hello3.txt');

// 可以通过监听流的open和close事件来监听的打开和关闭
/**
* on(事件字符串,回调函数) 可以为对象绑定一个事件
* once(事件字符串,回调函数 可以为我们的对象绑定一个一次性的事件,该事件将会触发一次之后自动失效
*
*/
ws.once("open", function () {
console.log("流打开了");
});
ws.once("close", function () {
console.log("流关闭了");
});
//通过ws向文件中输出内容
ws.write('窗前明月光\n');
ws.write('疑是地上霜\n');
ws.write('举头望明月\n');
ws.write('低头思故乡\n');

// 关闭流
ws.end();

简单文件读取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/*
1.同步文件读取
2.异步文件读取
3.简单文件读取
fs.readFile(path[, options], callback)
fs.readFileSync(path[, options])
- path文件路径
- options 读取选项
- callback回调函数,通过回调函数将读取的内容返回(err,data)
err 错误对象
data 读取到的数据,返回buffer对象
4.流式文件读取
*/

var fs = require('fs');
fs.readFile('hello3.txt', function (err, data) {
if (!err) {
console.log(data.toString());
// 将我们读到内容写入到其他文件中
fs.writeFile('hello4.txt',data,function (err) {
if (!err) {
console.log('输出成功');
}
})
}
});

流式文件读取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/*
流式文件读取适用于一些比较大的文件,可以分多次文件读取内存中
*/

var fs = require('fs');

//创建一个可读流
var rs = fs.createReadStream('code.png');
//创建一个可写流
var ws = fs.createWriteStream("a.png");
//监听流的开启和关闭
rs.once("open", () => {
console.log("可读流被打开了");
});

rs.once("close", () => {
console.log("可读流被关闭");
});

ws.once("open", () => {
console.log("可写流被打开了");
});

ws.once("close", () => {
console.log("可写流被关闭");
ws.end();
});
//如果要读取一个可读流中的数据,必须要为可读流绑定一个data事件,data事件绑定完毕,它会自动开始读取数据
rs.on("data", (data) => {
// console.log(data.length);
// 将读取到的数据写入可写流中
ws.write(data);
});

简化方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/*
流式文件读取适用于一些比较大的文件,可以分多次文件读取内存中
*/

var fs = require('fs');

//创建一个可读流
var rs = fs.createReadStream('code.png');
//创建一个可写流
var ws = fs.createWriteStream("b.png");
//监听流的开启和关闭

//pipe()可以将我们可读流中的内容,直接输出到可写流中
rs.pipe(ws);

fs其他操作

  • 验证路径是否存在

    fs.existsSync(path)

  • 获取文件信息

    fs.stat(path,callback)

    fs.statSync(path)

  • 删除文件

    fs.unlink(path,callback)

    fs.unlinkSync(path)

  • 读取一个目录结构

    fs.readdir(path[,options],callback)

    fs.readdirSync(path,[,options])

  • 截断文件

    fs.truncate(path,len,callback)

    fs.truncateSync(path,len)

  • 建立目录

    fs.mkdir(path[,mode],callback)

    fs.mkdirSync(path[,mode])

  • 删除目录

    fs.rmdir(path,callback)
    fs.rmdirSync(path)

  • 重命名文件

    fs.rename(oldPath,newPath,callback)
    fs.renameSync(oldPath,newPath)

  • 监视文件的修改

    fs.watchFile(filename[,filename[,options],listener)

http 模块

超文本传输协议(Hyper Text Transfer Protocol,HTTP)是一个简单的请求-响应协议,它通常运行在TCP之上。它指定了客户端可能发送给服务器什么样的消息以及得到什么样的响应。请求和响应消息的头以ASCII形式给出;而消息内容则具有一个类似MIME的格式。这个简单模型是早期Web成功的有功之臣,因为它使开发和部署非常地直截了当.

Node.js 中的 HTTP 接口旨在支持该协议的许多传统上难以使用的功能。 特别是大的,可能是块编码的消息。 接口从不缓冲整个请求或响应,因此用户能够流式传输数据。

HTTP 消息头由类似如下的对象表示:

1
2
3
4
5
{ 'content-length': '123', // 响应内容
'content-type': 'text/plain', // 内容类型
'connection': 'keep-alive', // 连接
'host': 'mysite.com', // 主机名
'accept': '*/*' } // 同意接收文件的类型 * 代表所有类型

请求和响应

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// http 超文本传输协议
const http = require('http');
const fs = require('fs');
// 创建web服务器
const serve = http.createServer();
// 监听请求
const file = 'hm-node/index.html';
let msg = '';
serve.on('request', (req, res) => {

console.log(req.headers); // 获取请求头信息(对象)
console.log(req.rawHeaders); // 获取请求头信息(数组)
console.log(req.httpVersion); // 获取HTTP版本
console.log(req.url); // 获取请求路径(注:不包含网址)

fs.readFile(file, 'utf-8', (err, data) => {
if (err) throw err;
msg = data;
});

// 留心:有请求就必须有响应,没有响应网页无法打开
// res.statusCode = 404; 状态码
// res.statusMessage = 'Not Found' 响应信息
// res.setHeader('Content-type':'text/html';charset=utf-8);
res.writeHead(200, 'IS Found', {
'Content-Type': 'text/html'
});
res.write(msg);

res.end();
})
// 端口在1-60000
serve.listen(8080, function () {
console.log('服务启动成功:,请访问:http://localhost:8080');
});
// 关闭服务器
// serve.close();

HTTP content-type

Content-Type(内容类型),一般是指网页中存在的 Content-Type,用于定义网络文件的类型和网页的编码,决定浏览器将以什么形式、什么编码读取这个文件,这就是经常看到一些 PHP 网页点击的结果却是下载一个文件或一张图片的原因。

Content-Type 标头告诉客户端实际返回的内容的内容类型。

语法格式:

1
2
Content-Type: text/html; charset=utf-8
Content-Type: multipart/form-data; boundary=something

HTTP content-type

常见的媒体格式类型如下:

  • text/html : HTML格式
  • text/plain :纯文本格式
  • text/xml : XML格式
  • image/gif :gif图片格式
  • image/jpeg :jpg图片格式
  • image/png:png图片格式

以application开头的媒体格式类型:

  • application/xhtml+xml :XHTML格式
  • application/xml: XML数据格式
  • application/atom+xml :Atom XML聚合格式
  • application/json: JSON数据格式
  • application/pdf:pdf格式
  • application/msword : Word文档格式
  • application/octet-stream : 二进制流数据(如常见的文件下载)
  • application/x-www-form-urlencoded :
    中默认的encType,form表单数据被编码为key/value格式发送到服务器(表单默认的提交数据的格式)

request 对象 和 response对象

request 对象

  • request 对象类型 <http.IncomingMessage>, 继承自stream.Readable
  • request 对象常用成员
    • request.headers
    • request.rawHeaders
    • request.httpVersion
    • request.method
    • request.url

response 对象

  • response 对象类型 <http.ServerResponse>

  • response 对象常用成员

    • response.writeHead(statusCode[, statusMessage][, headers])

      1. This method must only be called once on a message and it must be called before response.end() is called.
      • 这个方法在每次请求响应前都必须被调用(只能调用一次)。并且必须在end()方法调用前调用
2. If you call response.write() or response.end() before calling this, the implicit/mutable headers will be calculated and call this function for you.
- 如果在调用writeHead()方法之前调用了write() 或 end()方法,系统会自动帮你调用writeHead()方法,并且会生成默认的响应头

3. When headers have been set with response.setHeader(), they will be merged with any headers passed to response.writeHead(), with the headers passed to response.writeHead() given precedence.
- 如果通过 res.setHeader() 也设置了响应头,那么系统会将serHeader()设置的响应头和writeHead()设置的响应头合并。 并且writeHead()的设置优先
1
2
3
4
5
// 示例代码:
res.writeHead(200, 'OK', {
'Content-Type': 'text/html; charset=utf-8',
'Content-Length': Buffer.byteLength(msg)
});
  • response.write(chunk[, encoding][, callback])

    • 参数1:要写入的数据,可以是字符串或二进制数据,必填
    • 参数2:编码,默认是utf8,选填。
    • 参数3:回调函数,选填。
  • response.end([data][, encoding][, callback])

    • 结束响应。
    • This method signals to the server that all of the response headers and body have been sent; that server should consider this message complete. The method, response.end(), MUST be called on each response.
    • res.end()这个方法告诉服务器所有要发送的响应头和响应体都发送完毕了。可以人为这次响应结束了。
    • 同时每次响应都必须调用该方法,用来结束响应
* 参数1:结束响应前要发送的数据,选填。
* 参数2:编码,选填。
* 参数3:回调函数,选填。
  • response.setHeader(name, value)

    • 设置响应报文头
  • response.statusCode

    • 设置或读取http响应码
  • response.statusMessage

    • 设置或读取http响应状态消息

案例:留言模块

main.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
// 引入模块
const http = require('http');
const fs = require('fs');
const path = require('path');
const url = require('url');
// 引入字符查询模块 用于序列与反序列表单数据
const queryString = require('querystring');
const util = require('./public/js/util');
// 创建web服务器
let serve = http.createServer();
const BASE_URL = 'hm-node/msg/';
//创建留言数据对象
let msgs = [{
name: '张三',
content: "你好我是张三",
create_at: '2017-11-14 10:30:32'
},
{
name: '李四',
content: "你好我是李四",
create_at: '2017-11-15 10:11:14'
},
{
name: '王五',
content: "你好我是王五",
create_at: '2017-11-16 10:22:55'
}
];

console.log(__dirname);
console.log(__filename);
// 监听端口
serve.on('request', function (req, res) {
// 获取当前请求路径
let currentUrl = req.url;
// 判断当前路劲

if (currentUrl === '/') {
fs.readFile(path.join(__dirname, './view/index.html'), 'utf-8', function (err, data) {
if (err) throw err;
res.writeHead(200, 'IS Found', {
'Content-Type': 'text/html'
});
let html = '';
let innerData = '';
msgs.forEach(item => {
html += `<li class="list-group-item">${item.name}:说:${item.content}
<span class="pull-right">${item.create_at}</span>
</li>`
// 将评论列表的数据替换
innerData = data.replace('暂无数据', html);
})
res.write(innerData);
res.end();

})
} else if (currentUrl === '/add') {
fs.readFile(BASE_URL + 'view/add.html', 'utf-8', function (err, data) {
if (err) throw err;
res.writeHead(200, 'IS Found', {
'Content-Type': 'text/html'
});
res.write(data);
res.end();



})

} else if (currentUrl.indexOf('/public') === 0) {
// 静态资源不需要 设置Content-Type类型 浏览器自动解析
console.log(currentUrl);
fs.readFile(path.join(__dirname, currentUrl), 'utf-8', function (err, data) {
if (err) throw err;
res.write(data);
res.end();

})
} else if (currentUrl.indexOf('/doadd') === 0) {
// 判断表单提交
// 1.接收数据
// 2.存储数据
// 3.跳转

let reqData = '';
if (req.method == 'POST') {
let postDate = '';
// 注册data事件接收数据(每当收到一段表单提交的数据,该方法会执行一次)
req.on('data', function (chunk) {
// chunk 默认是一个二进制数据,和 data 拼接会自动 toString
postDate += chunk;
});
// 当接收表单提交的数据完毕之后,就可以进一步处理了
//注册end事件,所有数据接收完成会执行一次该方法
req.on('end', function () {

let obj = queryString.parse(postDate);
msgs.unshift(obj);
// 301:永久重定向。当返回的状态码是301时,浏览器需要重新发送一个HTTP请求,到服务器返回的新地址。
// 320:
res.statusCode = 302;
res.setHeader('Location', '/');
res.end();
});


} else {
let obj = url.parse(req.url, true).query;
obj.create_at = util.formartTime();
msgs.unshift(obj);
console.log(obj);
// 301:永久重定向。当返回的状态码是301时,浏览器需要重新发送一个HTTP请求,到服务器返回的新地址。
// 320:
res.statusCode = 302;
res.setHeader('Location', '/');
res.end();
}

} else {
fs.readFile(BASE_URL + 'view/404.html', 'utf-8', function (err, data) {
if (err) throw err;
res.writeHead(200, 'IS Found', {
'Content-Type': 'text/html'
});
res.write(data);
res.end();


})
}


});

// 监听端口
serve.listen(8080, function () {
console.log('启动成功,请访问: http:localhost:8080');
});

node http demo: node.js http 留言发布小模块 (gitee.com)

url模块

get请求时,用户请求的参数是在request的url属性中,纯字符串,使用起来并不方便

url模块可以更方便地解析用户请求的get参数

具体使用

  1. 加载模块 var url = require('url');
  2. 调用parse()方法解析
1
2
3
4
5
url.parse(urlString[, parseQueryString[, slashesDenoteHost]]);
var urlObj = url.parse(reqUrl, true);

// url对象的pathname属性,获取不包含查询字符串的url
// url对象的query属性中包含的就是请求字符串的键值对对象
谢谢老板