Node

218 阅读28分钟

**Node.js **

1.npm的使用

1.1终端中的快捷键

1.使用 ↑键,可以复制上一次输入的代码

2.使用tab可以在当前文件夹选择文件进行切换操作

3.使用esc按键,可以一键清空当前已输入的命令

4.输入cls命令,可以清空终端

5.使用node文件时:node 文件名。进行调用

2.文件系统模块

2.1 什么是fs文件系统模块

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

例如:

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

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

2.2读写指定文件中的内容
1. fs.readFile() 的语法格式

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

语法:

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

=>参数:

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

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

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


=>callback():

err读取成功时返回null

err读取失败时返回一个错误对象

success 读取成功时返回读取的值

success 读取失败时返回null

2. fs.writeFile() 的示例代码

fs.writeFile()方法,向指定文件路径中,写入文件内容。

语法:

fs.writeFile(path,data[,options],callback(err))

=>参数:

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

参数2 data :必选参数,表示要写入的内容。

参数3 options:可选参数,表示以什么格式写入文件内容,默认值utf-8

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


=>callback():

err写入成功时返回null

err写入失败时返回一个错误对象

2.3路径模块
1.路径拼接带来的问题

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

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

解决方案:在使用fs模块操作文本时,直接提供完整的路径,不要提供./或者../开头的相对路径,从而防止动态路径拼接的问题。

2.解决方式__dirname

​ __dirname表示当前文件所处的目录,也就可以解决相对路径,容易拼接错误。而完整路径不容易书写问题

2.4 path路径模块
1.path.join(参数1,参数....)代码示例

​ 使用 path.join()方法可以将多个路径片段拼接成完整的路径字符串。

​ const pathjoin = path.join('参数1',“参数2”,.....)

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

2. path.basename()方法,获取路径文件名

​ =>作用:将路径字符串中,最后的文件名解析出来。

​ path.basenmae(path[,ext])

​ => 参数用法:

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

​ ext 可选参数,表示文件扩展名

​ =>返回值:

​ 路径中的最后一部分的文件名。

  1. path.extname()方法,获取路径文件名的扩展名

​ =>作用:获取路径文件名的扩展名。

​ path.extname(path[,ext])

​ => 参数用法:

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

​ ext 可选参数,表示文件扩展名

​ =>返回值:

​ 路径中的最后一部分的文件名的扩展名。

3.http模块

3.1 服务器相关概念

​ ip地址就是互联网上每台计算机的唯一地址,因此ip地址具有唯一性。如果把“个人电脑”比作“一台电话”,ip地址就是电话号码

​ =>地址的格式:通常用"电分十进制"表示成(a,b,c,d)的形式,其中,a、b、c、d 都是 0~255 之间的十进制整数。例如:用点分十进制表示的ip地址(192.168.1.1)

+注意:

=> 1 .互联网的每台web服务器,都有自己的id地址,例如:大家可以在 Windows 的终端中运行 ping www.baidu.com 命令,即可查看到百度服务器的 ip 地址。

​ =>2 . 开发期间,自己的电脑既是一台服务器,也是一个客户端,为了方便测试,可以在自己的浏览器中输入 127.0.0.1 这个ip地址,就能把自己的电脑当做一台服务器进行访问。

3.2 服务器相关的概念
1.域名和域名服务器

​ =>域名与域名服务器对应的是,人与身份证的关系。ip地址是一长串数字,不直观、不便于记忆。

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

+注意:

​ =>单纯使用ip地址,互联网中的电脑也能够正常工作。但是有了域名的加持,能让互联网的事件变得更加方便。

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

2.端口号

​ 计算机的端口号,类似于现实生活的门牌号一样。通过门牌号,外卖小哥可以在整栋大楼众多的房间中,将外卖送到你的手中。

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

+注意:

​ =>每个端口号不能同时被多个web服务器占用。

​ =>在实际应用中,url中的 80 端口可以被省略

3.3 创建 web 服务器
1.获得http对象

​ const http = require('http');

2.通过http对象创建 server 对象,web服务器实例

​ const server = http**.**createServer();

3.使用服务器实例的 .on() 方法,为服务器绑定一个request事件

​ 每当服务器被请求时,触发.on()方法

​ server**.**on('request',function(req,res){})

​ =>request : 绑定request事件

​ =>req请求对象:存放着与客户端相关的数据或属性

​ +req对象的方法:

​ =>req.url : 返回客户请求的url地址

​ =>req.method:返回客户请求的method类型

​ =>res 响应对象方法:

​ =>res.end()方法,向客户端响应内容。并关闭客户端。

4.使用服务器实例的 .listen() 方法,启动服务器

​ server.liseten(参数1,function(){})

​ 参数1 :启动的服务器端口,如:8080

3.4 根据不同的 url 响应不同的 html 内容
1.核心实现步骤

​ 1)获取请求的 url 地址

​ 2)设置默认的响应内容为 404 Not found

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

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

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

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

image-20220626110355374转存失败,建议直接上传图片文件

4.Node.js 中的模块化

4.1 Node.js 中模块的分类

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

​ =>内置模块(内置模块是由 Node.js 官方提供的,例如 fs、path、http等),

​ => 自定义模块 (用户创建的每个 .js 文件 ,都是自定义模块)

​ => 第三方模块 (由第三方开发出来的模块 , 并非官方提供的内置模块,也不是用户创建的自定义模块,使用需要下载)

4.2 加载模块

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

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

4.3 node.js 中的模块作用域

​ 模块的好处:防止了全局变量污染的问题

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

​ => module.exports 对象

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

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

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

=> exports 对象

​ 由于 module.exports 拼写比较复杂。node 提供了 exports 对象。默认情况下, exports 和module.exports 指向同一个对象。最终共享的结果,还是以 module.exports 指向的对象为准

4.5 Node.js 中的模块化规范

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

​ CommonJS 规定:

​ => 每个模块内部、”module 变量“代表当前模块

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

​ => 加载某个模块,其实是加载模块的 module.exports 属性。"require() 方法用于加载模块"

5. npm 与包

5.2 npm 初体验

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

​ npm install 包的完整名字

简写

​ npm i 包的完整名字

5.3 包管理配置文件
1.如何记录项目中安装了哪些包

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

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

2.快速创建 package.json

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

​ =>代码:

​ npm init -y

​ +注意:

​ =>上述命令只能在“英文的目录下运行”!! 所以,项目文件夹的名称一定要使用英文命名,“不要使用中文,不能出现空格”

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

3. dependencies(依靠) 节点

package.json 文件中,有一个dependencies (依靠)节点,专门用来记录使用的 npm install 命令安装了哪些包。

4. 一次性安装所有的包

当我们拿到一个剔除了 node_modules 的项目之后,需要先把所有的包下载到项目中,才能将项目运行起来。否则会报类似于下面的错误:

​ Error:Cannot find module 'moment'

5. 一次性卸载所有包

代码:

=> npm uninstall jquery

卸载指定的包,将 jquery 包卸载,被卸载的包,自动从 package.json 的 dependencies 中移除掉

6.devDependencies 节点

如果某些包“只在项目开发阶段”会用到,在“项目上线之后不会用到”,则建议把这些包记录到 devDependencies 节点中。

代码:

​ => npm i 包名 -D

​ => npm install 包名 --save-dev

5.4 解决下包速度慢的问题
1.下载慢的原因

​ npm是国外的服务器,使用下载时间比较漫长

2.切换 npm 的下包镜像源

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

​ # 查看当前的下包镜像源

​ npm config get registry

​ #将下包的镜像源切换为淘宝镜像源

​ npm config set registry = registry.npm.taobao.org/

3. nrm

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

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

​ npm i nrm -g

​ # 查看所有可用的镜像源

​ nrm 1s

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

​ nrm use taobao

5.5 包的分类
1.项目包

​ 那些被安装到项目的 node_nodules 目录中的包,都是项目包

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

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

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

语法:

​ =>开发依赖包

​ npm i 包名 -D

​ =>核心依赖包

​ npm i 包名

2.全局包

​ 在执行 npm install 命令是,如果提供了 -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 点要求:

=> 包必须以(单独的目录)存在

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

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

5.6 包管理配置文件
1.包的结构功能

​ {

​ "name": 包的名字

​ "version":包的版本号

​ "main":包的入口

​ "description":包的简介

​ "keyWord":[ "包的关键词1","包的关键词2",包的关键词3,...]

​ "license": 包遵守的协定 (ISC)

​ }

5.7 开发属于自己的包
1. 将文本转义为 html

转义方式:

​ 使用 replace (/<|>|"|&/g,(e)=>{

switch(e){

​ case '<' : retun &lt,

}

})

5.8 发布包
1.登入npm账号

​ npm 账号注册完成后,可以在终端中执行 npm login 命令,依次输入用户、密码、邮箱后。即可登录成功

​ +注意:

​ 在运行 npm login 命令之前,必须先把(下包的服务器)地址切换为 (npm的官方服务器)。否则会发布失败

2.把包发布到 npm 上

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

3.删除以发布的包

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

+注意:

=> npm unpublish 命令只能删除72小时以内发布的包。

=> npm unpublish 删除的包72小时以内不能发布。

6.模块化的加载机制

6.1 优先从缓存中加载

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

​ + 注意:无论是内置模块、用户自定义模块、还是第三方模块,它们都会优先从缓存中加载,从而提高模块的加载效率。

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

​ 例如, require("fs") 始终返回内置的 fs 模块,及时在 node_modules 目录下有名字相同的包也叫做fs

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

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

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

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

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

​ 5.加载失败,终端报错

6.3 第三方模块的加载机制

​ 如果传递给 require() 的模块标识符不是一个内置模块,也没有以 "./" 或 '../'开头,则 Node.js 会从当前模块的父级目录开始,尝试/node_modules 文件夹中加载第三方模块

​ 如果没有找到对应的第三方模块,则移动到再上一层父级目录中,进行加载,知道文件系统的跟目录

例如:假设在 'c\user\a\b\c\test.js' 文件里调用了 require(‘tl’),则 Node.js 会按以下顺序查找;

=> 'c\user\a\b\c\node_modules\tl

=> 'c\user\a\b\node_modules\tl

=> 'c\user\a\node_modules\tl

​ ...

=> 'c\node_modules\tl

=> 报错 err

6.4 目录作为模块

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

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

=> 2 . 如果目录里没有 package.json 文件,或者 main 入口不存在或无法解析,则Node.js 将会试图加载目录下的 index.js 文件 。

=> 3. 如果以上两步都失败了, 则 Node.js 会在 终端打印错误消息,报告模块的缺失 :error:Cannot find module 'xxx'

7. Express

7.1 express 简介
1. 什么是Express

​ 官方给出的概念: express 是基于 Node.js 品台, 快速,开放,极简的 Web开发框架。

​ 通俗的理解: Express 的作用和 Node.js 内置的 http 模块类似 , 是专门用来创建 Web 服务器的。

​ Express 的本质:就是一个 npm 上的第三方包,提供了快速创建 Web 服务器的便捷方法。

Express与http的关系,类似 jquery 和 js的关系。前者是后者的封装代码
2. Express 能做什么

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

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

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

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

7.2express 基本使用
1.创建 express 服务器

语法:

// 1.导入express

const express = require('express')

// 2.创建 web 服务器

const web = express()

// 3. 调用 app.listen (端口号, 启动成功后的回调函数),启动服务器

web**.listen(80,()=>**{

console**.**log('你的服务器地址为 http://127.0.0.1');

})

2. 监听 GET 请求

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

=>语法:

​ web .get("请求url" ,function(req,res){

​ 处理函数

})

=> 参数1:请求的 url 地址

=>参数2 callback:

​ req : 请求对象 ( 包含了请求相关的属性与方法)

​ res : 响应对象 ( 包含了响应相关的属性与方法)

2. 监听 POST 请求

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

=>语法:

​ web .post("请求url" ,function(req,res){

​ 处理函数

})

=> 参数1:请求的 url 地址

=>参数2 callback:

​ req : 请求对象 ( 包含了请求相关的属性与方法)

​ res : 响应对象 ( 包含了响应相关的属性与方法)

3. 获取 url 中携带的查询参数 req.query

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

=> 语法:

​ req.query : 默认情况下返回一个空对象

​ 客户端使用 ?name = zs&age=20 这种查询字符串形式,发送到服务器的参数,可以通过 req.query 对象访问到,例如:

​ req.query.name req.query.age

=>代码 :

​ web.get('/',(req,res)=>{

​ // req.query 默认是一个空对象

​ log(req.query)

})

4. 获取 url 中的动态参数 req.params

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

语法:

=> url 地址中,可以通过 :参数名 的形式,匹配动态参数值

web.get('/user/:id',(req,res) => {

​ log(req.params)

​ //返回值一个对象 id:浏览器输入的值

})

7.3 托管静态资源
1.express.static()

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

​ web.use(express.static('public'))

可以访问 public 目录中的所有文件。

+注意:Express 在指定的静态目录中查找文件,并对外提供资源的访问路径。因此,存放静态文件的目录名不会出现在 URL 中。

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

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

​ web.use(express.static('public'))

​ web.use(express.static('files'))

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

访问机制:

​ =>谁写到前面,那就访问前面的页面。后面的不再访问

​ =>当前面的无法访问时,这时会继续访问后面的页面

3.挂载路径前缀

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

​ web.use(参数1,express.static('public'))

作用:访问文件路径时,要输入 参数1/ index.js 才能访问 public文件夹内的 index.js 文件

7.4 nodemon
1.安装nodemon

​ 使用全局命令 npm i nodemon -g

2.使用 nodemon

​ 当基于 Node.js 编写了一个网站应用的时候,传统的方式,是运行 node app.js 命令,来启动项目。这样的坏处是代码被修改之后,需要手动重启项目。

​ 现在,我们可以将· node 命令替换成 nodemon 命令,使用 nodemon app.js 来启动项目。这样做的好处是:代码被修改之后,会被 nodemon 监听到,从而实现自动重启项目的效果

8 Express路由

8.1 路由的概念
1.Express 中的路由

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

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

​ web."请求的类型GET\POST"(地址,处理函数)

​ web.get("./index",function(req,res){

})

2. 模块化路由 router

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

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

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

3)向路由对象上挂载具体的路由

语法:

=>1. 创建方式:

const router =express.router()

=>2. 调用,给路由对象挂载get、post路由

router.get('url',(req,res)=>{

})

=>3.将路由导出,直接将路由对象整个导出

module.exports = router


=>4.外部调用该路由

const getRouter = require('路由的地址')

=>5.注册该路由

web.use(getRouter )

3. 为路由模块添加前缀

为了方便用户访问,可以给路由添加前缀以作为区别:

语法:

​ web.use('添加的前缀', 路由对象)

9.Express 中间件

9.1.中间件的作用

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

​ 客户端 (请求)=>中间件1=>中间件2=>中间件n =>(响应)客户端

​ 语法:

​ web.use( (req,res,next)=>{

​ next()

} )

​ next :流转关系 ,转交给下一个中间件或路由。(必须调用)

9.2.定义多个全局中间件

​ 可以使用 web.use() 连续定义多个全局中间件,客户端请求到达服务器之后,会按照中间件定义的先后顺序依次进行调用。

​ web.use( (req,res,next)=>{

​ log 中间件1

​ next()

} )

​ web.use( (req,res,next)=>{

​ log 中间件2

​ next()

} )

打印 中间件1、中间件2

9.3. 局部生效的中间件

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

创建一个中间件函数

const middle = (req,res,next) =>{

console**.**log("局部中间件被调用");

next()

}

当路由1 添加中间件函数,路由2不添加时。路由2不会使用

=>路由1,添加了中间件函数

web**.get('/',middle,(req,res)=>**{

res**.**send('我是作用于index的局部中间1')

}

)

=>路由2

web**.get('/user',middle,(req,res)=>**{

res**.**send('我是作用于index的局部中间2')

}

)

9.4.路由可以挂载多个中间件函数

当路由挂载了多个中间件的时候,会按照中间件的顺序依次调用

语法1:多个对象=>

web**.get('/',middle1,middle2,(req,res)=>**{

res**.**send('我是作用于index的局部中间1')

}

)

语法2:数组对象方法=>

web**.get('/',[middle1,middle2],(req,res)=>**{

res**.**send('我是作用于index的局部中间1')

}

)

9.5.中间件的注意事项

​ => 一定要在路由之前注册中间件

​ => 客户端发过来的请求,可以连续调用多个中间件进行处理

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

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

​ => 连续调用多个中间件时,多个中间件之间,共享 req,res 对象

9.6.错误级别中间件

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

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

web.use(function(err,res,req,next){

​ err.message

})

+注意:错误类型中间件,必须注册在所有路由之后

9.7 Express 内置的中间件

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

​ => express.static 快速托管静态资源的内置中间件,例如:html文件、图片、css样式等

​ => express.json 解析 JSON 格式的请求体数据(有兼容性,仅在 4.16.0 + 版本中可用)

+注意:

​ 服务器需要用 req.body 接收数据。

​ 默认情况下,如果不配置解析表单的中间件,则 req.body 的值为 undefined

9.8 自定义中间件
1. 监听请求对象 req 的 data 事件

​ 在中间件中,需要监听 req 对象的 data 事件,来获取客户端发送到服务器的数据。

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

const str = ""

req.on('data',(chunk)=>{

​ str += chunk

})

2.监听请求对象 req 的 end事件

当请求体数据接收完毕之后,会自动触发 req 的 end 事件

因此,我们可以在 req 的 end 事件中,拿到并处理完整的请求体数据。实例代码如下:

​ req.on('end',()=>{

​ 将请求体数据解析成字符串格式

​ log(str)

})

3. 使用 querystring 模块解析请求体数据

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

使用方式:

1.导入处理 querystring 的 Node.js 内置模块

const qs = require('querystring')

2.调用 qs.parse() 方法,把查询字符串解析为对象

const body = qs.parse(str)

3.上游的中间件 和 下游的中间件 之间,共享一份 req 和 res ,因此可以将解析出的自定义属性挂载到 req 中,命名 req.body。以供下游使用

​ req.body = body

最后调用 next() 函数,执行后续的业务逻辑。

​ next()

10. 使用 Express 写接口

1.接口的跨域问题

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

​ 解决接口跨域问题的方案主要有两种;

​ 1、CORS (主流的解决方案、推荐使用)

​ 2、JSONP (有缺陷的解决方案,只支持 GET 请求)

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

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

使用步骤:

​ =>1. 运行 npm install cors 安装中间件

​ =>2. 使用 const cors = require("cors") 导入中间件

​ =>3. 在路由之前调用 web.use(cors()) 配置中间件。

3. cors 响应头部 - Access-Control-Allow-Origin

通过设置 res.setHeader("Access-Control-Allow-Origin",请求地址)。可以对请求的地址做指定,允许访问该资源的外域 URL。

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

res.setHeader("Access-Control-Allow-Origin ","*")

如果客户端向服务器发送了额外的请求头信息,则需要在服务器端,通过 Access - Control - Allow - Headers 对额外的请求头进行声明,否则这次请求会失败!

res.setHeader("Access-Control-Allow-Origin ",“GET”,"POST")

4.简单请求和预检请求的区别

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

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

预检请求

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

=> 请求方式为 GET、POST 、HEAD 之外的请求 Method 类型

=> 请求头中包含自定义头部字段

=> 向服务器发送了 application/json 格式的数据

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

11. Mysql 的基本使用

11.1. 增删改查
1.查询语句 Select

​ 查询列 select (查询键名称) from (查询表)

​ 输出值: 键对应的值

2.添加语句 insert into

​ 向某个表添加,插入数据: a 为 a1, b 为 b1

​ insert into (a,b) valuse (a1,b1)

3. 更改语句 update

​ 对某个字段的值做修改。可以不写where,对该键的所有值做修改

​ update 数据表名 set 更改的键 = 你修改的值 where 更改的条件 id = 1

​ 对多个字段的值做修改

update 数据表名 set 更改的键 = 你修改的值,更改的键2 = 你修改的值2 where 更改的条件 id = 1

4. 删除语句 Delete

语法: Delete from 表名 where 删除条件

对users 表进行操作,删除 id 为 4 的用户

delete from users where id = 4

11.2 where 子句中运算符的使用

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

操作符

​ = 等于

​ <> 不等于

​ > 大于

<		小于

​ >= 大于等于

​ <= 小于等于

​ between 在某个区间

​ like 搜索某种样式

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

11.3 SQL 的 AND 和 OR 运算符

语法:

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

​ AND: 表示必须同时满足多个条件,相当于 javascript 中2的 && 运算符,例如 if (a>0 && a==101)

UPDATE 表名 set 对内容修改 WHERE 条件1 or 条件2

满足条件1或者2,对内容进行修改

UPDATE student set NAME = '法外狂徒' WHERE gender = '男' or id = 3

以上代码将所有 gender = “男” 或者 id = 3 的对象选取出来

11.4 SQL 的 ORDER BY 子句

对数据进行升降序排序

语法:

​ select * from 表名 where 键名 (asc、desc) 升序、降序

对数据进行升降序多次排序

语法

​ select * from 表名 where 健名 (asc、desc) 升序、降序,健名 (asc、desc) 升序、降序

逗号隔开,多次排序

11.5 SQL 的 count(*) 函数

统计所有的列数可以使用 conut(*) 函数

语法:

​ select count(*) from 列表名 where 条件

​ select count(*) from student where name = "张三"

使用 AS 为列设置别名

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

​ select count(*) AS total from student where name = "张三"

返回值:

​ total : 3

12.在项目中操作 MySQL

1.配置 mysql 模板

// 1.导入 mysql 模块

const mysql = require('mysql')

// 2. 建立与数据库 Mysql 的链接关系

const db = mysql**.**createPool({

host:'127.0.0.1', //数据库的ip地址

user:'root', //登录数据库账号

password:'admin', //登录数据库的密码

database:'demo' //指定要操作哪个数据库

})

测试 mysql 模块能否正常工作

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

​ db.query((" SQL 语句 "),(err,success)=>{

​ if(err) return err.message

log success

})

2.标记删除

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

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

​ status = 0 默认为0表示存在 status = 1 默认删除

​ db.query('update 表名 set status = 1 where id=?')

13.web开发模式

image-20220706210728180转存失败,建议直接上传图片文件

image-20220706210906889转存失败,建议直接上传图片文件

image-20220706211103069转存失败,建议直接上传图片文件

image-20220706213441449转存失败,建议直接上传图片文件

13.1 什么是 cookie

image-20220706214431467转存失败,建议直接上传图片文件

13.2 cookie 在身份认证中的作用

image-20220706215033906转存失败,建议直接上传图片文件

13.3 cookie 不具有安全性

image-20220706215418709转存失败,建议直接上传图片文件

13.4 提高身份认证安全性

image-20220706215847108转存失败,建议直接上传图片文件

13.5 Session 认证机制

image-20220706220106558转存失败,建议直接上传图片文件