NodeJs基础教程
13.1 初认识NodeJS
13-1-1 什么是 Node.js
13-1-2 Node.js 中的 JavaScript 运行环境
13-1-3 Node.js 可以做什么
13.2 fs 文件系统模块
13-2-1 什么是 fs 文件系统模块
fs 模块是 Node.js 官方提供的、用来操作文件的模块。它提供了一系列的方法和属性,用来满足用户对文件的操作需求。
例如:
⚫ fs.readFile() 方法,用来读取指定文件中的内容
⚫ fs.writeFile() 方法,用来向指定的文件中写入内容
如果要在 JavaScript 代码中,使用 fs 模块来操作文件,则需要使用如下的方式先导入它:
13-2-2 读取指定文件中的内容
2-1 fs.readFile() 的语法格式
2-2 fs.readFile() 的示例代码
以 utf8 的编码格式,读取指定文件的内容,并打印 err 和 dataStr 的值:
2-3 判断文件是否读取成功
可以判断 err 对象是否为 null,从而知晓文件读取的结果:
13-2-3 向指定的文件中写入内容
3-1 s.writeFile() 的语法格式
使用 fs.writeFile() 方法,可以向指定的文件中写入内容,语法格式如下:
3-2 fs.writeFile() 的示例代码
3-3 判断文件是否写入成功
13-2-4 fs 模块 - 路径动态拼接的问题
在使用 fs 模块操作文件时,如果提供的操作路径是以 ./ 或 …/ 开头的相对路径时,很容易出现路径动态拼接错误的问题。
原因:代码在运行的时候,会以执行 node 命令时所处的目录,动态拼接出被操作文件的完整路径。
解决方案:在使用 fs 模块操作文件时,直接提供完整的路径,不要提供 ./ 或 …/ 开头的相对路径,从而防止路径动态拼接的问题。
13.3 path 路径模块
13-3-1 什么是 path 路径模块
13-3-2 路径拼接
2-1 path.join()的语法格式
使用 path.join() 方法,可以把多个路径片段拼接为完整的路径字符串,语法格式如下:
参数解读:
⚫ …paths 路径片段的序列
⚫ 返回值:
2-2 path.join() 的代码示例
使用 path.join() 方法,可以把多个路径片段拼接为完整的路径字符串:
注意:今后凡是涉及到路径拼接的操作,都要使用 path.join() 方法进行处理。不要直接使用 + 进行字符串的拼接
13-3-3 获取路径中的文件名
3-1 path.basename() 的语法格式
使用 path.basename() 方法,可以获取路径中的最后一部分,经常通过这个方法获取路径中的文件名,语法格式如下:
3-2 path.basename() 的代码示例
3-3 path.extname() 的语法格式
使用 path.extname() 方法,可以获取路径中的扩展名部分,语法格式如下:
3-4 path.extname() 的代码示例
13.4 http 模块
13-4-1 什么是 http 模块
如果要希望使用 http 模块创建 Web 服务器,则需要先导入它:
13-4-2 进一步理解 http 模块的作用
服务器和普通电脑的区别在于,服务器上安装了 web 服务器软件,例如:IIS、Apache 等。通过安装这些服务器软件,就能把一台普通的电脑变成一台 web 服务器。
在 Node.js 中,我们不需要使用 IIS、Apache 等这些第三方 web 服务器软件。因为我们可以基于 Node.js 提供的http 模块,通过几行简单的代码,就能轻松的手写一个服务器软件,从而对外提供 web 服务。
13-4-3 创建最基本的 web 服务器
3-1 创建 web 服务器的基本步骤
3-2 步骤1 - 导入 http 模块
3-3 步骤2 - 创建 web 服务器实例
调用 http.createServer() 方法,即可快速创建一个 web 服务器实例:
3-4 步骤3 - 为服务器实例绑定 request 事件
为服务器实例绑定 request 事件,即可监听客户端发送过来的网络请求:
3-5 步骤4 - 启动服务器
调用服务器实例的 .listen() 方法,即可启动当前的 web 服务器实例:
13-4-4 req 请求对象
只要服务器接收到了客户端的请求,就会调用通过 server.on() 为服务器绑定的 request 事件处理函数。如果想在事件处理函数中,访问与客户端相关的数据或属性,可以使用如下的方式:
13-4-5 res 响应对象
在服务器的 request 事件处理函数中,如果想访问与服务器相关的数据或属性,可以使用如下的方式:
13-4-6 解决中文乱码问题
当调用 res.end() 方法,向客户端发送中文内容的时候,会出现乱码问题,此时,需要手动设置内容的编码格式:
13-4-7 根据不同的 url 响应不同的 html 内容
7-1 核心实现步骤
7-2 动态响应内容
13.5 Node.js 中模块的
13-5-1 Node.js 中模块的分类
13-5-2 加载模块
使用强大的 require() 方法,可以加载需要的内置模块、用户自定义模块、第三方模块进行使用。例如:
13-5-3 Node.js 中的模块作用域
3-1 什么是模块作用域
和函数作用域类似,在自定义模块中定义的变量、方法等成员,只能在当前模块内被访问,这种模块级别的访问限制,叫做模块作用域
3-2 模块作用域的好处
防止了全局变量污染的问题
13-5-4 向外共享模块作用域中的成员
4-1 module对象
在每个 .js 自定义模块中都有一个 module 对象,它里面存储了和当前模块有关的信息,打印如下:
4-2 module.exports 对象
在自定义模块中,可以使用 module.exports 对象,将模块内的成员共享出去,供外界使用。外界用 require() 方法导入自定义模块时,得到的就是 module.exports 所指向的对象。
4-3 共享成员时的注意点
使用 require() 方法导入模块时,导入的结果,永远以 module.exports 指向的对象为准
4-4 exports 对象
由于 module.exports 单词写起来比较复杂,为了简化向外共享成员的代码,Node 提供了 exports 对象。默认情况下,exports 和 module.exports 指向同一个对象。最终共享的结果,还是以 module.exports 指向的对象为准。
4-5 exports 和 module.exports 的使用误区
时刻谨记,require() 模块时,得到的永远是 module.exports 指向的对象:
13-5-5 Node.js 中的模块化规范
13.6 模块的加载机制
13-6-1 优先从缓存中加载
模块在第一次加载后会被缓存。 这也意味着多次调用 require() 不会导致模块的代码被执行多次。
注意:不论是内置模块、用户自定义模块、还是第三方模块,它们都会优先从缓存中加载,从而提高模块的加载效率。
13-6-2 内置模块的加载机制
内置模块是由 Node.js 官方提供的模块,内置模块的加载优先级最高。
例如,require(‘fs’) 始终返回内置的 fs 模块,即使在 node_modules 目录下有名字相同的包也叫做 fs。
13-6-3 自定义模块的加载机制
13-6-4 第三方模块的加载机制
13-6-5 目录作为模块
13.7 Express
13-7-1 初识 Express
1-1 什么是 Express
1-2 Express 能做什么
1-3 Express 的基本使用
3-1 安装
3-2 创建基本的 Web 服务器
3-3 监听 GET 请求
通过 app.get() 方法,可以监听客户端的 GET 请求,具体的语法格式如下:
3-4 监听 POST 请求
通过 app.post() 方法,可以监听客户端的 POST 请求,具体的语法格式如下:
3-5 把内容响应给客户端
通过 res.send() 方法,可以把处理好的内容,发送给客户端
3-6 获取 URL 中携带的查询参数
通过 req.query 对象,可以访问到客户端通过查询字符串的形式,发送到服务器的参数
3-7 获取 URL 中的动态参数
通过 req.params 对象,可以访问到 URL 中,通过 : 匹配到的动态参数:
1-4 托管静态资源
4-1 express.static()
express 提供了一个非常好用的函数,叫做 express.static(),通过它,我们可以非常方便地创建一个静态资源服务器,
例如,通过如下代码就可以将 public 目录下的图片、CSS 文件、JavaScript 文件对外开放访问了:
4-2 托管多个静态资源目录
如果要托管多个静态资源目录,请多次调用 express.static() 函数
访问静态资源文件时,express.static() 函数会根据目录的添加顺序查找所需的文件。
4-3 挂载路径前缀
如果希望在托管的静态资源访问路径之前,挂载路径前缀,则可以使用如下的方式:
1-5 nodemon
5-1 为什么要使用 nodemon
5-2 安装 nodemon
5-3 使用 nodemon
当基于 Node.js 编写了一个网站应用的时候,传统的方式,是运行 node app.js 命令,来启动项目。这样做的坏处是:代码被修改之后,需要手动重启项目。
现在,我们可以将 node 命令替换为 nodemon 命令,使用 nodemon app.js 来启动项目。这样做的好处是:代码被修改之后,会被 nodemon 监听到,从而实现自动重启项目的效果。
13-7-2 Express 路由
2-1 路由的概念
1-1 Express 中的路由
在 Express 中,路由指的是客户端的请求与服务器处理函数之间的映射关系。
Express 中的路由分 3 部分组成,分别是请求的类型、请求的 URL 地址、处理函数,格式如下:
1-2 Express 中的路由的例子
1-3 路由的匹配过程
2-2 路由的使用
2-1 最简单的用法
在 Express 中使用路由最简单的方式,就是把路由挂载到 app 上,示例代码如下:
2-2 模块化路由
2-3 创建路由模块
2-4 注册路由模块
2-5 为路由模块添加前缀
类似于托管静态资源时,为静态资源统一挂载访问前缀一样,路由模块添加前缀的方式也非常简单:
13-7-3 Express 中间件
3-1 中间件的概念
1-1 什么是中间件
中间件(Middleware ),特指业务流程的中间处理环节
1-2 现实生活中的例子
1-3 Express 中间件的调用流程
当一个请求到达 Express 的服务器之后,可以连续调用多个中间件,从而对这次请求进行预处理。
1-4 Express 中间件的格式
Express 的中间件,本质上就是一个 function 处理函数,Express 中间件的格式如下:
注意:中间件函数的形参列表中,必须包含 next 参数。而路由处理函数中只包含 req 和 res。
1-5 next 函数的作用
next 函数是实现多个中间件连续调用的关键,它表示把流转关系转交给下一个中间件或路由。
3-2 Express 中间件的初体验
2-1 定义中间件函数
可以通过如下的方式,定义一个最简单的中间件函数
2-2 全局生效的中间件
客户端发起的任何请求,到达服务器之后,都会触发的中间件,叫做全局生效的中间件。
通过调用 app.use(中间件函数),即可定义一个全局生效的中间件,示例代码如下:
2-3 定义全局中间件的简化形式
2-4 中间件的作用
多个中间件之间,共享同一份 req 和 res。基于这样的特性,我们可以在上游的中间件中,统一为 req 或 res 对象添
加自定义的属性或方法,供下游的中间件或路由进行使用。
2-5 定义多个全局中间件
可以使用 app.use() 连续定义多个全局中间件。客户端请求到达服务器之后,会按照中间件定义的先后顺序依次进行调用,示例代码如下:
2-6 局部生效的中间件
不使用 app.use() 定义的中间件,叫做局部生效的中间件,示例代码如下:
2-7 定义多个局部中间件
可以在路由中,通过如下两种等价的方式,使用多个局部中间件:
2-8 了解中间件的5个使用注意事项
3-3 中间件的分类
3-1 应用级别的中间件
通过 app.use() 或 app.get() 或 app.post() ,绑定到 app 实例上的中间件,叫做应用级别的中间件,代码示例如下:
3-2 路由级别的中间件
绑定到 express.Router() 实例上的中间件,叫做路由级别的中间件。它的用法和应用级别中间件没有任何区别。只不\过,应用级别中间件是绑定到 app 实例上,路由级别中间件绑定到 router 实例上,代码示例如下:
3-3 错误级别的中间件
错误级别中间件的作用:专门用来捕获整个项目中发生的异常错误,从而防止项目异常崩溃的问题。
格式:错误级别中间件的 function 处理函数中,必须有 4 个形参,形参顺序从前到后,分别是 (err, req, res, next)。
**注意:**错误级别的中间件,必须注册在所有路由之后!
错误级别中间件的 function 处理函数中,必须有 4 个形参,形参顺序从前到后,分别是 (err, req, res, next)。
3-4 Express内置的中间件
3-5 第三方的中间件
3-4 自定义中间件
4-1 需求描述与实现步骤
4-2 定义中间件
使用 app.use() 来定义全局生效的中间件,代码如下:
4-3 监听 req 的 data事件
在中间件中,需要监听 req 对象的 data 事件,来获取客户端发送到服务器的数据。
如果数据量比较大,无法一次性发送完毕,则客户端会把数据切割后,分批发送到服务器。所以 data 事件可能会触
发多次,每一次触发 data 事件时,获取到数据只是完整数据的一部分,需要手动对接收到的数据进行拼接。
4-4 监听 req 的end事件
当请求体数据接收完毕之后,会自动触发 req 的 end 事件。
因此,我们可以在 req 的 end 事件中,拿到并处理完整的请求体数据。示例代码如下:
4-5 使用 querystring 模块解析请求体数据
Node.js 内置了一个 querystring 模块,专门用来处理查询字符串。通过这个模块提供的 parse() 函数,可以轻松把查询字符串,解析成对象的格式。示例代码如下
4-6 将解析出来的数据对象挂载为req.body
上游的中间件和下游的中间件及路由之间,共享同一份 req 和 res。因此,我们可以将解析出来的数据,挂载为 req 的自定义属性,命名为 req.body,供下游使用。示例代码如下:
4-7 将自定义中间件封装为模块
为了优化代码的结构,我们可以把自定义的中间件函数,封装为独立的模块,示例代码如下:
13-7-4 使用 Express 写接口
4-1 创建基本的服务器
4-2 创建 API 路由模块
4-3 编写 GET 接口
4-4 编写 POST 接口
4-5 CORS 跨域资源共享
5-1 接口的跨域问题
5-2 使用cors 中间件解决跨域问题
5-3 什么是 CORS
5-4 CORS 的注意事项
5-5 CORS 响应头部 - Access-Control-Allow-Origin
响应头部中可以携带一个 Access-Control-Allow-Origin 字段,其语法如下:
其中,origin 参数的值指定了允许访问该资源的外域 URL。
例如,下面的字段值将只允许来自 http://itcast.cn 的请求:
如果指定了 Access-Control-Allow-Origin 字段的值为通配符 *****,表示允许来自任何域的请求,示例代码如下:
5-6 CORS 响应头部 - Access-Control-Allow-Headers
5-7 CORS 响应头部 - Access-Control-Allow-Methods
5-8 CORS请求的分类
5-9 简单请求
5-10 预检请求
5-11 简单请求和预检请求的区别
4-6 JSONP 接口
6-1 回顾 JSONP 的概念与特点
6-2 创建 JSONP 接口的注意事项
如果项目中已经配置了 CORS 跨域资源共享,为了防止冲突,必须在配置 CORS 中间件之前声明 JSONP 的接口。否则JSONP 接口会被处理成开启了 CORS 的接口。示例代码如下:
6-3 实现 JSONP 接口的步骤
6-4 实现 JSONP 接口的具体代码
6-5 在网页中使用 jQuery 发起 JSONP 请求
调用 $.ajax() 函数,提供 JSONP 的配置选项,从而发起 JSONP 请求,示例代码如下:
13.8 数据库与身份认证
13-8-1 在项目中操作mysql
1-1 在项目中操作数据库的步骤
1-2 安装与配置 mysql 模块
2-1 安装 mysql 模块
mysql 模块是托管于 npm 上的第三方模块。它提供了在 Node.js 项目中连接和操作 MySQL 数据库的能力。
想要在项目中使用它,需要先运行如下命令,将 mysql 安装为项目的依赖包
2-2 配置 mysql 模块
在使用 mysql 模块操作 MySQL 数据库之前,必须先对 mysql 模块进行必要的配置,主要的配置步骤如下:
2-3 测试 mysql 模块能否正常工作
调用 db.query() 函数,指定要执行的 SQL 语句,通过回调函数拿到执行的结果:
1-3 使用 mysql 模块操作 MySQL 数据库
3-1 查询数据
查询 users 表中所有的数据:
3-2 插入数据
向 users 表中新增数据, 其中 username 为 Spider-Man,password 为 pcc321。示例代码如下:
3-3 插入数据的便捷方式
向表中新增数据时,如果数据对象的每个属性和数据表的字段一一对应,则可以通过如下方式快速插入数据:
3-4 更新数据
可以通过如下方式,更新表中的数据
3-5 更新数据的便捷方式
更新表数据时,如果数据对象的每个属性和数据表的字段一一对应,则可以通过如下方式快速更新表数据:
3-6 删除数据
在删除数据时,推荐根据 id 这样的唯一标识,来删除对应的数据。示例如下:
3-7 标记删除
使用 DELETE 语句,会把真正的把数据从表中删除掉。为了保险起见,推荐使用标记删除的形式,来模拟删除的动作。
所谓的标记删除,就是在表中设置类似于 status 这样的状态字段,来标记当前这条数据是否被删除。
当用户执行了删除的动作时,我们并没有执行 DELETE 语句把数据删除掉,而是执行了 UPDATE 语句,将这条数据对应的 status 字段标记为删除即可。
13-8-2 前后端的身份认证
2-1 Web 开发模式
1-1 服务端渲染的 Web 开发模式
服务端渲染的概念:服务器发送给客户端的 HTML 页面,是在服务器通过字符串的拼接,动态生成的。因此,客户端不需要使用 Ajax 这样的技术额外请求页面的数据。代码示例如下
1-2 服务端渲染的优缺点
1-3 前后端分离的 Web 开发模式
前后端分离的概念:前后端分离的开发模式,依赖于 Ajax 技术的广泛应用。简而言之,前后端分离的 Web 开发模式,就是后端只负责提供 API 接口,前端使用 Ajax 调用接口的开发模式。
1-4 前后端分离的优缺点
1-5 如何选择 Web 开发模式
2-2 身份认证
2-1 什么是身份认证
2-2 为什么需要身份认证
2-3 不同开发模式下的身份认证
2-3 Session 认证机制
3-1 HTTP 协议的无状态性
3-2 如何突破HTTP 无状态的限制
3-3 什么是Cookie
3-4 Cookie 在身份认证中的作用
3-5 Cookie 不具有安全性
3-6 提高身份认证的安全性
3-7 Session*的工作原理
2-4 在 Express 中使用 Session 认证
4-1 安装express-session 中间件
在 Express 项目中,只需要安装 express-session 中间件,即可在项目中使用 Session 认证:
4-2 配置 express-session 中间件
express-session 中间件安装成功后,需要通过 app.use() 来注册 session 中间件,示例代码如下:
4-3 向 session 中存数据
当 express-session 中间件配置成功后,即可通过 req.session 来访问和使用 session 对象,从而存储用户的关键信息:
4-4 从 session 中*取数据
可以直接从 req.session 对象上获取之前存储的数据,示例代码如下:
4-5 清空 session
调用 req.session.destroy() 函数,即可清空服务器保存的 session 信息。
2-5 JWT 认证机制
5-1 了解 Session 认证的局限性
5-2 什么是 JWT
JWT(英文全称:JSON Web Token)是目前最流行的跨域认证解决方案。
5-3 JWT 的工作原理
总结:用户的信息通过 Token 字符串的形式,保存在客户端浏览器中。服务器通过还原 Token 字符串的形式来认证用户的身份。
5-4 JWT 的组成部分
JWT 通常由三部分组成,分别是 Header(头部)、Payload(有效荷载)、Signature(签名)。
三者之间使用英文的“.”分隔,格式如下:
下面是 JWT 字符串的示例:
5-5 JWT 的三个部分各自代表的含义
5-6 JWT 的使用方式
客户端收到服务器返回的 JWT 之后,通常会将它储存在 localStorage 或 sessionStorage 中。
此后,客户端每次与服务器通信,都要带上这个 JWT 的字符串,从而进行身份认证。推荐的做法是把 JWT 放在 HTTP
请求头的 Authorization 字段中,格式如下:
2-6 在 Express 中使用 JWT
6-1 安装JWT 相关的包
其中:
⚫ jsonwebtoken 用于生成 JWT 字符串
⚫ express-jwt 用于将 JWT 字符串解析还原成 JSON 对象
6-2 导入JWT 相关的包
6-3 定义 secret 密钥
6-4 在登录成功后生成 JWT 字符串
调用 jsonwebtoken 包提供的 sign() 方法,将用户的信息加密成 JWT 字符串,响应给客户端:
6-5 将JWT 字符串还原为JSON 对象
6-6 使用 req.user 获取用户信息
当 express-jwt 这个中间件配置成功之后,即可在那些有权限的接口中,使用 req.user 对象,来访问从 JWT 字符串
中解析出来的用户信息了,示例代码如下:
6-7 捕获解析 JWT 失败后产生的错误
当使用 express-jwt 解析 Token 字符串时,如果客户端发送过来的 Token 字符串过期或不合法,会产生一个解析失败
的错误,影响项目的正常运行。我们可以通过 Express 的错误中间件,捕获这个错误并进行相关的处理,示例代码如下:
13-8-3 bcryptjs进行加密
3-1 安装
npm i bcryptjs@2.4.3
3-2 导入 bcryptjs
const bcrypt = require('bcryptjs')
3-3 进行加密
// 对用户的密码,进行 bcrype 加密,返回值是加密之后的密码字符串userinfo.password = bcrypt.hashSync(userinfo.password, 10)
13-8-4 优化数据验证
4-1 安装 @escook/express-joi 中间件
来实现自动对表单数据进行验证的功能:
npm i @escook/express-joi
4-2 安装 @hapi/joi 包
为表单中携带的每个数据项,定义验证规则:
npm install @hapi/joi@17.1.0
4-3 新建 /schema/user.js
用户信息验证规则模块,并初始化代码如下:
/*** string() 值必须是字符串* alphanum() 值只能是包含 a-zA-Z0-9 的字符串* min(length) 最小长度* max(length) 最大长度* required() 值是必填项,不能为 undefined* pattern(正则表达式) 值必须符合正则表达式的规则*/// 用户名的验证规则const username = joi.string().alphanum().min(1).max(10).required()// 密码的验证规则const password = joi.string().pattern(/^[\S]{6,12}$/).required()// 注册和登录表单的验证规则对象exports.reg_login_schema = {// 表示需要对 req.body 中的数据进行验证body: { username, password, },}
4-4 修改 /router/user.js 中的代码如下
const express = require('express')const router = express.Router()// 导入用户路由处理函数模块const userHandler = require('../router_handler/user')// 1. 导入验证表单数据的中间件const expressJoi = require('@escook/express-joi')// 2. 导入需要的验证规则对象const { reg_login_schema } = require('../schema/user')// 注册新用户// 3. 在注册新用户的路由中,声明局部中间件,对当前请求中携带的数据进行验证// 3.1 数据验证通过后,会把这次请求流转给后面的路由处理函数// 3.2 数据验证失败后,终止后续代码的执行,并抛出一个全局的 Error 错误,进入全局错误级别中间件中进行处理router.post('/reguser', expressJoi(reg_login_schema), userHandler.regUser)// 登录router.post('/login', userHandler.login)module.exports = router
4-5 在 app.js 的全局错误级别中间件中
捕获验证失败的错误,并把验证失败的结果响应给客户端:
const joi = require('@hapi/joi')// 错误中间件app.use(function (err, req, res, next) { // 数据验证失败 if (err instanceof joi.ValidationError) return res.cc(err) // 未知错误 res.cc(err)})