Node.js零基础自学(持续更新中)

1. Node.js时基于Chrome V8 引擎的JavaScript运行环境。官网:Node.jsNode.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.https://nodejs.org/en/

 2. Node.js中的Javascript的运行环境

nodejs的介绍使用+npm的作用+淘宝镜像源+本地服务器_超人不会飞啊的博客-CSDN博客

注意:1.浏览器时javascript的前端运行环境

            2.Node.js是JavaScript的后端运行环境

            3.Node.js中无法调用DOM和BOM等浏览器内置API

3.Node.js可以做什么

Node.js 作为一个javascript的运行环境,仅仅提供了基础的功能和API。

1)基于Express框架的Express - 基于 Node.js 平台的 web 应用开发框架 - Express 中文文档 | Express 中文网,可以快速构建web应用

2)基于Electrom框架 Electron | Build cross-platform desktop apps with JavaScript, HTML, and CSS.

3)基于 restify框架 Restify ,可以快速构建API接口项目

 4)读写和操作数据库,创建使用的命令行工具辅助前端开发 、etc..

 4.Node.js 怎么学

Node.js 学习路径

javascript基础语法+Node.js 内置API模块(fs,path,http等)+第三方API模块(express,mysql等)

5. Node.js安装 1)官网下载安装包 https://nodejs.org/en/  LTS(建议安装)为长期稳定版 CURRENT为尝鲜版本

                        2)建议安装在默认路径,(C盘)

                         3) 命令行 输入 node -v 查看node版本号

6.什么是终端: 终端(terminal)是专门为开发人员设计的,用于实现人机交互的一种方式。

作为一名合格的程序员,我们有必要认识和脊柱一些常用的终端命令,来辅助我们更好的操作与使用计算机。

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

1)打开终端

2)输入node 要执行的js文件的路径 (要切换到文件所处的目录当中)

3)在文件 所处位置 按住shift + 右键  打开powershell

       

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

例如:

fs.readFile()方法,同来读取指定的文件中的内容 

fs.writefile()方法,用来向指定的文件中写入内容

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

const fs = require('fs')

9. fs.readfile()语法格式

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

                 path, callback可选参数  options

path:必选参数,字符串,表示文件的路径

options:可选参数,表示以什么编码格式来读取

callback:必选参数,文件读取完成后,通过回调函数拿到读取的结果

例如:

//2. 带哦用fs.readFiles()方法读取文件

//参数1:读取文件存放路径

//参数2:读取文件时候采用的编码格式,一般默认指定 utf8

fs.readFile("./files/11.txt", "utf8", function (err, dataStr) {

  //2.1打印失败的结果

  //如果读取成功 则err的值为null

  //如果读取失败 则err的值为错误对象 dataStr的值为 undefined

  console.log(err);

  console.log("------------");

  //2.2打印成功的结果

  console.log(dataStr);

});

10.判断文件是否读取成功

例如:

const fs = require("fs");

fs.readFile("./files/11.txt", "utf8", function (err, dataStr) {

  if (err) {

    return console.log("文件读取失败" + err.message);

  }

  console.log("读取文件成功" + dataStr);

});

11. 向指定文件中写入内容

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

fs.writeFile(file,data[,options], callback)

参数1:必选参数,需要指定一个文件路径的字符串,表示文件的存放路径。

参数2:必选参数,表示要写入的北荣。

参数3:可选参数,表示以什么格式写入内容,默认值是utf8

参数4:必选参数,文件写入完成后的回调函数

 const fs = require('fs')

fs.writeFile('./files/2.txt', 'hello Node.js!' , function(err) {

console.log(err)

})

12. 判断文件是否写入成功 

const fs = require("fs");

fs.writeFile("F:/files/2.txt", "hello node.js", function (err) {

  if (err) {

    return console.log("文件写入失败" + err.message);

  }

  console.log('文件写入成功')

});

13. 整理成绩案例 (见文件包)

14.路径拼接出错误问题

在使用fs模块操作文件时,如果提供的操作路径是以./ 或../的相对路径时,很容易出现路径动态拼接错误的问题。

原因:代码在运行时,会以执行node命令时所处的目录,动态拼接储备操作文件的完整路径。

const fs = require("fs");

fs.readFile("./files/1.txt", "utf8", function (err, dataStr) {

  if (err) {

    return console.log("读取文件失败" + err.message);

  }

  console.log("scucess" + dataStr);

});

15.提供一个完整的绝对路径(复制完路径后,加一个\) 缺点:移植性差,不利于维护

16. 加__dirname

fs.readFile(__dirname + "/files/1.txt", "utf8", function (err, dataStr) {

  if (err) {

    return console.log("读取文件失败" + err.message);

  }

  console.log("scucess" + dataStr);

});

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

path.join()方法,同来将多个路径偏暖拼接成一个完整的路径字符串

path.basename()方法,用来从路径字符串中,将文件名解析出来

const path = require('path')

18.路径拼接

path.join()的语法格式

path.join([..path])

..path <string>路径片段

返回值<string>

今后凡是涉及到路径拼接的操作,都要用path.join()方法进行处理。不要直接用+ 进行字符串拼接

19.

1)获取路径中的文件名

path.basename()的语法格式

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

path.basename(path[, ext])

path<string>必选参数,表示一个路径的字符串

ext<string> 可选参数,表示文件扩展名

返回值:<string>表示路径的最后一个部分

获取文件名 1. 有扩展名 2. 没有扩展名

2).获取文件扩展名 

path.extname()

20.时钟案例 (见文件)

注意点1.fs.writeFile()只能用于创建文件,不能用于创建路径

           2.重复调用fs.writeFile()写入同一文件,新写入的内容会覆盖之前的内容

21.什么是http模块

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

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

22.http模块的作用

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

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

23.服务器相关概念

1).IP地址就是互联网上每台计算机的唯一地址,因此IP地址具有唯一性。如果把“个人电脑”比作“一台电话”,那么"IP地址”就相当于“电话号码”, 只有在知道对方IP地址的前提下,才能与对印度个电脑之间进行数据通信。

2).域名和域名服务器

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

IP和域名时一一对应的关系。这份对应关系存放在一种叫做域名服务器的电脑中。

3). 端口号

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

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

注意点

1.每个端口号不能同时被多个web服务占用。

2.在实际应用中,URL中的80端口可以被省略。

24.创建最基本的web服务器

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

1). 导入http模块

const http = require('http')

2).创建web服务器实例

调用http.createServer()方法,既可以快速创建一个web服务器实例

const server = http.createServer()

3).为服务器绑定request事件,监听客户端的请求

4).启动服务器

调用服务器实例的 .listen()方法,既可以启动当前的web服务器实例

        

 25. req请求对象

只要服务器接收到了客户端的请求,就会通过调用server.on()为服务器绑定的request事件处理函数。

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

 26. res响应对象

通过res.end向客户端发送指定内容

 27. 解决中文乱码问题

28.个面具不同url 相应不同的html内容

1). 获取请求的url地址

2). 设置默认的响应内容

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

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

5). 设置Content-Type 响应头,防止中文乱码

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

 29.时钟web服务器案例

1.核心思路如下:

Node——时钟web服务器案例_Zhangzilnn的博客-CSDN博客

 2.实现步骤

1). 导入需要的模块

2).创建基本的web服务器

3).将资源的请求url地址映射为文件的从存放路径

4).读取文件内容并相应给客户端

5).优化资源的请求路a径

30. 模块化的概念

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

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

1、提高代码的复用性

2、提高了代码的可维护性

3、可以实现按需加载

2)模块化规范

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

例如:

使用什么样的语法来引用模块

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

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

31.Node.js中模块的分类

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

1).内置模块:是由Node.js官方提供的,例如fs,path,http等

2).自定义模块: 用户创建的每一个.js文件,都是自定义模块

3).第三方模块:由第三方开发出来的模块,并非官方提供的内置模块,也不是用户创建的自定义模块,使用前需要先下载

32.加载模块

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

33. 什么是模块作用域

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

模块作用域的的好处,防止了全局变量污染的问题

34.向外共享模块作用域中的成员

1.module对象

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

35.modeule.exports对象

在自定义模块中,可以使用module.exports对象,将模块内的成员共享出去,共外界使用。

外界用require()方法导入自定义模块时,得到的就是module.exports所指向的对象。

36.共享成员时的注意点

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

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

 38. exports 和 module.exports的使用误区

时刻谨记, require() 模块时,得到的永远时module.exports只想的对象

39.Node.js中的模块化规范

Node.js遵循了CommonJS模块化规范,CommonJS规定了模块的特征和个模块之间的互相依赖。

CommonJS规定

1). 每个模块内部,module变量代表当前模块

2). module 变量是一个对象,它的exports属性(即module.exports)是对外的接口

3). 加载某个模块,其实时加载该模块的module.exports属性。require()方法用于加载模块

 40.包与npm-包的概念

1).什么是包

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

第三方模块和包值得时同一个概念,只不过叫法不同。

2).包的来源

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

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

3). 为什么需要包

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

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

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

4).从哪里下载包

国外有一家IT公司,叫做npm,Inc.这家公司旗下有一个非常著名的网站:http://www.npmjs.com/

,他是全球最大的包共享平台,你可以从这个网站上搜索任何你需要的包,只要你有足够的耐心。

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

npm,Inc.这家公司提供了一个地址为https://registry.npmjs.org/的服务器,来对外共享所有的包,我们可以通过这个服务器山刚下载自己所需要的包。

5). 如何下载包

npm,Inc.这家公司提供了一个包管理工具,我们可以使用这个包管理工具,从https://registry.npmjs.org/服务器把需要的包下载到本地使用。

这个包管理工具的名字叫做  Node Package Manager (简称npm 包管理工具), 这个包管理工具随着Node.js的安装包一起被安装到了用户的电脑上。

41.npm初体验 

1). 格式化时间的传统做法 见文件js的15、16

2). 格式化时间的高级做法 1.使用npm包管理工具,在项目中 安装格式化时间的包 moment

                                          2.使用require()导入格式化时间的包

                                          3.参考 moment的官方API文档对事件进行格式化

42.在项目中安装包的命令

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

npm install 包的完整名称(简写 npm i)

43. 初次装包后多了哪些文件

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

其中:

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

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

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

44. 安装指定版本的包

默认情况下,使用npm install命令安装包的时候,会自动安装最新版本的包。如果需要安装指定版本的包,可以在包名之后,通过@符号指定具体的版本,例如:

npm i moment@2.22.2

45. 包的语义化版本规范

包的版本号是以“点分十进制” 形式进行定义的,总共有三位数字。例如2.24.0

其中每一位数字所代表的含义如下:

1位数字:大版本

2位数字:功能版本

3位数字:Bug修复版本

版本号提升的规则:只要前面的版本号增长了,则后面的版本号归零

46.包管理配置文件

npm规定,在项目中,必须提供一个叫做package.json的包管理配置文件。用来记录与项目有关的一些配置信息, 例如:

项目的名称、版本号、描述等

项目中都用到了哪些包

哪些包只在开发期间会用到

哪些包只在开发和部署时都需要用到

47. 多人协作的问题

第三方包的体积过大, 不方便团队成员共享项目的源代码

48. 如何记录项目中都用了哪些包

在项目根目录中,创建一个叫做package.json的配置文件, 既可用来记录项目中安装了哪些包。 从而方便剔除node_modules 目录之后, 在团队成员之间共享项目源代码。

注意:今后在项目开发项目中,一定要把node_modules文件夹, 添加到.gitinore文件中

49.快速创建package.json 

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

npm init -y

1.上述命令只能在英文的目录下成功运行! 所以,项目文件夹的名称一定要使用英文命名,不要使用中文, 不能出现空格

2.运行 npm install 命令安装包的时候, npm包管理工具会自动把包的名称和版本号,记录package.json中

50. dependencies节点

package.json文件夹中, 有一个dependencies节点, 专门用来记录您使用npm install命令安装额哪些包

 51. 一次性的安装所有的包

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

当我们拿到一个剔除了node_modules的项目后,需要先把所有的包下载到项目中,才能将项目运行起来。

否则会报类似于下面的错误:

Error: Cannot find module 'moment'

52.卸载包 

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

53.devDependencies节点

如果某些包只在项目开发阶段会用到,在项目上线之后不会用到,则建议把这些包记录到devDependencies节点中。代码: npm i 包名 -D

如果某些包在开发和项目上线之后都会用到,则建议把这些包记录到dependencies节点中。

54.解决下包速度慢的问题

在使用npm下包的时候,默认从国外的http://registry.npmjs.org/服务器进行下载,此时,网络数据的传输需要经过漫长的海底光缆,因此下包速度会很慢。

55.淘宝npm镜像服务器

淘宝在国内搭建了一个服务器,专门把国外官方服务器上的包同步到国内的服务器,然后再国内提供下包的服务。从而极大的提高了下包的速度。

镜像是一种文件存储的形式,一个磁盘上的数据在另一个磁盘上存在一个完全相同的副本即为镜像。

56.切换npm的下包的镜像源

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

查看当前下包镜像源

npm  config get registry

切换为淘宝的镜像源

npm config get registry=http://registry.npm.raobao.org/

检查镜像源是否下载成功

npm config get registry

57. nrm 

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

通过npm的包管理器, 将nrm安装为全局可用的工具

npm i nrm -g

查看所有可用的镜像源

nrm ls

将下包的镜像源切换为taobao 镜像

nrm use taobao

58.包的分类

使用npm包管理工具下载的包, 共分为两大类,分别是:

1).项目包: 那些被安装到项目的node_module目录中的包,都是项目包。

项目包又分为两类,分别是:

开发依赖包(被记录到devDependencies 节点中的包, 旨在开发期间会用到)

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

2).全局包 :在执行npm install 命令时,如果提供了 -g参数, 则会把包安装为全局包。

全局包会被安装到C:\Users\用户目录\AppleData\Roaming\npm\node_modules目录下。

npm i 包名 -g 全局安装指定的包

npm uninstall 包名 -g 卸载全局安装的包

注意:

只有工具性质的包,才有全局安装的必要性。 因为它们提供了好用的终端命令

判断某个包是否需要全局安装后才能使用, 可以参考官方提供的使用说明即可

3). i5ting_toc

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

将i5ting_toc 安装为全局包

npm install -g i5ting_toc

调用i5ting_toc 轻松实现md 转html的功能

i5ting_toc -f 要转换的md文件路径 -o

4). 规范的包结构

一个规范的包,它的结构组成,必须符合一下3点要求:

1).包必须以单独的目录存在

2).包的顶级目录下必须要包含package.json这个包管理配置文件

3). package.json中必须包含name, version, main 这三个属性,分别代表包的名字、版本号、包的入口。

59.开发属于自己的包

1.需要实现的功能

2.初始化包的基本结构

1).新建itheima-tools文件夹,作为包的根目录

2).在itheima-tools文件夹中,新建如下三个文件:

package.json

index.js

README.md

3.初始化package.json

60. 在index.js中定义格式化时间的方法

61.在index.js中定义还原HTML的方法

 

62.将不同功能进行模块化拆分 (见文件 itheima-tools)

1).将格式化时间的功能,拆分到 src ->dateFormat中

2).讲处理HTML字符串的功能,拆分到src->htmlEscape.js中

3).在index.js中, 导入两个模块,得到需要向外共享的方法

4).在index.js中,使用module,exports把对应的方法共享出去

63.编写包的说明文档

包的根目录中的README.md文件,时包的使用说明文档。

安装方式、导入方式、格式化时间、转移HTML中的特殊字符、还原HTML中的特殊字符、开源协议

64.发布包

1).创建npm账号

2).登录npm账号 (在终端)npm login

3).把包发布到npm上:将终端切换到包的目录之后,运行npm publish命令,即可将包发布到npm上(注意:包名不能雷同)。

4).删除一发布的包 运行 npm unpublish 包名 --force 命令,即可从npm删除已发布的包

65.模块的加载机制

1).模块在第一次加载后会被缓存。这也意味着多次调用require()不会导致 模块的代码被执行多次。

2).内置模块的加载机制

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

例如,require('fs')始终返回内置的fs模块,既是在node_modules目录下有名字相同的包也叫fs。

3).自定义模块的加载机制

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

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

1.按照确切的文件名进行加载

2.补全.js扩展名进行加载

3.补全.json扩展名进行加载

4.补全.node扩展名进行加载

4).第三方模块的加载机制

如果传递给require()的模块不是一个内置模块, 也没有以 ./ 或者../开头,则node.js会从当前模块的夫目录开始,尝试从/node_modules文件夹中加载第三方模块。

例如,假设在 'C:\Users\itheima\project\foo.js’ 文件里调用了require('tool'),则node.js会按以下顺序查找:

1).C:\Users\itheima\project\node_modules\tools

2).C:\Users\itheima\node_modules\tools

3).C:\Users\node_modules\tools

4).C:\node_modules\tools

5).目录作为模块

当把目录作为模块标识符,传递给require()进行加载时,由三种加载方式:

1).json文件里的 main 属性指向的文件

2). index

66.express简介

1). 什么是express

官方给出概念:express是基于node.js平台,快速、开放、极简的web开发框架。

通俗的理解,express的作用和node.js内置的http模块类似,都是用来创建web服务器的。

express的本质:就是一个npm里的第三方的包,提供了快速创建web服务器的快捷方法

express中文官网:http://www.expressjs.com.cn/

2).进一步理解express

思考:不适用express能否创建web服务器

答案:能,使用node.js提供的原生http模块即可

思考:既生瑜何生亮(有了http模块,为何还要用express)

答案:http内置模块用起来很复杂,开发效率低;express是基于http模块进一步封装出来的,能够极大的提高开发效率

思考:http内置模块于express是什么关系

答案:类似于web api 和 jQuery的关系。后者是基于前者进一步封装出来的。

3).express能做什么

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

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

api接口服务器:专门对外提供api接口的服务器

使用express,我们可以方便、快速的创建web 网站服务器或api接口的服务器

67.express的基本使用

1).安装

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

npm i express@4.17.1

2).创建基本的web服务器

68.express监听GET和POST请求&参数

1). 监听get请求

通过app.get()方法

//参数1: 客户段请求 URL地址

//参数2: 请求对应的处理函数

req:请求对象

res:响应对象

app.get('请求URL',function(req, res) {/*处理函数*/})

2). 监听post请求

通过app.post()方法

//参数1: 客户段请求 URL地址

//参数2: 请求对应的处理函数

req:请求对象

res:响应对象

app.post('请求URL',function(req, res) {/*处理函数*/})

3).把内容相应给客户端

通过res.send()方法

69.获取URL中携带的查询参数

通过req.query对象

 70.获取URL中的动态参数

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

 可以有连个动态参数

 71.express静态资源处理

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

app.use(express.static('public'))

现在你就可以访问public目录中所有的文件了:

http://localhost:3000/images/bg.jpg

http://localhost:3000/css/style.css

http://localhost:3000/js/login.js

注意:express在指定的静态目录中查找文件,并对外提供资源的访问路径。

因此,存放静态文件的目录名不会出现咋URL中。

2).托管多个静态资源目录

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

3). 挂在路径前缀

app.use('/public' , express.static('public')) 

http://localhost:3000/public/images/bg.jpg

http://localhost:3000/public/css/style.css

http://localhost:3000/public/js/login.js

72.nodemon

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

2).安装nodemon

npm install -g nodemon

3).使用nodemon

node app.js

nodemon app.js (npx nodemon app.js)

73.express路由 - 路由的概念

1.什么是路由

广义来讲,路由就是按键与服务之间的映射关系。

express中的路由:

在express,路由指的是客户端请求与服务器处理函数之间的映射关系。

express中的路由由三部分组成,分别是请求的类型,请求的URL地址、处理函数、格式如下:

app.METHOD(path, handler)

express中的路由的例子

//匹配 GET请求,且请求URL为/

app.get('/', function(req, res) { 

res.send('hello world')

}

//匹配 POST请求,且请求URL为/

app.post('/', function(req, res) { 

res.send('Got a POST request')

}

2.路由的匹配过程

每当一个请求到达服务器后,需要先经过路由的匹配, 只有匹配成功之后,才会调用对应的处理函数。

在匹配时,会按照路由的顺序进行匹配,如果请求类型请求URL同时匹配成功,则express会将这次请求,转叫给对应的function函数进行处理

 路由匹配的注意点:

1).按照定义的先后顺序进行匹配

2).请求类型和请求URL同时匹配成功。蔡虎调用对应的处理函数。

3.模块化路由

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

1).创建路由模块对应的.js文件

2).调用express.Route()函数创建路由对象

3).向路由对象挂在具体的路由

4).使用module.exports向外共享路由对象

5).使用app.use()函数注册路由模块

 4.注册路由模块

 5.为路由模块添加前缀

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

74.中间件middleware

中间件的概念与格式 

 1.中间件特指业务流程中处理环节。中间件一般都有输入与输出,上一级的输出作为下一级的输入

2.express中间件的调用流程

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

3.express中间件的格式

express的中间件,本质上就是一个function处理函数,express中间件的格式如下

注意:中间件 函数的形参列表中,必须包含next参数。而路由处理函数中只包含req和res

4.next函数的作用

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

75. 中间件-全局生效的中间件

1).定义中间件函数

2).全局生效的中间件

客户端发起的任何请求,到达服务器之后,都会触发的中间件,叫做全局生效的中间件

通过调用app.use(中间件函数),即可定义一个全局生效的中间件,

 

3).定义全局中间件的简化形式 

4).中间件的作用

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

5). 定义多个全局中间件

76.局部生效的中间件

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

 2).定义多个局部的中间件

77.中间件- 5个注意事项

1).一定要在路由之前注册中间件

2).客户端发送过来的请求,可以连续调用多个中间件进行处理

3).执行完中间件的业务代码之后,不要忘记调用next()函数

4).为了防止代码逻辑的混乱,调用next()函数之后不要再写额外的代码了

5).连续调用多个中间件时,多个中间件之间,共享req和res对象

78.中间件的分类

1).应用级别的中间件

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

代码示例如下:

//应用级别的中间件(全局中间件)
app.use((req, res, next)=> {
   next()
})

//应用级别的中间件(局部中间件)
app.get('/', mw1, (req,res)=> {
   res.send('home page')
})

2).路由级别的中间件

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

const app = express()
const router = express.Router()


//路由级别的中间件
router.use(function(req, res, next) {
   console.log('Time:' + Datenow())
   next()
})

app.use('/', router)

3). 错误级别的中间件

错误级别的中间件的作用:专门用来捕获整个项目中发生的错误,从而防止项目异常的崩溃问题。

格式:错误级别中间件的function处理函数中,必须有4个形参,形参顺序从前到后,分别是(err,req,res,next)

app.get('/', function(req,res) {          //1.路由
 throw new Error('服务器内部发生了错误!')   //1.1抛出一个自定义的错误
 res.send('Home page')
})

app.use(function(err,req,res,next) {      //2.错误级别的中间件
  console.log('发生了错误' + err.message)  //2.1 在服务器打印错误消息
  res.send('Error!' + err.message)        //2.2向客户端相应错误相关的内容
})

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

4).Express内置的中间件

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

  1. express.static快速托管静态资源的内置中间件,例如HTML文件,图片、css等 (无兼容性)
  2. express.json 解析json格式的请求体数据(有兼容性,仅在4.16.0+版本中可用)
  3. express.urlencoded解析URL-encoded格式的请求体数据(有兼容性,仅在4.16.0+版本中可用)
    //配置解析 application/json格式数据的内置中间件
    app.use(express.json())
    
    //配置解析 application/x-www-form-urlencoded 格式数据的内置中间件
    app.use(express.urlencoded({extended:false}))

     

5).第三方中间件

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

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

1).运行npm install body-parser安装中间件

2).使用require导入中间件

3).调用app.use()注册并使用中间件

79. 自定义中间件

1.需求描述与实现步骤

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

实现步骤:

  1. 定义中间件 
  2. 监听req的data事件(在中间件中,需要监听req对象的data事件,来获取客户端发送到服务器的数据。 如果数据量比较大,无法一次型发送完毕,则客户端会把数据切割后,分批发送到服务器。 所以data事件可能会被触发多次,每一次触发data事件时, 获取到数据只是完整数据的一部分, 需要手动对接收到的数据进行拼接。)
  3. 监听req的end事件
  4. 使用querystring模块请求体的数据 
  5. 将解析出来的数据对象挂在为req.body
  6. 将自定义中间件封装为模块

80. 将自定义中间件封装为模块

(见Code2 的13 和14) 

81.编写接口-创建基本的服务器创建API路由模块

 

82编写GET接口 (见 code2 15 和16 )

83 编写post接口  

84.编写接口——基于cors解决接口跨域的问题 (见Code2 的17)

Staticfile CDN

刚才编写的GET和POST接口, 存在一个很严重的问题: 不支持跨域请求。

解决跨域问题的方法主要有两种:

1).CORS(主流的解决方案,推荐使用)

2).JSONP(有缺陷的解决方法:支支持GET请求)

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

cors时express的一个第三方中间件。通过安装和配置cors中间件,可以很方便的解决跨域问题。

使用步骤分为以下三步:

1).运行 npm install cors安装中间件

2).使用const cors = require('cors') 导入中间件

3).在路由之前调用app.use(cors())配置中间件

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

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

CORS的注意事项

1).CORS主要在服务器端进行配置。 客户端浏览器无需做任何额外的配置,即可请求开启了CORS的接口。

2).CORS在浏览器中有兼容性。 只支持XMLHttpRequest Level2的浏览器, 才能正常访问开启了CORS的服务器端接口(例如IE10+,chrome4+, FireFox3.5+)

86. 跨域-cors相关的三个响应头

1).CORS 响应头部 Access-Control-Allow-Origin

 2).CORS 响应头部 Access-Control-Allow-Headers

3).CORS 响应头部 Access-Control-Allow-Methods 

 

 87.跨域-cors的简单请求与预检测请求

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

1).简单请求:1.请求方式:GET、POST、HEAD三者之一

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

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

1)。请求方式为GET、POST、HEAD之外的请求Method类型

2).请求头中包含自定义头部字段

3).向服务器发送了 application/json格式的数据

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

简单请求与预检请求的区别

简单请求的特点:客户端与服务器之间之会发生一次请求。

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

88.跨域 编写jsonp接口

概念:浏览器通过<script>标签的src属性, 请求服务器上的数据,同时, 服务器返回一个函数的调用。这种请求数据的方式叫做jsonp

特点:

1).jsonp不属于真正的ajax请求, 因为他没有使用XMLHttpRequest这个对象

2).jsonp 仅支持GET请求, 不支持post、put、delete等请求

创建jsonp接口的注意事项:

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

 

实现jsonp 接口的步骤

1).获取客户端发送过来的回调函数的名字

2).得到要通过jsonp形式发送给客户端的数据

3).根据前两部得到的数据,拼接出一个函数调用的字符串

4).把上一步拼接的道德字符串, 响应给客户端<script>标签进行解析执行 

 

 89.数据库的基本概念

1). 什么是数据库

数据库database 是用来组织、存储和管理数据的仓库。

为了方便管理互联网世界中的数据, 就有了数据库管理系统的概念。 用户可以对数据库中的数据进行新增、查询、更新、删除等操作。

90.安装mysql

mysql server:专门用来童工数据存储和服务器的软件

mysql workbench:可视化的mysql 管理工具, 通过它, 可以方便的操作存储在mysql server的数据

91mysql-workbench的基本用法

1).连接数据库(登录mysql)

2).了解主界面的组成部分

3).创建数据库

4).创建数据表

DataType

1).int 整数

2).varchar字符串

3).tinyint布尔值

5).设置字段的特殊标识

PK primary key 主键唯一标识

NN Not Null 值不许为空

UQ Unique 值唯一

AI auto increament 值自动增长

6).向表中写入数据

91.了解是什么sql 

SQL (Structured query language) 时结构化的查询语言, 专门用来访问和处理数据库的编程语言。 能够让我们以编程的形式, 操作数据库里面的数据。

三个关键点:

1).SQL时一门数据库编程语言

2). 使用SQL语言编写出来的代码, 叫做SQL语句

3).SQL语言只能在关系型数据库中使用(例如MySql、Oracle、SQL Server)。 非关系型数据库(例如mongodb)不支持SQL语言

SQL能做什么

1).从数据库中查询数据

2).先数据库中插入新的数据

3).更新数据库中的数据

4).从数据库删除数据

SQL的学习目标

重点掌握如何使用SQL从数据表中

查询数据(select)、插入数据(insert into)、更新数据(update)、删除数据(delete)

额外需要掌握的4种SQL语法

where条件、 and 和or 运算符、order by 排序、 count(*)函数

92.SQL语句-select insert update delete

SQL的select语句

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

语法格式如下:

--这是注释
--从FROM指定的【表中】,查询【所有的】数据。 *表示【所有列】
SELECT * FROM 表名称

--从FROM指定的【表中】,查询指定的 列名称(字段)的数据
SELECT 列名称 FROM 表名称

SELECT *示例

SQL的INSERT INTO 语句

1.语法

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

--语法解读:向指定的表中,插入如下几列数据, 列的值通过 values- 指定
--注意:列和值要--对应,多个列和多个值之间。使用英文的逗号分隔
INSERT INTO table_name(列1,列2,...) VALUES(值1,值2,...)

SQL的UPDATE语句

--语法解读
--1.用UPDATE指定要更新那个表中的数据
--2.用SET指定对应的新值
--3.用WHERE指定更新的条件
UPDATE 便名称 SET 列名称= 新值 WHERE 列名称 = 某值

SQL的DELETE

--语法解读
--从指定的表中, 根据WHERE条件, 删除对应的数据行
DELETE FROM 表名称 WHERE 列名称=值

92.SQL语句-where子句、and和or运算符

-- 查询剧中的WHERE条件
SELECT 列名称 FROM 表名称 WHERE 列 运算符 值
-- 更新语句中的 WHERE 条件
UPDATE 表名称 SET 列= 新值  WHERE 列 运算符 值
-- 删除语句中的 WHERE 条件
DELETE FROM 表名称 WHERE 列 运算符 值

1).通过WHERE子句来限定SELECT的查询条件

-- 查询status 为 1 的所有用户
SELECT * FROM users WHERE status=1
-- 查询 id 大于 2 的所有用户
SELECT * FROM users WHERE id>2
--查询 username 不等于 admin的所有用户
SELECT * FROM users WHERE username<>'admin

2). 

AND和OR可在WHERE语句中 把两个或多个条件结合起来

AND表示必须同时满足多个条件,相当于javascript中的&&运算符, 例如if(a!=10 && a!= 20)

OR表示只要满足任意一个条件即可, 相当于JavaScript中的 ||运算符,例如if (a!==10 && a!==20)

实例

-- 使用 AND 来显示所有status为0, 并且id小于3的用户
SELECT * FROM users WHERE status=0 AND id<3
-- 使用 OR 来显示所有status 为1, 或者username 为zs 的用户
SELECT * FROM users WHERE status=1 OR username='zs'

93.SQL语句-排序

1).SQL的 ORDER BY 语句

ORDER BY 语句用于根据指定的列对结果集进行排序。

ORDER BY 语句默认按照升序对记录进行排序

如果你希望按照降序对记录进行排序, 可以使用的DESC关键字

升序排序:

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

SELECT * FROM users ORDER BY status
SELECT * FROM users ORDER BY status ASC
-- 这两句子等价

降序排序:

SELECT * FROM users ORDER BY id DESC

多重排序:

对users表中的数据,先按照status进行降序排序, 再按照username的字母顺序进行升序排序,

示例如下:

SELECT * FROM users ORDER BY status DESC, username ASC

94SQL语句-count函数和as关键字

1).SQL的COUNT(*)函数

语法:

SELECT COUNT(*) FROM 表名称
SELECT COUNT(*) FROM users WHERE status=0

 2). SQL的 COUNT(*)函数

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

SELECT COUNT(*) AS total FROM users WHERE status=0

95.mysql模块——安装并配置MySQL模块

在项目中操作mysql的步骤

1).安装操作MySQL数据库的第三方模块(mysql)

2).通过mysql模块连接到MySQL数据库

3).通过mysql模块执行SQL语句

1.安装mysql模块

mysql模块是托管于npm上的第三方模块。 它提供了在node.js项目中来凝结和操作MySQL数据库的能力。

要想在项目中使用它,需要先运行如下命令, 将mysql安装为项目的依赖包:

npm install mysql

2.配置mysql模块

//1. 导入mysql数据库
const mysql = require("mysql");

//2.建立于 MySQL 数据库的连接关系
const db = mysql.createPool({
  host: "127.0.0.1", // 数据库的 ip 地址
  user: "root", //登录数据库的账号
  password: "123456", // 登录数据库的密码
  database: "my_db_01", //指定要操作那个数据库
});

 3.测试mysql模块能否正常工作

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

//测试mysql模块能否正常工作
db.query("select 1", (err, results) => {
  //mysql模块工作期间报错了
  if (err) return console.log(err.message);
  //能够成功执行SQL语句
  console.log(results);
});

95.mysql模块- 查询和插入数据

1). 查询数据

查询users表中的所有数据:

//查询users表中的所有数据
const sqlStr = "select * from users";
db.query(sqlStr, (err, results) => {
  //查询数据失败
  if (err) return console.log(err.message);
  //查询数据成功
  //注意 如果执行的时select 查询语句, 则执行的结果是数组
  console.log(results);
});

2).插入数据

向users表里新增数据,其中username 为Spider-man, password为pcc321. 示例代码如下:

//向users 表中 其中username 为Spider-man, password为pcc321
const user = { username: "Spider-man", password: "pcc321" };
//定义待执行的SQL 语句
const sqlStr = "insert into users (username, password) values(?,?)";
//执行SQL语句
ab.query(sqlStr, [user.username, user.password], (err, results) => {
  if (err) return console.log(err.message);
  //成功了
  //如果执行的是insert into 这个语句 则results 是一个对象
  //可以通过affectedRows属性, 来判断是否插入数据成功
  if (results.affectedRows === 1) {
    console.log("插入数据成功!");
  }
});

3).插入数据的便捷方式

//向users 表中 其中username 为Spider-man, password为pcc321
const user = { username: "Spider-man", password: "pcc321" };
//定义待执行的SQL 语句
const sqlStr = "insert into users set ?";
//执行SQL语句
db.query(sqlStr, user, (err, results) => {
  if (err) return console.log(err.message);
  //成功了
  //如果执行的是insert into 这个语句 则results 是一个对象
  //可以通过affectedRows属性, 来判断是否插入数据成功
  if (results.affectedRows === 1) {
    console.log("插入数据成功!");
  }
});

96.mysql模块-更新和删除数据

1).更新数据

//演示如何更新用户的信息
const user = { id: 7, username: "aaa", password: "000" };
//定义SQL语句
const sqlStr = "update users set username=?, password=? where id=?";
//执行SQL语句
db.query(sqlStr, [user.username, user.password, user.id], (err, results) => {
  if (err) return console.log(err.message);
  //注意 执行可update语句之后,执行的结果 也是一个对象,可以通过affectRows 判断是否更新成功
  if (results.affectedRows === 1) {
    console.log("更新成功");
  }
});

2).更新数据的快捷方式

//演示更新数据的便捷方式
const user = { id: 7, username: "aaaa", password: "0000" };
//定义SQL语句
const sqlStr = "update users set ? where id=?";
//执行SQL语句
db.query(sqlStr, [user, user.id], (err, results) => {
  if (err) return console.log(err.message);
  if (results.affectedRows === 1) {
    console.log("更新成功");
  }
});

3).删除数据

推荐通过id这样的唯一标识,来删除对应的数据

//删除id 为7的用户
const sqlStr = "delete from users where id =?";
db.query(sqlStr, 7, (err, results) => {
  if (err) return console.log(err.message);
  //注意执行delete 语句之后 结果也是一个对象 也会包含affectedRows 属性
  if (results.affectedRows === 1) {
    console.log("删除数据成功");
  }
});

4).标记删除

实例: 就是把 status 改为1

//标记删除
const sqlStr = "update users set status=? where id=?";
db.query(sqlStr, [1, 8], (err, results) => {
  if (err) return console.log(err.message);
  if (results.affectedRows === 1) {
    console.log("标记删除成功");
  }
});

97.web开发模式

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

1).记忆服务器渲染的传统web开发模式

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

app.get('/index.html', (req, res)=> {
//1.渲染的数据
const user = {name: 'zs', age: 20}
//2.服务器通过字符串的拼接, 动态生成的HTML内容
const html = `<h1>姓名:${username}, 年龄:${user.age}</h1>`
//3.把生成的页面内容响应给客户端。 因此, 客户端拿到的是带有真是数据的HTML页面
res.send(html)
})

优点1:前端耗时少。因为服务器负责动态生成HTML内容, 浏览器只需要直接渲染页面即可。尤其是移动端,更省电。

优点2:有利于SEO。 因为服务器响应的是完整的HTML页面内容, 所以爬虫更容易爬取 获得信息,更有利于SEO

缺点1:占用服务器资源。即服务器完成HTML页面内容的拼接, 如果请求较多, 会对服务器造成一定压力。

缺点2:不利于前后端分离,开发效率低。使用服务器端渲染,则无法进行分工合作,尤其对于前端复杂度高的项目,不利于项目高效开发。

2).基于前后端分离的新型web开发模式

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

优点1:开发体验好。前端专注于UI页面的开发,后端专注于api的开发,且前端有更多的选择性。

优点2:用户体验号。ajax技术的广泛应用,极大提高了用户的体验, 可以轻松实现页面的布局刷新。

优点3.减轻了服务器的渲染压力。因为页面最终是在每个用户的浏览器中生成的。

缺点1:不利于SEO。因为完整的HTML页面需要在客户端动态拼接完成的,所以爬虫对无法爬取页面的有效信息。(解决方法:利用vue、react等前端框架的SSR(server side render)技术能够很好的解决SEO问题!)

如何选择web开发模式?

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

不如企业级的网站, 主要功能是展示而没有复杂的交互,并且徐娅良好的SEO支持,则这是我们就需要服务器端渲染

类如后台管理项目,交互性比较强,不需要SEO,那么就可以使用前后端分离的开发模式

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

98.身份认证的概念

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

*日常生活中的身份认证所处可见,,例如:高铁的验票乘车,手机的密码或指纹解锁, 支付宝或微信的支付密码等。

*在Web开发中,也涉及到用户的身份验证,例如:各大网站的手机验证码登录、邮箱密码登录、二维码登录等。

2). 为什么需要身份认证

身份认证的目的,是为了确认当前所声称为某种身份的用户,确实是所声称的用户。例如:你去找快递员取快递,你要怎么证明快递是你的。

3).不同开发模式下开发认证

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

1.服务器渲染推荐使用session认证机制

2.前后端分离推荐使用JWT认证机制

99. Session原理

1.HTTP协议的无状态性

了解HTTP的无状态性是进一步学习session认证机制的必要前提

HTTP协议的无状态性,指的是客户端的每次HTTP请求都是独立的, 连续多个请求之间没有直接的关系, 服务器不会主动保留每次HTTP请求的状态。

2.如何突破HTTP无状态的限制

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

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

3.什么是cookie

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

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

cookie的几大特性

1).自动发送

2).域名独立

3).过期时限

4) 4KB限制

4.cookie在身份认证中起的作用

客户端在第一次请求服务器的时候, 服务器通过响应头的形式, 向客户端发送一个身份认证的cookie, 客户端会自动将cookie保存在浏览器中。

随后,当客户端浏览器每次请求服务器的时候, 浏览器会自动将身份认证相关的cookie, 通过请求头的形式发送给服务器, 服务器即可验明客户端的身份。

5.cookie不具有安全性

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

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

6.提高身份认证的安全性

为了防止客户伪造会员卡,收银员在拿到客户出示的会员卡之后,可以在收银机上进行刷卡认证。只有收银机确认存在的会员卡,才能被正常使用。(这种会员卡+刷卡认证的设计理念,就是session认证机制的精髓所在)

7.session的工作原理

 100.session中间件的使用

1).安装express-session中间件

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

npm install express-session

2).配置express-session中间件

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

const session = require('express-session');
app.use(
  session({
    secret: "itheima", //secret属性的值可以为任意字符
    resave: false, //固定写法
    saveUninitialized: true, //固定写法
  })
);

3).向session中存数据

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

// 登录的 API 接口
app.post("/api/login", (req, res) => {
  // 判断用户提交的登录信息是否正确
  if (req.body.username !== "admin" || req.body.password !== "000000") {
    return res.send({ status: 1, msg: "登录失败" });
  }

  //todo_02: 请将登录成功后的用户信息,保存到session 中
  //express-session中间件配置成功后,可以通过req.session访问和使用session对象
  req.session.user = req.body; //用户的信息村到session中
  req.session.islogin = true; // 将用户的登录状态存储到session中
  res.send({ status: 0, msg: "登录成功" });
});

4). 从session中取数据

可以直接从req.session对像傻瓜获取之前存储的数据,

//4、从session中取数据
// 获取用户姓名的接口
app.get("/api/username", (req, res) => {
  //判断用户是否登录
  if (!req.session.islogin) {
    return res.send({ status: 1, msg: "fail" });
  }

  res.send({
    status: 0,
    msg: "success",
    username: req.session.username,
  });
});

5).清空session

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

//5、清空session
// 退出登录的接口
app.post("/api/logout", (req, res) => {
  //清空当前客户端对应的session信息
  req.session.destroy();
  res.send({
    status: 0,
    msg: "退出登陆成功",
  });
});

101.JWT 了解session认证的局限性

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

注意:

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

 什么是JWT

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

 

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

JWT 组成部分:
Header、Payload、Signature

  • Payload 是真正的用户信息,加密后的字符串
  • Header 和 Signature 是安全性相关部分,保证 Token 安全性
  • 三者使用 . 分隔
Header.Payload.Signature

JWT 使用方式:

  • 客户端会把 JWT 存储在 localStorage 或 sessionStorage 中
  • 此后客户端与服务端通信需要携带 JWT 进行身份认证,将 JWT 存在 HTTP 请求头 Authorization 字段中
  • 加上 Bearer 前缀
Authorization: Bearer <token>

 102.JWT-在express中生成token

1).安装JWT相关的包

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

npm install jsonwebtoken express-jwt

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

2).导入JWT相关的包

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

//1.导入用于生成JWT字符串的包
const jwt = require('jsonwebtoken')

//2.导入用于将客户端发送过来的JWT字符串, 解析还原成JSON对象的包
const expressJWT = require('express-jwt')

3).定义 secret 密钥

  • 为保证 JWT 字符串的安全性,防止其在网络传输过程中被破解,需定义用于加密和解密的 secret 密钥
  1. 生成 JWT 字符串时,使用密钥加密信息,得到加密好的 JWT 字符串
  2. 把 JWT 字符串解析还原成 JSON 对象时,使用密钥解密

4).在登陆成功后生成JWT字符串

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

app.post('/api/login', function (req, res) {
  // 将 req.body 请求体中的数据,转存为 userinfo 常量
  const userinfo = req.body
  // 登录失败
  if (userinfo.username !== 'admin' || userinfo.password !== '000000') {
    return res.send({
      status: 400,
      message: '登录失败!',
    })
  }
  // 登录成功
  // TODO_03:在登录成功之后,调用 jwt.sign() 方法生成 JWT 字符串。并通过 token 属性发送给客户端
  // 参数1:用户的信息对象
  // 参数2:加密的秘钥
  // 参数3:配置对象,可以配置当前 token 的有效期
  // 记住:千万不要把密码加密到 token 字符中
  const tokenStr = jwt.sign({ username: userinfo.username }, secretKey, { expiresIn: '30s' })
  res.send({
    status: 200,
    message: '登录成功!',
    token: tokenStr, // 要发送给客户端的 token 字符串
  })
})

5).将JWT字符串还原JSON对象

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

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

//使用app.use()来注册中间件
//expressJWT({secret:secretKey}) 就是用来解析Token的中间件
// .unless({path:[/^\/api\//]})用来指定哪些接口不需要权限
app.use(expressJWT({ secret: secretKey }).unless({ path: [/^\/api\//] }))

6).使用req.user获取用户信息

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

app.get('/admin/getinfo', function (req, res) {
  // TODO_05:使用 req.user 获取用户信息,并使用 data 属性将用户信息发送给客户端
  console.log(req.user)
  res.send({
    status: 200,
    message: '获取用户信息成功!',
    data: req.user, // 要发送给客户端的用户信息
  })
})

7).捕获解析JWT失败后产生的错误

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

// TODO_06:使用全局错误处理中间件,捕获解析 JWT 失败后产生的错误
app.use((err, req, res, next) => {
  // 这次错误是由 token 解析失败导致的
  if (err.name === 'UnauthorizedError') {
    return res.send({
      status: 401,
      message: '无效的token',
    })
  }
  res.send({
    status: 500,
    message: '未知的错误',
  })
})

102.项目 api server

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值