一、1.初识Node.js

1.1、回顾与思考

1.1.1、思考:为什么JavaScript可以在浏览器中被执行

1.1.2、思考:为什么JavaScript可以操作DOM和BOM

1.1.3、浏览器中的JavaScript运行环境

运行环境是指代码正常运行所需的必要环境。

1.1.4、思考: JavaScript 能否做后端开发

1.2、Node.js简介

1、什么是Node.js

**Node.js⑧is a JavaScript runtime built on Chrome’s V8 JavaScript engine.
Node.js是一个基于Chrome V8,弓擎的JavaScript运行环境。
Node.js的官网地址: https://nodejs.org/en/

1.2.1、Node.is中的JavaScript运行环境

1.2.2、Node.js可以做什么

Node.js作为-个JavaScript的运行环境,仅仅提供了基础的功能和API。然而,基于Node.js提供的这些基础能,很多强大的工具和框架如雨后春笋,层出不穷,所以学会了Node.js,可以让前端程序员胜任更多的工作和岗位:

  • ① 基于Express框架http://www.expressjs.com.cn/),可以快速构建Web应用
  • ② 基纡Electron框架(https://electronjs.org/) ,可以构建跨平台的桌面应用
  • ③ 基于restify框架(http://restify.com/) , 可以快速构建API接口项目
  • ④ 读写和操作数据库、创建实用的命令行工具辅助前端开发、etC…

总之: Node.js 是大前端时代的”大宝剑”, 有了Node.js这个超级buff的加持,前端程序员的行业竞争力会越来越强!

1.2.3、Node.js怎么学

浏览器中的JavaScript学习路径:
JavaScript基础语法+浏览器内置API (DOM + BOM) +第三方库(jQuery、art-template 等)

Node.js的学习路径:
JavaScript基础语法+ Node.js内置API模块(fs、 path、 http等) +第三方API模块(express、 mysql 等)

1.3、Node.js环境的安装

1.3.1、查看已安装的Node.js的版本号

打开终端,在终端输入命令node-v后,按下回车键,即可查看已安装的Node.js的版本号。

windows系统快速打开终端的方式:
使用快捷键(windows徽标键+R)打开运行面板,输入cmd后直接回车,即可打开终端。

1.3.2、什么是终端

1.3.3、在Node.js环境中执行JavaScript代码

  • ① 打开终端
  • ② 切换到代码目录的路径
  • ③ 输入node要执行的js文件名

    PS:现在cmd命令行是无法直接切换至其它盘符的,只能切换至当前盘符的目录下,如果要切换至其它盘符,需要在cd 和路径间增加‘/d’,才能从c盘正常切换至其它盘符。

1.4、终端中的快捷键

在Windows的powershell或cmd终端中,我们可以通过如下快捷键,来提高终端的操作效率:

  • ① 使用↑键,可以快速定位到上一次执行的命令
  • ② 使用tab键,能够快速补全路径
  • ③ 使用esc键,能够快速清空当前已输入的命令
  • ④ 输入cls命令,可以清空终端

1.5、什么是fs文件系统模块

fs模块是Node.js官方提供的、用来操作文件的模块。它提供了一系列的方法和属性, 用来满足用户对文件的操作需求。
例如:

  • fs.readFile( )方法,用来读取指定文件中的内容
  • fs.writeFile( )方法,用来向指定的文件中写入内容

如果要在JavaScript代码中,使用fs模块来操作文件,则需要使用如下的方式先导入它:

1.5.1、读取指定文件中的内容

1、 fs.readFile()的语法格式
使用fs.readFile( )方法,可以读取指定文件中的内容,语法格式如下:

参数解读:

  • 参数1:必选参数,字符串,表示文件的路径。
  • 参数2:可选参数,表示以什么编码格式来读取文件。
  • 参数3:必选参数,文件读取完成后,通过回调函数拿到读取的结果。

2、 fs.readFile( )的示例代码

以utf8的编码格式,读取指定文件的内容,并打印err和dataStr的值:

//1.导入 fs模块 来操作文件
const fs = require('fs')//2. 调用fs. readFile() 方法读取文件
//.参数1:读取文件的存放路径
//.参数2:读取文件时候采用的编码格式,一般默认指定UTF 8
//....参数3:回调函数,拿到读取失败和成功的结果 err. . dataStrfs.readFile('./files/01.txt', 'utf-8', function (err, dataStr) {//2.1 打印失败的结果//如果读取成功,则err 的值为null//如果读取失败,则err的值为错误对象,dataStr 的值为undefined console.log(err);console.log('---------');//2.2 打印成功的结果console.log(dataStr);
})

1.5.2、判断文件是否读取成功

可以判断err对象是否为null,从而知晓文件读取的结果:

1.5.3、向指定的文件中写入内容

1、 fs.writeFile()的语法格式

使用fs.writeFile( )方法,可以向指定的文件中写入内容,语法格式如下:

参数解读:

  • file:必选参数,需要指定一个文件路径的字符串,表示文件的存放路径.
  • data:必选参数,表示要写入的内容。
  • options:可选参数,表示以什么格式写入文件内容,默认值是utf-8。
  • callback:必选参数,文件写入完成后的回调函数.
 //1.导入 fs 文件系统模块
const fs = require('fs');
// 2.调用fs.writeFile() 方法,写入文件的内容
// 参数1:表示文件的存放路径
// 参数2:表示要写入的内容
// 参数3:回调函数fs.writeFile('./files/01.txt', 'abcd', function (err) {//2.1 如果文件写入成功 则err 的值等于null //2.2 如果文件写入失败 则err 的值等于一个错误的对象console.log(err);//null
})
fs.writeFile('f:/files/02.txt', 'abcd', function (err) {//2.1 如果文件写入成功 则err 的值等于null //2.2 如果文件写入失败 则err 的值等于一个错误的对象console.log(err); //[Error: ENOENT: no such file or directory, open 'f:\files\02.txt'] {})

1.5.4、判断文件是否写入成功

可以判断err对象是否为null,从而知晓文件写入的结果:

fs.writeFile('./files/01.txt', 'abcd', function (err) {//2.1 如果文件写入成功 则err 的值等于null //2.2 如果文件写入失败 则err 的值等于一个错误的对象if (err) {return console.log('文件写入失败!' + err.message); //文件写入成功!}console.log('文件写入成功!');
})

1.5.5、练习-考试成绩整理

使用fs 文件系统模块,将素材目录下成绩.txt文件中的考试数据,整理到成绩-ok.txt文件中。
整理前,成绩.txt文件中的数据格式如下:

整理完成之后,希望得到的成绩-ok.txt文件中的数据格式如下:

//1.导入 fs模块 来操作文件
const fs = require('fs')
//2. 调用fs. readFile() 方法读取文件
//.参数1:读取文件的存放路径
//.参数2:读取文件时候采用的编码格式,一般默认指定UTF 8
//....参数3:回调函数,拿到读取失败和成功的结果 err. . dataStr
fs.readFile('./files/成绩.txt', 'utf-8', function (err, dataStr) {if (err) {return console.log('读取文件失败!' + err.message);}// console.log('读取文件成功!' + dataStr);// 4.1 先把成绩的数据,按照空格进行分割const arrOld = dataStr.split(' ')// 4.2 循环分割后的数组,对每一项数据,进行字符串的替换操作const arrNew = []// 4.3 把新数组中的每一项,进行合并,得到一个新的字符串arrOld.forEach(item => {arrNew.push(item.replace('=', ':'))})const arrStr = arrNew.join('\r\n');console.log(arrStr);// 5 调用wrideFile  为文件写入数据fs.writeFile('./files/成绩.txt',arrStr,function(err){if(err){return console.log('写入文件失败' + err.message);}console.log('写入文件成功');})
})

1.6、 fs模块-路径动态拼接的问题

在使用fs模块操作文件时,如果提供的操作路径是以./或…/开头的相对路径时,很容易出现路径动态拼接错误的问题。
原因:代码在运行的时候,会以执行node命令时所处的目录,动态拼接出被操作文件的完整路径。
解决方案:在使用fs模块操作文件时,直接提供完整的路径,不要提供./或/开头的相对路径,从而防止路径动态拼接的问题。

使用__dirname完美解决路径动态拼接的问题

//1.导入 fs模块 来操作文件
const fs = require('fs')
fs.readFile(__dirname+'/files/01.txt', 'utf-8', function (err, dataStr) {//2.1 打印失败的结果//如果读取成功,则err 的值为null//如果读取失败,则err的值为错误对象,dataStr 的值为undefined console.log(err);console.log('---------');//2.2 打印成功的结果console.log(dataStr);
})

二、path路径模块

2.1、什么是path路径模块

path模块是Node.js官方提供的、用来处理路径的模块。它提供了一系列的方法和属性,用来满足用户对路径的处理需求。
例如:

  • pathjoin()方法,用来将多个路径片段拼接成一 个完整的路径字符串
  • path.basename( )方法,用来从路径字符串中,将文件名解析出来
    如果要在JavaScript代码中,使用path模块来处理路径,则需要使用如下的方式先导入它:

2.2、pathjoin( )的语法格式

1、path Join()的语法格式
使用path.join( )方法,可以把多个路径片段拼接为完整的路径字符串,语法格式如下:

2、path Join()的代码示例
使用path.join( )方法,可以把多个路径片段拼接为完整的路径字符串:

const path = require('path')
// 注意 ../会抵消前面的路径
const pathStr = path.join('/a', '/b/c', '../', './d', 'e');
console.log(pathStr); //\a\b\d\e
const pathStr1 = path.join(__dirname, './files/01.txt')
console.log(pathStr1);// 输出当前所在目录\files\1.txt

2.3、获取路径中的文件名

1、path.basename()的语法格式
使用path.basename()方法,可以获取路径中的最后一部分, 经常通过这个方法获取路径中的文件名,语法格式如下:

参数解读:

  • path 必选参数,示个路径的字符串
  • ext 可选参数,表示文件扩展名
  • 返回: 表示路径中的最后一部分
    2、 path.basename()的代码示例

使用path.basename( )方法,可以从一个文件路径中,获取到文件的名称部分:

const path = require('path')
//定义文件的存放路径
const fpath = '/a/b/c/index.html'
const fullName = path.basename(fpath)
console.log(fullName);//index.html
const nameWithoutExt = path.basename(fpath, '.html')
console.log(nameWithoutExt); //index

2.4、获取路径中的文件扩展名

1、path.extname()的代码示例
使用path.extname( )方法,可以获取路径中的扩展名部分:

const path = require('path')
//定义文件的存放路径
const fpath = '/a/b/c/index.html'
const fext = path.extname(fpath)
console.log(fext); //.html

三、什么是http模块

3.1什么是http模块

回顾:什么是客户端、什么是服务器?

在网络节点中,负责消费资源的电脑,叫做客户端;负责对外提供网络资源的电脑,叫做服务器。

http模块是Node.js官方提供的、用来创建web服务器的模块。通过http模块提供的http.createServer()方法,就能方便的把一台普通的电脑, 变成-台Web服务器,从而对外提供Web资源服务。

如果要希望使用http模块创建Web服务器,则需要先导入它:

3.2、进一步理解http模块的作用

服务器和普通电脑的区别在于,服务器上安装了web服务器软件,例如: IIS、 Apache 等。通过安装这些服务器软件就能把一台普通的电脑变成-台web服务器。

在Node.js中,我们 不需要使用IlS、Apache 等这些 第三方web服务器软件。因为我们可以基于Node.js提供的http模块,通过几行简单的代码,就能轻松的手写一个服务器软件,从而对外提供web服务。

3.3、服务器相关的概念

1、IP地址

3.4、服务器相关的概念

域名和域名服务器

尽管IP地址能够唯一地标记网络上的计算机,但IP地址是一长串数字, 不直观,而且 不便于记忆,于是人们又发明了另一套字符型的地址方案,即所谓的域名(Domain Name)地址。

IP地址和域名是一对应的关系, 这份对应关系存放在一种叫做域名服务器(DNS, Domain name server)的电脑中。使用者只需通过好记的域名访问对应的服务器即可,对应的转换工作由域名服务器实现。因此,域名服务器就是提供IP地址和域名之间的转换服务的服务器。

注意:

  • ① 单纯使用IP地址,互联网中的电脑也能够正常工作。但是有了域名的加持,能让互联网的世界变得更加方便。

  • ② 在开发测试期间,127.0.0.1 对应的域名是localhost,它们都代表我们自己的这台电脑,在使用效果上没有任何区别。

3.4.1、端口号

计算机中的端口号,就好像是现实生活中的J牌号样。 通过i ]牌号,外卖小哥可以在整栋大楼众多的房间中,准确把外卖送到你的手中。

同样的道理,在一台电脑中, 可以运行成百上千个web服务。每个web服务都对应一个唯一 的端口号。 客户端发送过来的网络请求,通过端口号,可以被准确地交给对应的web服务进行处理。

3.5、创建最基本的web服务器

1.创建web服务器的基本步骤

  • ① 导入http模块

  • ② 创建web服务器实例

  • ③ 为服务器实例绑定request事件,监听客户端的请求

  • ④ 启动服务器

3.5.1、req请求对象

只要服务器接收到了客户端的请求,就会调用通过server.on( )为服务器绑定的request事件处理函数。如果想在事件处理函数中,访问与客户端相关的数据或属性,可以使用如下的方式:

3.5.2、res响应对象

在服务器的request事件处理函数中,如果想访问与服务器相关的数据或属性,可以使用如下的方式:

3.5.3、5.解决中文乱码问题

当调用res.end( )方法,向客户端发送中文内容的时候,会出现乱码问题,此时,需要手动设置内容的编码格式:

3.6、根据不同的url响应不同的html内容

3.6.1、核心实现步骤

  • ① 获取请求的url地址

  • ② 设置默认的响应内容为404 Not found

  • ③ 判断用户请求的是否为/或/index.html首页

  • ④ 判断用户请求的是否为/about.html关于页面

  • ⑤ 设置Content-Type响应头,防止中文乱码

  • ⑥ 使用res.end( )把内容响应给客户端

3.6.2、动态响应内容

四、模块化的基本概念

4.1、什么是模块化

4.2、编程领域中的模块化

编程领域中的模块化,就是遵守固定的规则,把一个大文件拆成独立并互相依赖的多个小模块。

把代码进行模块化拆分的好处:

  • ① 提高了代码的复用性
  • ② 提高了代码的可维护性
  • ③ 可以实现按需加载

4.3、模块化规范

模块化规范就是对代码进行模块化的拆分与组合时,需要遵守的那些规则。

例如:

  • 使用什么样的语法格式来引用模块
  • 在模块中使用什么样的语法格式向外暴露成员

模块化规范的好处:大家都遵守同样的模块化规范写代码,降低了沟通的成本,极大方便了各个模块之间的相互调用,利人利己。

4.4、Node.js中模块的分类

Node.js中根据模块来源的不同,将模块分为了3大类,分别是:

  • 内置模块(内置模块是由Node.js官方提供的,例如fs、path、 http 等)
  • 自定义模块(用户创建的每个.js 文件,都是自定义模块)
  • 第三方模块(由第三方开发出来的模块,并非官方提供的内置模块,也不是用户创建的自定义模块,使用前需要先下载)

4.4.1、加载模块

使用强大的require( )方法,可以加载需要的内置模块、用户自定义模块、第三方模块进行使用。例如:

注意:使用require()方法加载其它模块时,会执行被加载模块中的代码。

4.5、什么是模块作用域

和函数作用域类似,在自定义模块中定义的变量、方法等成员,只能在当前模块内被访问,这种模块级别的访问限制,叫做模块作用域。

4.5.1、模块作用域的好处

防止了全局变量污染的问题

4.6、向外共享模块作用域中的成员

4.6.1、** module对象**

在每个js自定义模块中都有一个module对象,它里面存储了和当前模块有关的信息,打印如下:

4.6.2、module.exports对象

在自定义模块中,可以使用module.exports对象,将模块内的成员共享出去,供外界使用。
外界用require()方法导入自定义模块时,得到的就是module.exports所指向的对象。

const username = 'zs'module.exports.username = username
exports.age = 20
exports.sayHello = function() {console.log('大家好!')
}

4.6.3、共享成员时的注意点

使用require()方法导入模块时,导入的结果,永远以module.exports指向的对象为准。

4.7、exports对象

由于module.exports单词写起来比较复杂,为了简化向外共享成员的代码,Node 提供了exports对象。默认情况下,exports和module.exports指向同一个对象。最终共享的结果,还是module.exports指向的对象为准。

4.7.1、exports和module.exports的使用误区

时刻谨记,require()模块时,得到的永远是module.exports指向的对象:


注意:为了防止混乱,建议大家不要在同一个模块中同时使用exports和module.exports

4.7.2、nods.js中模块化规范

nods.js遵循了CommonJS模块化规范,CommonJS规定了模块的特性和各模块之间如何相互依赖。

CommonJS规定:

  • ① 每个模块内部,module 变量代表当前模块。
  • ② module 变量是一个对象, 它的exports属性(即module.exports)是对外的接口。
  • ③ 加载某个模块,实是加载该模块的module.exports属性。require( 方法用于加载模块。

五、npm与包

5.1、包

5.1.1、什么是包

Node.js中的第三方模块又叫做包。

就像电脑和计算机指的是相同的东西,第三方模块和包指的是同一个概念,只不过叫法不同。

5.1.2、包的来源

不同于Node.js中的内置模块与自定义模块,包是由第三方个人或团队开发出来的,免费供所有人使用。

注意:Node.js中的包都是免费且开源的,不需要付费即可免费下载使用。

5.1.3、为什么需要包

由于Node.js的内置模块仅提供了一些底层的API,导致在基于内置模块进行项目开发的时,效率很低。

包是基于内置模块封装出来的,提供了更高级、更方便的API,极大的提高了开发效率。

包和内置模块之间的关系,类似于jQuery和浏览器内置API之间的关系。

5.1.4、从哪里下载包

国外有一家IT公司,叫做npm, Inc这家公司旗下有一个非常著名的网站: https://www.npmjs.com/ ,它是全球最大的包共享平台,你可以从这个网站上搜索到任何你需要的包,只要你有足够的耐心!

到目前位置,全球约1100多万的开发人员,通过这个包共享平台,开发并共享了超过120多万个包供我们使用。

npm, Inc.公司提供了一个地址为https://registr.npmjs.org/的服务器,来对外共享所有的包,我们可以从这个服务器上下载自己所需要的包。
注意:

  • 从https://www.npmjs.com/网站上搜索自己所需要的包
  • 从https://registry.npmjs.org/服务器上下载自己需要的包

5.2 npm初体验

5.2.1 、格式化时间的传统做法

5.2.2 、格式化时间的高级做法

  • ① 使用npm包管理工具,在项目中安装格式化时间的包moment
  • ② 使用require( )导入格式化时间的包
  • ③ 参考moment的官方API文档对时间进行格式化

5.2.3、在项目中安装包的命令

如果想在项目中安装指定名称的包,需要运行如下的命令:

上述的装包命令,可以简写成如下格式:

5.2.4、初次装包后多了哪些文件

初次装包完成后,在项目文件夹下多-个叫做node_ modules 的文件夹和package-lock.json的配置文件。
其中:

node_ modules文件夹用来存放所有已安装到项目中的包。require( 导入第三E方包时,就是从这个目录中查找并加载包。

package-lock.json 配置文件用来记录node_ modules 目录下的每一个包的下载信息, 例如包的名字、版本号、下载地址等。

注意:程序员不要手动修改node modules或package-lock.json文件中的任何代码,npm包管理工具会自动维护它们。

5.3、 包管理工具配置文件

5.3.1、多人协作的问题

5.3.2、如何记录项目中安装了哪些包

在项目根目录中,创建一个叫做 package.json的配置文件,即可用来记录项目中安装了哪些包。从而方便剔除 node_ modules 目录之后,在团队成员之间共享项目的源代码。
注意:今后在项目开发中,一定要把 node_ modules 文件夹,添加到.gitignore忽略文件中。

5.3.3、快速创建package.json

npm包管理工具提供了一个快捷命令,可以在执行命令时所处的目录中,快速创建package.json这个包管理
配置文件:

注意:

  • ① 上述命令只能在英文的目录下成功运行!所以,项目文件夹的名称一 定要使用英文命名,不要使用中文,不能出现空格。
  • ② 运行npm install命令安装包的时候,npm包管理工具会自动把包的名称和版本号,记录到package.json中。

5.3.4、dependencies节点

5.3.5、一次性安装所有的包

可以运行 npm install命令(或 npm i)一次性安装所有的依赖包:

5.3.6、卸载包

可以运行npm uninstall命令,来卸载指定的包:

注意: npm uninstall命令执行成功后,会把卸载的包,自动从package.json的dependencies中移除掉。

5.3.7、devDependencies节点

如果某些包只在项目开发阶段会用到,在项目上线之后不会用到,则建议把这些包记录到devDependencies节点中。与之对应的,如果某些包在开发和项目上线之后都需要用到,则建议把这些包记录到dependencies节点中。
您可以使用如下的命令,将包记录到devDependencies节点中:

5.3.8、切换npm的下包镜像源

下包的镜像源,指的就是下包的服务器地址

5.3.9、nrm

为了更方便的切换下包的镜像源,我们可以安装nrm这个小工具,利用nrm提供的终端命令,可以快速查看和切换下包的镜像源。

5.4、包的分类

5.4.1、项目包

那些被安装到项目的node_ modules目录中的包,都是项目包。
项目包又分为两类,分别是:

  • 开发依赖包 (被记录到devDependencies节点中的包,只在开发期间会用到)
  • 核心依赖包 (被记录到dependencies节点中的包,在开发期间和项目上线之后都会用到)

5.4.2、全局包

在执行npm install命令时,如果提供了-g参数,则会把包安装为全局包。
全局包会被安装到C:\Users\用户目录AppData\Roaming\npm\node_ modules 目录下。

注意:

  • ① 只有工具性质的包,才有全局安装的必要性。因为它们提供了好用的终端命令。
  • ② 判断某个包是否需要全局安装后才能使用,可以参考官方提供的使用说明即可。

5.4.3、i5ting_ toc

i5ting_ toc 是一个可以把md文档转为html页面的小工具,使用步骤如下:

5.4.4、规范的包结构

在清楚了包的概念、以及如何下载和使用包之后,接下来,我们深入了解一下包的内部结构。
一个规范的包,它的组成结构,必须符合以下3点要求:

  • ① 包必须以单独的目录而存在
  • ② 包的顶级目录下要必须包含package.json这个包管理配置文件
  • ③ package.json 中必须包含name, version, main 这三个属性,分别代表包的名字、版本号、包的入口。

注意:以上3点要求是一个规范的包结构必须遵守的格式,关于更多的约束,可以参考如下网址:
https://yarnpkg.com/zh-Hans/docs/package-json

5.5、模块的加载机制

5.5.1、优先从缓存中加载

模块在第一次加载后会被缓存。这也意味着多次调用 require( )不会导致模块的代码被执行多次。
注意:不论是内置模块、用户自定义模块、还是第三方模块,它们都会优先从缓存中加载,从而提高模块的加载效率

5.5.2、内置模块的加载机制

内置模块是由Node.js官方提供的模块,内置模块的加载优先级最高。

例如,require(‘fs’) 始终返回内置的fs模块,即使在node_ modules目录下有名字相同的包也叫做fs。

5.5.3、自定义模块的加载机制

使用require()加载自定义模块时,必须指定以./或…开头的路径标识符。在加载自定义模块时,如果没有指定./或.这样的路径标识符,则node会把它当作内置模块或第三方模块进行加载。

同时,在使用require0导入自定义模块时,如果省略了文件的扩展名,则Node.js会按顺序分别尝试加载以下的文件:

  • ① 按照确切的文件名进行加载
  • ② 补全.js扩展名进行加载
  • ③ 补全.json扩展名进行加载
  • ④ 补全.node扩展名进行加载
  • ⑤ 加载失败,终端报错

5.5.4、第三方模块的加载机制

如果传递给require0的模块标识符不是一个内置模块, 也没有以’/’ 或./’ 开头, 则Node.js会从当前模块的父目录开始,尝试从/node_ modules 文件夹中加载第二彷模块。

如果没有找到对应的第三方模块,则移动到再上一层父目录中,进行加载,直到文件系统的根目录。
例如,假设在’C:\Users\itheima\project\foo.js’文件里调用了requre('tools"),则Node.js会按以下顺序查找:
① C:\Users\itheima\project\node modules\tools
② C:\Users\itheima\node_ modules\tools
③ C:\Users\node_ modules\tools
④ C:\node_ modules\tools

5.5.5、目录作为模块

当把目录作为模块标识符,传递给require0进行加载的时候,有三种加载方式:

  • ① 在被加载的目录下查找一个叫做package.json的文件,并寻找main属性,作为require( )加载的入口

  • 如果目录里没有package.json文件,或者main入口不存在或无法解析,则Node.js将会试图加载目录下的index.js文件。
  • ③ 如果以上两步都失败了,则Node.js会在终端打印错误消息,报告模块的缺失: Error: Cannot find module

六、Express简介

6.1、什么是Express

官方给出的概念: Express 是基于Node.js平台,快速、开放、极简的Web开发框架。
通俗的理解: Express 的作用和Node.js内置的http模块类似,是专门用来创建Web服务器的。
Express的本质:就是一个npmy上的第三方包,提供了快速创建Web服务器的便捷方法。
Express的中文官网: https://www.expressjs.com.cn/

6.1.1、进一步理解Express

6.1.2、Express能做什么

对于前端程序员来说,最常见的两种服务器,分别是:

  • Web网站服务器:专门对外提供Web网页资源的服务器。

  • API 接口服务器:专]对外提供API接口的服务器。

使用Express,我们可以方便、快速的创建Web网站的服务器或API接口的服务器。

6.2、安装

在项目所处的目录中,运行如下的终端命令,即可将express安装到项目中使用:

6.2.1、创建基本的Web服务器

6.2.2、监听GET请求

通过app.get( )方法,可以监听客户端的GET请求,具体的语法格式如下:

6.2.3、监听POST请求

通过app.post( )方法,可以监听客户端的POST请求,具体的语法格式如下:

6.2.4、把内容响应给客户端

通过res.send()方法,可以把处理好的内容,发送给客户端:

6.2.5、获取URL中携带的查询参数

通过req.query对象,可以访问到客户端通过查询字符串的形式,发送到服务器的参数:

6.2.6、获取URL中的动态参数

通过req.params对象,可以访问到URL中,通过:匹配到的动态参数:

6.3、托管静态资源

6.3.1、express.static( )

express提供了一个非常好用的函数, 叫做express.static( ),通过它,我们可以非常方便地创建一个静态资源服务器,例如,通过如下代码就可以将public目录下的图片、CSS 文件、JavaScript 文件对外开放访问了:

6.3.2、托管多个静态资源目录

如果要托管多个静态资源目录,请多次调用express.static( )函数:

访问静态资源文件时,express.static( ) 函数会根据目录的添加顺序查找所需的文件。

6.3.3、挂载路径前缀

如果希望在托管的静态资源访问路径之前,挂载路径前缀,则可以使用如下的方式:

6.4、 nodemon

6.4.1、为什么要使用nodemon

在编写调试Node.js项目的时候,如果修改了项目的代码,则需要频繁的手动close掉,然后再重新启动,非常繁琐。

现在,我们可以使用nodemon (https://www.npmjs.com/package/nodemon) 这个工具, 它能够监听项目文件的变动,当代码被修改后,nodemon 会自动帮我们重启项目,极大方便了开发和调试。

6.4.2、安装nodemon

在终端中,运行如下命令,即可将nodemon安装为全局可用的工具:

6.5、Express路由

6.5.1、生活中的路由

6.5.2、Express中的路由

在Express中,路由指的是客户端的请求与服务器处理函数之间的映射关系。
Express中的路由分3部分组成,分别是请求的类型、请求的URL地址、处理函数,格式如下:

6.5.3、Express中的路由的例子

6.5.4、路由的匹配过程

每当一个请求到达服务器之后,需要先经过路由的匹配,只有匹配成功之后,才会调用对应的处理函数。
在匹配时,会按照路由的顺序进行匹配,如果请求类型和请求的URL同时匹配成功,则Express会将这次请求,转交给对应的function函数进行处理。

6.6、路由的使用

6.6.1、最简单的用法

在Express中使用路由最简单的方式,就是把路由挂载到app上,示例代码如下:

6.6.2、模块化路由

为了方便对路由进行模块化的管理,Express 不建议将路由直接挂载到app上,而是推荐将路由抽离为单独的模块。
将路由抽离为单独模块的步骤如下:

  • ① 创建路由模块对应的js文件
  • ② 调用express.Router( )函数创建路由对象
  • ③ 向路由对象上挂载具体的路由
  • ④ 使用module.exports向外共享路由对象
  • ⑤ 使用app.use( )函数注册路由模块

6.6.3、创建路由模块

6.6.4、注册路由模块

6.6.5、为路由模块添加前缀

类似于托管静态资源时,为静态资源统-挂载访问前缀一 样, 路由模块添加前缀的方式也非常简单:

7、Express 中间件

7.1.1、中间件的概念

现实生活中的例子

7.1.2、Express中间件的调用流程

当一个请求到达Express的服务器之后,可以连续调用多个中间件,从而对这次请求进行预处理。

7.1.3、中间件的概念

Express中间件的格式

7.1.4、next函数的作用

next函数是实现多个中间件连续调用的关键,它表示把流转关系转交给下一一个中间件或路由。.

7.1.5、定义中间件函数

可以通过如下的方式,定义一个最简单的中间件函数:

7.1.6、中间件的作用

多个中间件之间,共享同一份req和res.基于这样的特性,我们可以在上游的中间件中,统-为req或res对象添加自定义的属性或方法,供下游的中间件或路由进行使用。

7.1.7、局部生效的中间件

不使用app.use( )定义的中间件,叫做局部生效的中间件,示例代码如下:

7.1.8、定义多个局部中间件

可以在路由中,通过如下两种等价的方式,使用多个局部中间件:

7.1.9、了解中间件的5个使用注意事项

  • ① 一定要在路由之前注册中间件
  • ② 客户端发送过来的请求,可以连续调用多个中间件进行处理
  • ③ 执行完中间件的业务代码之后,不要忘记调用next( )函数
  • ④ 为了防止代码逻辑混乱,调用next(函数后不要再写额外的代码
  • ⑤ 连续调用多个中间件时,多个中间件之间,共享req和res对象

7.2、中间件的分类

7.2.1、应用级别的中间件

通过app.use( )或app.get( )或app.post(),绑定到app实例上的中间件,叫做应用级别的中间件,代码示例如下:

7.2.2、路由级别的中间件

绑定到express.Router( )实例上的中间件,叫做路由级别的中间件。它的用法和应用级别中间件没有任何区别。只不过,应用级别中间件是绑定到app实例上,路由级别中间件绑定到router实例上,代码示例如下:

7.2.3、错误级别的中间件

错误级别中间件的作用:专门]用来捕获整个项目中发生的异常错误,从而防止项目异常崩溃的问题。
格式:错误级别中间件的function处理函数中,必须有4个形参。,形参顺序从前到后,分别是(err, req, res, next)。

注意:错误级别的中间件,必须注册在所有路由之后!

7.2.4、Express内置的中间件

自Express 4.16.0版本开始,Express 内置了3个常用的中间件,极大的提高了Express项目的开发效率和体验:

  • ① express.static 快速托管静态资源的内置中间件,例如: HTML 文件、图片、CSS 样式等(无兼容性)
  • ② express.json解析JSON格式的请求体数据(有兼容性,仅在4.16.0+版本中可用)
  • ③ express.urlencoded 解析URL-encoded格式的请求体数据(有兼容性,仅在4.16.0+版本中可用)

7.2.5、第三方的中间件

非Express官方内置的,而是由第三方开发出来的中间件,叫做第三方中间件。在项目中,大家可以按需下载并配置第三方中间件,从而提高项目的开发效率。

例如:在express@4.16.0之前的版本中,经常使用body-parser这个第三方中间件,来解析请求体数据。使用步骤如下:

  • ① 运行npm install body-parser安装中间件
  • ② 使用require导入中间件
  • ③ 调用app.use()注册并使用中间件

注意: Express 内置的express.urlencoded中间件,就是基于body-parser这个第=三方中间件进一步封装出来的。

7.3、自定义中间件

7.3.1、需求描述与实现步骤

自己手动模拟一个类似于express.urlencoded这样的中间件,来解析POST提交到服务器的表单数据。
实现步骤:

  • ① 定义中间件
  • ② 监听req 的data事件
  • ③ 监听req的end事件
  • ④ 使用querystring模块解析请求体数据
  • ⑤ 将解析出来的数据对象挂载为req.body
  • ⑥ 将自定义中间件封装为模块

7.3.2、定义中间件

使用app.use( )来定义全局生效的中间件,代码如下:

7.3.3、监听req的data事件

在中间件中,需要监听req对象的data事件,来获取客户端发送到服务器的数据。
如果数据量比较大,无法一次性发送完毕, 则客户端会把数据切割后,分批发送到服务器。所以data事件可能会触发多次,每一次触发data事件时,获取到数据只是完整数据的一部分, 需要手动对接收到的数据进行拼接。

7.3.4、监听req的end事件

当请求体数据接收完毕之后,会自动触发req的end事件。
因此,我们可以在req的end事件中,拿到并处理完整的请求体数据。示例代码如下:

7.3.5、使用querystring模块解析请求体数据

Node.js内置了一个querystring模块,专门用来处理查询字符串。通过这个模块提供的parse()函数,可以轻松把查询字符串,解析成对象的格式。示例代码如下:

7.3.6、将解析出来的数据对象挂载为req.body

上游的中间件和 下游的中间件及路由之间,共享同一份req 和res。 因此,我们可以将解析出来的数据, 挂载为req的自定义属性,命名为req.body,供下游使用。示例代码如下:

7.3.7、将自定义中间件封装为模块

为了优化代码的结构,我们可以把自定义的中间件函数,封装为独立的模块,示例代码如下:

7.4、使用Express 写接口

7.4.1、创建基本的服务器

7.4.2、创建API路由模块

7.4.3、编写GET接口

7.4.4、编写POST接口


注意:如果要获取URL-encoded格式的请求体数据,必须配置中间件app.use(express.urlencoded({ extended: false }))

7.5、CORS跨域资源共享

7.5.1、接口的跨域问题

刚才编写的GET和POST接口,存在一-个很严重的问题:不支持跨域请求。
解决接口跨域问题的方案主要有两种:

  • ① CORS (主流的解决方案,推荐使用)
  • ② JSONP (有缺陷的解决方案:只支持GET请求)

7.5.2、使用cors中间件解决跨域问题

cors是Express的一个第三方中间件。通过安装和配置cors中间件,可以很方便地解决跨域问题。
使用步骤分为如下3步:

  • ① 运行npm install cors安装中间件
  • ② 使用const cors = require('cors)导入中间件
  • ③ 在路由之前调用app.use(cors( ))配置中间件

7.5.3、什么是CORS

CORS (Cross-Origin Resource Sharing,跨域资源共享)由-系列HTTP响应头组成,这些HTTP响应头决定浏览器是否阻止前端JS代码跨域获取资源。

浏览器的同源安全策略默认会阻止网页“跨域”获取资源。但如果接口服务器配置了CORS相关的HTTP响应头,就可以解除浏览器端的跨域访问限制。

7.5.4、CORS的注意事项

  • ① CORS 主要在服务器端进行配置。客户端浏览器无须做任何额外的配置,即可请求开启了CORS的接口。
  • ② CORS 在浏览器中有兼容性。只有支持XMLHttpRequestLevel2的浏览器,才能正常访问开启了CORS的服务端接口(例如: IE10+、 Chrome4+、FireFox3.5+) 。

7.5.5、CORS响应头部- Access-Control-Allow-Origin

响应头部中可以携带一个Access-Control-Allow-Origin字段,其语法如下:

其中,origin参数的值指定了允许访问该资源的外域URL。
例如,下面的字段值将只允许来自http://itcast.cn 的请求:


如果指定了Access-Control-Allow-Origin字段的值为通配符*,表示允许来自任何域的请求,示例代码如下:

7.5.6、CORS响应头部- Access-Control-Allow-Headers

默认情况下,CORS仅支持客户端向服务器发送如下的9个请求头:
Accept、Accept-Language、 Content-Language DPR、Downlink、 Save-Data、 Viewport-Width、 Width 、Content-Type (值仅限于text/plain、multipart/form-data、 application/x-www-form-urlencoded 三者之一)
如果客户端向服务器发送了额外的请求头信息,则需要在服务器端,通过Access-Control-Allow-Headers对额外的请求头进行声明,否则这次请求会失败!

7.5.7、CORS响应头部- Access-Control-Allow-Methods

默认情况下,CORS仅支持客户端发起GET、POST、 HEAD请求。

如果客户端希望通过PUT、DELETE 等方式请求服务器的资源,则需要在服务器端,通过Access-Control-Alow-Methods来指明实际请求所允许使用的HTTP方法。

7.6、CORS请求的分类

客户端在请求CORS接口时,根据请求方式和请求头的不同,可以将CORS的请求分为两大类,分别是:

  • ① 简单请求
  • ② 预检请求

7.6.1、简单请求

同时满足以下两大条件的请求,就属于简单请求:

  • ① 请求方式: GET、POST、HEAD三者之一

  • ② HTTP 头部信息不超过以下几种字段“ 无自定义头部字段、Accept、 Accept-Language、
    Content-Language、 DPR、Downlink、Save-Data、 Viewport-Width、 Width 、Content-Type (只有三个值application/x-www-form-urlencoded、multipart/form-data、 text/plain)

7.6.2、预检请求

只要符合以下任何一个条件的请求,都需要进行预检请求:

  • ① 请求方式为 GET、POST、HEAD之外的请求Method类型
  • ② 请求头中包含自定义头部字段
  • ③ 向服务器发送 了application/json格式的数据

在浏览器与服务器正式通信之前,浏览器会先发送OPTION请求进行预检,以获知服务器是否允许该实际请求,所以这一次的QPTION请求称为"预检请求”。服务器成功响应预检请求后,才会发送真正的请求,并且携带真实数据。

7.6.3、简单请求和预检请求的区别

简单请求的特点:客户端与服务器之间只会发生一次请求。
预检请求的特点:客户端与服务器之间会发生两次请求,OPTION 预检请求成功之后,才会发起真正的请求。

7.7、JSONP接口

7.7.1、回顾JSONP的概念与特点

概念:浏览器端通过

  • ① JSONP不属于真正的Ajax请求,因为它没有使用XMLHttpRequest这个对象。

  • ② JSONP 仅支持GET请求,不支持POST、PUT、 DELETE 等请求。

7.7.2、创建JSONP接口的注意事项

如果项目中已经配置了CORS跨域资源共享,为了防止冲突,必须在配置CORS中间件之前声明JSONP的接口。否则JSONP接口会被处理成开启了CORS的接口。示例代码如下:

7.7.3、实现JSONP接口的步骤

  • ① 获取客户端发送过来的回调函数的名字
  • ② 得到要通过JSONP形式发送给客户端的数据
  • ③ 根据前两步得到的数据,拼接出一个函数调用的字符串
  • ④ 把上一步拼接得到的字符串,响应给客户端的

7.7.4、实现JSONP接口的具体代码

7.7.5、在网页中使用jQuery发起JSONP请求

调用$.ajax( )函数,提供JSONP的配置选项,从而发起JSONP请求,示例代码如下:

八、Mysql

8.1、使用SQL管理数据库;

8.1.1、SQL的学习目标

重点掌握如何使用SQL从数据表中:
查询数据(select) 、 插入数据(insert into)、 更新数据(update) 、删除数据(delete)
额外需要掌握的4种SQL语法:
where条件、and和or运算符、order by排序、、count(*) 函数

8.2、SQL的SELECT语句

8.2.1、语法

SELECT语句用于从表中查询数据。执行的结果被存储在一个结果表中(称为结果集)。语法格式如下:

注意: SQL语句中的关键字对大小写不敏感。SELECT 等效于select, FROM等效于from。

8.2.2、SELECT *示例

我们希望从users表中选取所有的列,可以使用符号*取代列的名称,示例如下:

8.2.3、SELECT列名称示例

如需获取名为"username“和”password"的列的内容(从名为"users"的数据库表),请使用下面的SELECT语句:

8.3、SQL的INSERT INTO语句

8.3.1、语法

INSERT INTO语句用于向数据表中插入新的数据行,语法格式如下:

8.3.2、INSERT INTO示例

向users表中,插入一条username为tony stark, password 为098123的用户数据,示例如下:

8.4、SQL的UPDATE语句

8.4.1、语法

Update语句用于修改表中的数据。语法格式如下:

8.4.2、UPDATE示例- 更新某一行中的一个列

把users表中id为7的用户密码,更新为888888。示例如下:

把users表中id为2的用户密码和用户状态,分别更新为admin123和1。示例如下:

8.5、SQL的DELETE语句

8.5.1、语法

DELETE语句用于删除表中的行。语法格式如下:

8.5.2、DELETE示例

从users表中,删除id为4的用户,示例如下:

8.6、SQL的WHERE子句

8.6.1、语法

WHERE子句用于限定选择的标准。在SELECT、UPDATE、DELETE 语句中,皆可使用WHERE子句来限定选择的标准。

8.6.2、可在WHERE子句中使用的运算符

下面的运算符可在WHERE子句中使用,用来限定选择的标准:

注意:在某些版本的SQL中,操作符<>可以写为!=

8.6.3、WHERE子句示例

可以通过WHERE子句来限定SELECT的查询条件:

8.7、SQL的AND和OR运算符

8.7.1、语法

AND和OR可在WHERE子语句中把两个或多个条件结合起来。
AND表示必须同时满足多个条件,相当于JavaScript中的&&运算符,例如if(a!== 10 &&a!== 20)
OR表示只要满足任意一个条件即可,相当于JavaScript中的|I 运算符,例如if(a!== 10||a!== 20)

8.7.2、AND运算符示例

使用AND来显示所有status为0,并且id小于3的用户:

8.7.3、OR运算符示例

使用OR来显示所有status 为1,或者username为zs的用户:

8.8、SQL的ORDER BY子句

8.8.1、语法

  • ORDER BY语句用于根据指定的列对结果集进行排序。
  • ORDER BY语句默认按照升序对记录进行排序。
  • 如果您希望按照降序对记录进行排序,可以使用DESC关键字。

8.8.2、ORDER BY子句 - 升序排序

对users表中的数据,按照status字段进行升序排序,示例如下:

8.8.3、ORDER BY子句 - 降序排序

对users表中的数据,按照id字段进行降序排序,示例如下:

8.8.4、ORDER BY子句 - 多重排序

对users表中的数据,先按照status字段进行降序排序,再按照username的字母顺序,进行升序排序,示例如下:

8.9、SQL的COUNT(*)函数

8.9.1、语法

COUNT(*)函数用于返回查询结果的总数据条数,语法格式如下:

8.9.2、COUNT(*)示例

查询users表中status为0的总数据条数:

8.9.3、使用AS为列设置别名

如果希望给查询出来的列名称设置别名,可以使用AS关键字示例如下:

8.10、在项目中操作MySQL

8.10.1、在项目中操作数据库的步骤

  • ① 安装操作MySQL数据库的第三方模块(mysqI)
  • ② 通过mysql模块连接到MySQL数据库
  • ③ 通过mysql模块执行SQL语句

8.10.2、安装mysqI模块

mysql模块是托管于npm上的第三方模块。它提供了在Node.js项目中连接和操作MySQL数据库的能力。
想要在项目中使用它,需要先运行如下命令,将mysql安装为项目的依赖包:

8.10.3、配置mysql模块

在使用mysql模块操作MySQL数据库之前,必须先对mysql模块进行必要的配置,主要的配置步骤如下:

8.10.4、测试mysql模块能否正常工作

调用db.query()函数,指定要执行的SQL语句,通过回调函数拿到执行的结果:

8.10.5、查询数据

查询users表中所有的数据:

8.10.6、插入数据

向users表中新增数据,其中username为Spider-Man, password 为pcc321。示例代码如下:
**
**

8.10.7、插入数据的便捷方式

向表中新增数据时,如果数据对象的每个属性和数据表的字段一 一对应, 则可以通过如下方式快速插入数据:

8.10.8、更新数据

可以通过如下方式,更新表中的数据:

8.10.9、更新数据的便捷方式

更新表数据时,如果数据对象的每个属性和数据表的字段一 一对应, 则可以通过如下方式快速更新表数据:

8.10.9、删除数据

在删除数据时,推荐根据id这样的唯一标识, 来删除对应的数据。示例如下:

8.10.10、标记删除

使用DELETE语句,会把真正的把数据从表中删除掉。为了保险起见,推荐使用标记删除的形式,来模拟删除的动作。
所谓的标记删除,就是在表中设置类似于status这样的状态字段,来标记当前这条数据是否被删除。

当用户执行了删除的动作时,我们并没有执行DELETE语句把数据删除掉,而是执行了UPDATE语句,将这条数据对应的status字段标记为删除即可。

九、前后端的身份认证

9.1、Web开发模式

目前主流的Web开发模式有两种,分别是:

  • ① 基于服务端渲染的传统Web开发模式
  • ② 基于前后端分离的新型Web开发模式

9.1.1、服务端渲染的传统Web开发模式

服务端渲染的概念:服务器 发送给客户端的HTML页面,是在服务器通过字符串的拼接,动态生成的。因此,客户端不需要使用Ajax这样的技术额外请求页面的数据。代码示例如下:

9.1.2、前后端分离的新型Web开发模式

前后端分离的概念:前后端分离的开发模式,依赖于Ajax技术的广泛应用。简而言之,前后端分离的Web开发模式,就是后端只负责提供API接口,前端使用Ajax调用接口的开发模式。

9.1.2、如何选择Web开发模式

不谈业务场景而盲目选择使用何种开发模式都是耍流氓。

  • 比如企业级网站,主要功能是展示而没有复杂的交互,并且需要良好的SEO,则这时我们就需要使用服务器端渲染;
  • 而类似后台管理项目,交互性比较强,不需要考虑SEO,那么就可以使用前后端分离的开发模式。

另外,具体使用何种开发模式并不是绝对的,为了同时兼顾了首页的渲染速度和前后端分离的开发效率,一些网站采用了首屏服务器端渲染+其他页面前后端分离的开发模式。

9.2、身份认证

9.2.1、什么是身份认证

身份认证(Authentication) 又称"身份验证”、“鉴权” ,是指通过一定的手段,完成对用户身份的确认。

  • 日常生活中的身份认证随处可见,例如:高铁的验票乘车,手机的密码或指纹解锁,支付宝或微信的支付密码等。
  • 在Web开发中,也涉及到用户身份的认证,例如:各大网站的手机验证码登录、邮箱密码登录、二维码登录等。

9.2.2、为什么需要身份认证

身份认证的目的,是为了确认当前所声称为某种身份的用户,确实是所声称的用户。例如,你去找快递员取快递,你要怎么证明这份快递是你的。
在互联网项目开发中,如何对用户的身份进行认证,是一个值得深入探讨的问题。例如,如何才能保证网站不会错误的将“马云的存款数额”显示到” 马化腾的账户”上。

9.2.3、不同开发模式下的身份认证

对于服务端渲染和前后端分离这两种开发模式来说,分别有着不同的身份认证方案:

  • ① 服务端渲染推荐使用 Session认证机制
  • ② 前后端分离推荐使用 JWT认证机制

9.3、Session认证机制

9.3.1、HTTP协议的无状态性

了解HTTP协议的无状态性是进一步学习Session认证机制的必要前提。
HTTP协议的无状态性,指的是客户端的每次HTTP请求都是独立的,连续多个请求之间没有直接的关系,服务器不会主动保留每次HTTP请求的状态。

9.3.2、如何突破HTTP无状态的限制

对于超市来说,为了方便收银员在进行结算时给VIP用户打折,超市可以为每个VIP用户发放会员卡。

注意:现实生活中的会员卡身份认证方式,在Web开发中的专业术语叫做Cookie.

9.3.3、什么是Cookie

Cookie是存储在用户浏览器中的一段不超过4 KB的字符串。它由一个名称(Name) 、-个值(Value) 和其它几个用于控制Cookie有效期、安全性、使用范围的可选属性组成。

不同域名下的Cookie各自独立,每当客户端发起请求时,会自动把当前域名下所有未过期的Cookie一同发送到服务器。
Cookie的几大特性:

  • ① 自动发送
  • ② 域名独立
  • ③ 过期时限
  • ④ 4KB 限制

9.3.4、Cookie在身份认证中的作用

客户端第一次请求服务器的时候,服务器通过响应头的形式,向客户端发送一个身 份认证的Cookie,客户端会自动将Cookie保存在浏览器中。
随后,当客户端浏览器每次请求服务器的时候,浏览器会自动将身份认证相关的Cookie,通过请求头的形式发送给服务器,服务器即可验明客户端的身份。

9.3.5、Cookie不具有安全性

由于Cookie是存储在浏览器中的,而且浏览器也提供了读写Cookie的API,因此Cookie很容易被伪造,不具有安全性。因此不建议服务器将重要的隐私数据,通过Cookie的形式发送给浏览器。

注意:千万不要使用Cookie存储重要且隐私的数据! 比如用户的身份信息、密码等。

9.3.6、提高身份认证的安全性

为了防止客户伪造会员卡,收银员在拿到客户出示的会员卡之后,可以在收银机上进行刷卡认证。只有收银机确认存在的会员卡,才能被正常使用。

这种 ” 会员卡 + 刷卡认证” 的设计理念,就是Session认证机制的精髓。

9.3.7、Session的工作原理

9.4、在Express中使用Session认证

9.4.1、安装express-session中间件

在Express项目中,只需要安装express-session中间件,即可在项目中使用Session认证:

9.4.2、配置express-session中间件

express-session中间件安装成功后,需要通过app.use(来注册session中间件,示例代码如下:

9.4.3、向session中存数据

当express-session中间件配置成功后,即可通过req.session来访问和使用session对象,从而存储用户的关键信息:

9.4.4、从session中取数据

可以直接从req.session对象上获取之前存储的数据,示例代码如下:

9.4.5、清空session

调用req.session.destroy()函数,即可清空服务器保存的session信息。

9.5、JWT认证机制

9.5.1、了解Session认证的局限性

Session认证机制需要配合Cookie才能实现。由于Cookie默认不支持跨域访问,所以,当涉及到前端跨域请求后端接口的时候,需要做很多额外的配置,才能实现跨域Session认证。
注意:

  • 当前端请求后端接口不存在跨域问题的时候,推荐使用Session身份认证机制。
  • 当前端需 要跨域请求后端接口的时候,不推荐使用Session身份认证机制,推荐使用JWT认证机制。

9.5.2、什么是JWT

JWT (英文全称: JSON Web Token)是目前最流行的跨域认证解决方案。

9.5.3、JWT的工作原理


总结:用户的信息通过Token字符串的形式,保存在客户端浏览器中。服务器通过还原Token字符串的形式来认证用户的身份。

9.5.4、JWT的组成部分

JWT通常由三部分组成,分别是Header (头部)、Payload (有效荷载)、Signature (签名)。
三者之间使用英文的"." 分隔,格式如下:

下面是JWT字符串的示例:

9.5.5、JWT的三个部分各自代表的含义

JWT的三个组成部分,从前到后分别是Header、Payload、 Signature.
其中:

  • Payload 部分才是真正的用户信息,它是用户信息经过加密之后生成的字符串。

  • Header 和Signature是安全性相关的部分,只是为了保证Token的安全性。

9.5.6、JWT的使用方式

客户端收到服务器返回的JWT之后,通常会将它储存在localStorage或sessionStorage中。

此后,客户端每次与服务器通信,都要带上这个JWT的字符串,从而进行身份认证。推荐的做法是把JWT放在HTTP请求头的Authorization字段中,格式如下:

9.6、在Express中使用JWT

9.6.1、安装JWT相关的包

运行如下命令,安装如下两个JWT相关的包:

其中:

  • jsonwebtoken用于生成JWT字符串

  • express-jwt 用于将JWT字符串解析还原成JSON对象

9.6.2、导入JWT相关的包

使用require()函数,分别导入JWT相关的两个包:

9.6.3、定义secret密钥

为了保证JWT字符串的安全性,防止JWT字符串在网络传输过程中被别人破解,我们需要专i门]定义一一个用于加密和解密的secret密钥:

  • ① 当生成JWT字符串的时候,需要使用secret密钥对用户的信息进行加密,最终得到加密好的JWT字符串
  • ② 当把JWT字符串解析还原成JSON对象的时候,需要使用secret密钥进行解密

9.6.4、在登录成功后生成JWT字符串

调用jsonwebtoken包提供的sign()方法,将用户的信息加密成JWT字符串,响应给客户端:

9.6.5、将JWT字符串还原为JSON对象

客户端每次在访问那些有权限接口的时候,都需要主动通过请求头中的Authorization字段,将Token字符串发送到服务器进行身份认证。

此时,服务器可以通过express-jwt这个中间件,自动将客户端发送过来的Token解析还原成JSON对象:

9.6.6、使用req.user获取用户信息

当express-jwt这个中间件配置成功之后,即可在那些有权限的接口中,使用req.user对象,来访问从JWT字符串中解析出来的用户信息了,示例代码如下:

9.6.7、捕获解析JWT失败后产生的错误

当使用express-jwt解析Token字符串时,如果客户端发送过来的Token字符串过期或不合法,会产生一个解析失败的错误,影响项目的正常运行。我们可以通过Express的错误中间件,捕获这个错误并进行相关的处理,示例代码如下:

前后端交互、Node、npm、Express、mysql基础相关推荐

  1. 前后端交互node服务器

    文章目录 1.概念 2.创建web服务器 3.获取报文 4.响应报文 5.请求参数 GET请求参数 POST请求参数 6.路由 7.静态资源和动态资源 8.同步API和异步API 同步API 异步AP ...

  2. Mysql+Echarts+Python+Flask实现前后端交互及数据可视化

    前言 社区版Pycharm实现python+flask+echarts+Mysql实现简单的前后端交互. 新手入门,记录经验,欢迎交流. 一.首先检测Flask框架是否成功? 首先,在你的项目下中安装 ...

  3. Ionic+Angular+Express实现前后端交互使用HttpClient发送get请求数据并加载显示(附代码下载)

    场景 Ionic介绍以及搭建环境.新建和运行项目: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/106308166 在上面搭建起 ...

  4. 前后端交互ajax和axios入门讲解,以及http与服务器基础

    ajax和http小白入门,客户端与服务器基础讲解,前后端交互(从入门到实践详细解析) 文章目录 ajax和http小白入门,客户端与服务器基础讲解,前后端交互(从入门到实践详细解析) 前言 一.Aj ...

  5. 前端angular与服务器端nodejs实现从mysql数据库读取数据实现前后端交互实例

    一.angular前端交互 使用angular框架的人都知道angular提供了内置的HttpClientModule模块来实现前后端交互请求. 实际操作如下: 1.在app.module.ts文件中 ...

  6. vue+node.js前后端交互中的token令牌验证

    这篇文章分享一下本人学习vue+node.js前后端交互中的登录token令牌的心得 最近准备写一个个人博客网站,前端采用的是vue+element,后端用node.js 在做用户登录的时候就想到 如 ...

  7. python前后端交互_Django基础之简单的前后端交互

    Python Python开发 Python语言 Django基础之简单的前后端交互 学习Django有一段时间了,最近刚好写了一个小项目,用到了前后端交互,刚开始写前后端交互确实很让人头晕目眩呢,下 ...

  8. ajax+node前后端交互学习笔记(1)

    大致理解前后端交互的概念 当你去餐馆吃饭的时候,坐下后服务员会带着⼀个菜单过来,问你需要点什么菜,这个时候你浏览了 菜单上的菜,把想吃的菜告诉服务员,服务员把你点的菜拿到后台,后台根据你点的菜名,逐⼀ ...

  9. 通过JS搭建后台,并实现前后端交互

    通过JS搭建后台,并实现前后端交互 一.下载node 二.建立环境 1.打开WebStrom,进入命令行 2.输入命令后回车 ,换个抓取文件的地址 3.在命令行中下载一些配置文件 4.编辑JS文件 三 ...

  10. 前后端交互,网络请求

    这边文章主要根据我自己的前端开发工作经验,东拼西凑出来的一点理解,希望能够对大家有点帮助,如果有误导或者错误的地方还请帮助指正,感谢!!! 前后端交互我理解主要分为三个主要的部分: 1.客户端 2.服 ...

最新文章

  1. 关于寒假和春季开学!教育部通知来了!近400所高校寒假时间已公布!
  2. 下一代 Web 应用模型 —— Progressive Web App (PWA)
  3. Qt学习笔记之SQLITE数据库
  4. SPOJ - BALNUM Balanced Numbers(数位dp+进制转换)
  5. [转]asp 没有权限: 'CreateObject'的解决方法
  6. Atitit 法学体系树与知识点attilax大总结 法学体系 0301法学类 030101 法学理论 宪法 行政法 民法 商法 婚姻法和继承法 经济法 社会法 刑法 民事诉讼法 行政诉讼法
  7. JSP javaweb餐厅点餐系统源码(点餐系统)jsp点餐系统网上订餐
  8. RDP Wrapper
  9. CNN--MINIST
  10. VS2019正式版注册码秘钥
  11. android tabhost的使用方法,android TabHost的基本使用
  12. python输入整数_限制输入整数的5种情况(学数学学Python编程)
  13. mysql中的eeplace,mysql必知必会笔记
  14. 阿里云对象存储OSS收费标准(很详细一看就懂)
  15. 有哲理有关java的_从Java到“浑沌之死”再到人生哲理
  16. Apache详解(一)Internet和HTTP协议
  17. PL_SQL模块学习之十六、记录
  18. leaflet、cesium加载百度地图,加载自定义样式百度地图
  19. 一文搞懂Kiss、Yagni原则
  20. 参考文献管理软件Jabref和Zotero使用笔记

热门文章

  1. Johnson法则-流水作业调度-动态规划
  2. 华为移动wifi显示无服务器,华为移动wifi设置方法
  3. android 雷达扫描动画,Android编程简单实现雷达扫描效果
  4. 关于Ant Design
  5. matlab去除图片水印_一种基于MATLAB去图片水印方法
  6. 免费真实增加网站访问量的方法
  7. CMake file命令参数GLOB和GLOB_RECURSE
  8. Apache Spark 不过时的六大理由
  9. 不管怎么换相,电机始终反转 。这是怎么回事?
  10. jquery datatable 前端分页和后端分页例子