Nodejs文档笔记

本文详细介绍了Node.js的安装过程、CMD命令行的使用技巧,以及核心系统模块fs和path的使用,包括读写文件、路径解析等。深入讲解了http模块,展示了创建Web服务器的基本方法。此外,还涵盖了Express框架的初步使用,包括创建服务器、路由处理、中间件等。最后讨论了身份验证中的Session和JWT机制,并简单提及了数据库操作和Mysql的使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Nodejs笔记

1. Node.js安装

1.1 Node.js安装

1.Node.js官网下载长期版本(LTS)安装包,选择默认安装设置,进行安装。
2.在微软终端输入node -v回车,查看到Nodejs版本号证明安装成功。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8SZk2FIN-1674715884000)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221019161402032.png)]

2. 使用Nodejs运行代码及CMD命令行快捷键

2.1创建js文件

console.log('你好Node')

2.2 CMD终端运行 nodejs

通过终端打开文件保存路径,我的文件存在D:\课堂随机点名\js。

C:\Windows\system32>cd D:\课堂随机点名\js

C:\Windows\system32>d:

D:\课堂随机点名\js>node hello
你好Node

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9MyMlSAL-1674715884004)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221019161430279.png)]也可以在js所在的文件的地址栏直接输入cmd加回车键,快速打开cmd命令。

2.3Powershell终端运行 nodejs

在js文件路径下,通过快捷键Shift+鼠标右键,选择通过在此处打开Powershell窗口然后运行node hello

2.4CMD命令行快捷键

1、上箭头:可以自动输入之前运行过的命令

2、Tab键文件名自动补全

3、Esc键可以快速清空当前已输入的命令

4、输入命令**cls**可以清空窗口里的内容

3. 系统模块

3.1 fs模块fs.readFile读取和fs.writeFile写入

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

例如:

  • 读取文容fs.readFile(filename[, options], callback)

​ 参数使用说明如下:

​ path:指定文件路径

​ options:(必选,否则会出问题)指定读取文件的编码格式

​ callback:指定文件读取后通过回调函数拿到数据结果

  • 写入文件fs.writeFile(file, data[, options], callback)

    参数使用说明如下:

    file - 文件名或文件描述符。

    data - 要写入文件的数据,可以是 String(字符串) 或 Buffer(缓冲) 对象。

    options - 该参数是一个对象,包含 {encoding, mode, flag}。默认编码为 utf8, 模式为 0666 , flag 为 ‘w’

    callback - 回调函数,回调函数只包含错误信息参数(err),在写入失败时返回。

3.1.1读取文件内容
	// 1.导入fs模块
const fs=require('fs')

	// 使用fs.readFile方法读取文件
	// 参数一路径,参数二编码格式,参数三回调函数,拿到读取失败和成功的结果 err datastr
fs.readFile('../css/新建文本文档.txt','utf8',function(err,datastr){
    
    // 读取失败会返回错误代码如:errno: -4058,
    // 读取成功会返回null
  console.log(err)
    
    // 显示分割线
  console.log('-------------')
    
    // 读取失败返回 undefined
    // 读取成功返回文本文件的内容
 console.log(datastr)

运行结果如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9DmrQ6us-1674715884011)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221019180543451.png)]

3.1.1.2小案例:判断是否读取成功,读取失败返回读取失败提示
const fs=require('fs')
fs.readFile('../css/新建文本文档.txt','utf8',function(err,datastr){
    
    // 判断是否读取成功,err=true 时代表读取失败,返回读取失败信息,否则~
 if(err){
    return console.log('读取文件失败!'+ err.message)
 }
    console.log('读取文件成功,内容为:' + datastr)
})

运行结果如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ScDCxi89-1674715884015)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221019182339836.png)]

3.1.2写入文件内容
const fs=require('fs')

	// 参数一,保存路径和文件名,参数二写入内容,参数三省略,为默认utf8格式,参数四,对调函数返回错误信息
fs.writeFile('../css/2.txt','hello.js',function(err){
    //写入成功err为null
    //写入失败,err则为一个错误对象
console.log(err)
})

运行结果:

console.log(err) 返回null表明执行成功

3.1.2.1小案例:读取文件后替换指定字符并写入文件

数据准备:小明=100 小红=20 小兰=34 小弟=545 笑傲=2

将以上数据写入txt文件,然后通过fs模块的readFile读取数据后,把等号替换成“:”,将空格替换为换行,然后用witeFile重新写入txt文件

const fs=require('fs')
//不写编码格式,在字符串处理时会报错
fs.readFile('../css/2.txt','utf8',function(err,str){
if (err){    console.log('读取失败!'+ err.message)    }
 console.log('读取成功:'+ str)
 
const n=str.split(' ')
const arr2=[]
n.forEach(item=> {arr2.push(item.replace('=',':'))});
const nstr=arr2.join('\r\n')
console.log('转换后的内容:' + nstr)


fs.writeFile('../css/2.txt',nstr,function(err){
    if(err){
        console.log('写入失败:'+ err.message)
    }
console.log('写入成功')
})
})

运行结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cJTZyWJ1-1674715884018)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221019202429154.png)]

3.2 path路径模块

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

例如:

  • path.join() 拼接路径

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Wu7LVi4H-1674715884022)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221019210704870.png)]

在join中'/b/c','../'表示c的上级目录,的结果为/b

__dirname 表示当前文件所在目录,例如: **path.join(__dirname, '../css/2.txt)**可替代''../css/2.txt''

  • path.basename() 从路径中解析文件名

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KJB9CwEr-1674715884064)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221019210920169.png)]

  • path.extname()获取文件拓展名(后缀名)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2L8SLIFe-1674715884069)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221019212113522.png)]

3.3.1小案例:解析HTML文件案例

案例目标:把嵌入式css、js从html提取出来,并添加成外联样式

// 1、导入模块
const fs=require('fs')
const path=require('path')

// 2、正则表达式匹配<style> </style>和<script> </script>
//   \s是指空白,包括空格、换行、tab缩进等所有的空白,而\S刚好相反,组合就表示所有的字符
//  <\/Style>反斜杠需要转义符/进行转义
const regcss = /<style>[\s\S]*<\/style>/
const regjs = /<script>[\s\S]*<\/script>/

// 3、读取文件内容,并执行三个方法
fs.readFile('../计时器.html','utf8',function(err,str){
    if (err){    console.log('读取失败!'+ err.message)    }
     console.log('读取成功:'+ 'str')
    resCSS(str)
    resJS(str)
    resHTML(str)
    })

// 4、定义三个构造方法
// CSS构造方法
function resCSS(htmlstr){
    // 正则表达式匹配resCSS(str)传递的参数
        const r1=regcss.exec(htmlstr)
        // 替换<Style>标签对
        const r2=r1[0].replace('<style>','').replace('</style>','')
    // 将处理好的结果r2写入文件夹
        fs.writeFile('../css/index.css',r2,function(err){
            if(err){console.log('写入失败:'+ err.message)}
        console.log('写入css成功')  
        })          
    }
    
// JS构造方法
function resJS(htmlstr){
    // 正则表达式匹配resJS(str)传递的参数
        const r1=regjs.exec(htmlstr)
    // 替换<Script>标签对
        const r2=r1[0].replace('<script>','').replace('</script>','')
    // 将处理好的结果r2写入文件夹
        fs.writeFile('../css/index.js',r2,function(err){
            if(err){console.log('写入失败:'+ err.message)}
        console.log('写入js成功') 
        })
    }

// HTML构造方法
function resHTML(htmlstr){
    // 将内嵌标签替换为外联标签
        const r2=htmlstr.replace(regcss,'<link rel="stylesheet" href="./index.css"/>')
        .replace(regjs,'<script src="./index.js"></script>')

    // 写入index.html
            fs.writeFile('../css/index.html',r2,function(err){
                if(err){console.log('写入失败:'+ err.message)}
            console.log('写入html成功')         
            })
        }

3.3 http模块学习

3.3.1 服务器相关概念介绍

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

  • ip地址:cmd运行 ping www.baidu.com命令,即可访问百度服务器的ip地址。

  • 在开发期间,自己的电脑既是一台服务器,又是一个客户端。127.0.0.1localhost都代表本机ip地址。仅供自己测试用,无法分享给别人访问。

  • 域名和域名服务器:域名是ip的别名,域名比ip方便记忆。域名转换成ip要通过域名服务器DNS解析。

  • 端口号:服务器可以运行无数个web服务,每个服务都对应一个唯一的端口号。客户端发过来的请求通过端口号,可以被准确的交给对应的web服务器进行处理。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ChS98XOS-1674715884074)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221020114700243.png)]

3.3.2 创建基本的Web服务器的方法
// 1、导入http模块
const http=require('http')
// 2、创建web服务器实例
const server=http.createServer()
// 3、为服务器绑定 request 事件,监听客户端的请求
server.on('request',function(req,res){
    console.log('服务器正在被访问')
})
// 4、启动服务器
server.listen(8080,function(){
    console.log('server running at http://127.0.0.1:8080')
})

在vscode中新建端口,可以直接执行以上程序,结束快捷键Ctrl+c,清空终端输入cls命令

3.3.2.1 req请求对象
  • req请求对象

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

  • 通过以下方式可以访问客户端相关的数据和属性

    req.url 是客户端请求的URL地址

    req.method 是客户端请求的method类型

    const http=require('http')
    const server=http.createServer()
    // req 是请求对象,包含了客户端相关的数据和属性
    server.on('request', req => {
        // req.url 是客户端请求的URL地址
        // req.method 是客户端请求的method类型
       const url= req.url
       const method=req.method
        // 占位符${}是写在反引号中的,不是'' 
       const str=`your request URL is ${url} , and request method is ${method}`
       console.log('服务器正在被访问:'+str)   
    })
    
    // ()=>{}箭头函数,等同于function(){}
    server.listen(8080,()=>{
        console.log('server running at http://127.0.0.1')
    })
    
3.3.2.2 res响应对象
  • res响应对象,在服务器的requwst事件处理函数中,如果想访问与服务器相关的数据和属性,可以使用如下方式:

    res.end()方法向客户端发送指定内容,并结束这次请求的处理过程。

    const http=require('http')
    const server=http.createServer()
    server.on('request', (req,res) => {
       const str=`your request URL is ${req.url} , and request method is ${req.method}`
       console.log('服务器正在被访问:'+str)
    //    调用res.end()方法,向客户端响应str字符串
       res.end('res:'+str)
    })
    
    server.listen(8080,()=>{
        console.log('server running at http://127.0.0.1')
    })
    
    
    3.3.2.3 解决res.end()响应中文内容乱码问题

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

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AOqRxkwx-1674715884079)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221020172754873.png)]

    const http=require('http')
    const server=http.createServer()
    server.on('request', (req,res) => {
       const str=`您请求的URL是:${req.url} , 及method类型: ${req.method}`
       console.log('服务器正在被访问:'+str)
       // 设置content-type的值为'text/html;charset=utf-8',可防止中文显示乱码
       res.setHeader('content-type','text/html;charset=utf-8')
       res.end('res:'+str)
    })
    
    server.listen(8080,()=>{
        console.log('server running at http://127.0.0.1')
    })
    
    

3.4 自定义模块

3.4.1module.exports对象

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

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZLiT40O2-1674715884082)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221020182012830.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1QpSEMiJ-1674715884086)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221020183023535.png)]

3.4.2 共享成员时的注意点

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XHEasDU2-1674715884092)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221020184008859.png)]

3.4.3 exports对象
  • exports和module.exports指向的是同一个对象。是module.exports的代码简化。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v9V9jpX4-1674715884097)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221020184542310.png)]

  • exports和module.exports使用误区:

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-58M4tXls-1674715884102)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221020190516758.png)]

3.4.4 node中的CommonJ模块规范化

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

CommonJ规定:

  1. 每个模块内部,module变量代表当前模块;
  2. module变量是一个对象,它的exports属性module.exports是对外的接口
  3. require()加载某个模块,其实是加载该模块的module.exports属性。
3.4.5 案例:自定义格式化时间模块
// 自定义格式化时间的模块
function datay1(dtstr){
const dt=new Date(dtstr)

const y=dt.getFullYear()
const m=padZero(dt.getMonth()+1)
const d=padZero(dt.getDate())
const hh=padZero(dt.getHours())
const mm=padZero(dt.getMinutes())
const ss=padZero(dt.getSeconds())
return `${y}-${m}-${d}  ${hh}:${mm}:${ss}`

}

// 定义补0函数 如果值大于9直接返回,否则前面补'0'
function padZero(n){
    return n > 9? n:'0'+n
}

module.exports={
    datay1
}

调用自定义格式化时间的模块

const m = require('./11')
const dt=new Date()
const dd=m.datay1(dt)
console.log(dt)
console.log(dd)

执行结果如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PizY2XwJ-1674715884105)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221020200519023.png)]

4. npm与包

4.1 npm包介绍

Nodejs中第三方模块又称为包。包不同于内置的模块或自定义模块。包是第三方团队或个人开发出来的,免费供所有人使用的模块。

包检索网址:https://www.npmjs.com

包下载网址:https://www.registry.npmjs.org/ 需要npm工具才可以下载,网址无法直接访问

4.2 npm包的安装、卸载

4.2.1 安装包

安装指定包:npm install moment 或简写命令npm i moment

一次性安装多个包(包名间空格隔开):npm install [包名1] [包名2] [包名...]

安装指定包并指定版本号:npm install moment@2.22.2或简写命令npm i moment@2.22.2

安装项目所依赖的所有包:npm install 或简写命令 npm i

//命令全写 安装moment包
//命令简写
npm install moment
npm i moment
//一次性安装多个包,包名间用空格隔开。以下命令同时安装moment,jquery,art-template这3个包。
npm i moment jquery art-template

//版本号的含义:第一位数字:大版本 第二位数字:功能版本 第三位数字:Bug修复版本
//版本号提升规则:前面的版本号增长,后面的一律归零,如2.22.2 大版本号提升结果为:3.0.0
//安装指定版本的包,通过@指定包版本号
npm i moment@2.22.2

//通过moment包,打印格式化后的当前时间 
const moment = require('moment')
const dt=moment().format(`YYYY-MM-DD HH:mm:ss`)
console.log(dt)
4.2.2 卸载包

卸载包命令:npm uninstall [包名]

执行该命令卸载成功后,会自动在package.json文件中的dependencies节点中移除被卸载包的信息。

4.2.3 解决下载包速度慢的问题

淘宝npm镜像服务器:淘宝在国内搭建了一个服务器,每隔一段时间自动同步npm官方服务器上的包,这样在国内下载包速度比直接从npm官方服务器下载更快。

  • 切换npm的下包镜像源

    方法1:命令行直接修改

//查看当前的包下载镜像源
npm config get registry
//将下载包镜像源切换为淘宝镜像源
npm config set registry=https://registry.npm.taobao.org/
//重新查看当前的包下载镜像源,查看切换是否成功
npm config get registry


方法2:通过nrm工具,可以快速查看和切换下zai包的镜像源

//通过npm包管理器,将nrm安装为全局可用
npm i nrm -g
//查看所有可用的镜像源,*开头的是当前使用的镜像源
nrm ls
//切换镜像源为淘宝镜像源
nrm use taobao






###  4.3 包管理的配置文件

1. 快速创建package.json文件

   - npm报告管理提供了一个快捷命令 `npm init -y`,可以在执行命令时所处的目录中,快速创建package.json文件

   - 快捷命令,只能在英文无空格的目录下运行,运行命令会自动记录包名和版本号。

     

2.  一次性安装package.json文件中的所有包

   - 1`npm install`或者`npm i`命令在执行时,npm包管理工具会先读取package.json 文件中的dependencies节点中包名和版本号,然后一次性下载。

     

3. dependencies节点

   - package.json文件中的dependencies节点,专门用来记录npm install命令安装了哪些包。

   - 项目在开发阶段和项目上线后都需要用的包,建议记录在dependencies节点

     

4. devDependencies节点

   - 仅在项目开发阶段使用的包,可以记录在devDependencies节点中

   - 安装指定的包,并记录在devDependencies节点中

     在终端执行命令:`npm install [包名] --save-dev` 或简写命令**`npm i [包名] -D`**

     该命令包名和--save-dev 或者-D的顺序可以颠倒,npm i -D  [包名],这样也可以运行
     
     

### 4.4 包的分类

被安装在项目的node_modules目录中的包都是项目包,项目包又分为开发依赖包、核心依赖包、全局包;

- 开发依赖包:记录在devDependencies节点中的包,仅开发阶段会用到的包

- 核心依赖包:记录在dependencies节点中的包,开发阶段和上线后都会使用的包

//开发依赖包:记录在devDependencies节点中,
npm i 包名 -D
//核心依赖包:记录在dependencies节点中
npm i 包名



- 全局安装包:执行命令时提供-g参数。

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

- 可以参考官方文档使用说明,来判断该包是否有必要全局安装
- 全局包会被安装到C:\Users\用户目录\AppData\Roaming\npm\node_modules目录

//全局安装nrm包
npm i nrm -g
//卸载全局安装的nrm包
npm uninstall nrm -g


i5ting_toc:该包可以把md文档转为html页面的工具,使用步骤如下:
//1、全局安装
npm install i5ting_toc -g
//2、终端中运行命令,-o表示转换成功后用默认浏览器打开html文件
i5ting_toc -f md文件路径 -o




### 4.5 开发属于自己的包

#### 4.5.1 包结构规范

一个规范的包结构上必须符合以下三个点:

1. 包必选包含单独的目录而存在

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

3. package.json中必选包含属性:name 包名,version 版本号,main 包入口

 

#### 4.5.2 初始化报的基本结构

1. 新建iset-time-html文件夹作为包的根目录

2. 在iset-time-html文件夹新建如下三个文件

 - packeag.json	(包管理配置文件)

   ```json
   {
       "name":"iset-time-html",
       "version":"1.0.0",
       "main":"index.js",
       "description": "提供了时间格式化、html特殊标签转换相关功能",
       "keywords":["time","html"],
       "license":"ISC"
       // license许可协议相关内容参考:https://www.jianshu.com/p/86251523e898
   }

 - index.js			  (包的入口文件)

   1. index.js中设置时间格式化的方法

   ```js
   // 自定义格式化时间的方法
   function datay1(dtstr){
       const dt=new Date(dtstr)
       
       const y=dt.getFullYear()
       const m=padZero(dt.getMonth()+1)
       const d=padZero(dt.getDate())
   
       const hh=padZero(dt.getHours())
       const mm=padZero(dt.getMinutes())
       const ss=padZero(dt.getSeconds())
       return `${y}-${m}-${d}  ${hh}:${mm}:${ss}`
       }
   
   function padZero(n){return n > 9? n:'0'+n }// 定义补0函数 如果值大于9直接返回,否则前面补'0'    
   module.exports={datay1}//向外暴露需要的成员
   ```

   调用index.js进行测试函数是否有效

   ```js
   const ith = require('../iset-time-html/index.js')
   const dt=ith.datay1(new Date())
   console.log(dt)
   ```

   2. index.js设置定义转义与反转义html的方法

      ```js
      
      //定义转义html字符的函数的
      return str.replace(/<|>|"|&/g,(match)=>{
          switch (match){
              case '<':
                  return '&lt;'
              case '>':
                  return '&gt;'
              case '"':
                  return '&quot;'
              case '&':
                  return '&amp;'
          	}
      	})
      }
      ```

      ```js
      //定义反转义html字符的函数的
      function HstrF(str){
          return str.replace(/&lt;|&gt;|&quot;|&amp;/g,(match)=>{
              switch (match){
                  case '&lt;':
                      return '<'
                  case '&gt;':
                      return '>'
                  case '&quot;':
                      return '"'
                  case '&amp;':
                      return '&'
              }
          })
        }
      ```

      调用Hstr HstrF或者方法,记得在module.exports中要添加该接口{datay1,Hstr,HstrF}

      ```js
      const ith = require('../iset-time-html/index.js')
      const s='<h1 style=" ">点名系统&nbsp;</h1>'
      console.log(ith.Hstr(s))
      //运行结果为&lt;h1 style=&quot; &quot;&gt;点名系统&amp;nbsp;&lt;/h1&gt;

   3. 将不同的功能进行模块化

      首先把时间格式化和html转义函数别保存在一个目录,然后在index.js文件中写上包的入口文件和需要向外暴漏的成员。

      

      下例中时间格式化和html转义函数分别保存在包的根目录下的src文件夹(/iset-time-html/src)dateFormat.js | htmlescape.js,然后在index.js文件中写入包的入口信息及需要向外暴露的成员。

      ```js
      //index.js文件中
      // 这是包的入口文件
      const date=require(./src/dateFormat)
      const escape=require(./src/htmlescape)
      
      //向外暴露需要的成员
      //...date是展开date变量的意思
      module.exports={
          ...date,
          ...escape
      }
      ```

 - REANME.md	 (文档资料)

   ````markdown
   ## 安装
   ```
   npm i iset-time-html
   ```
   ## 导入
   ```js
   const ith = require('../iset-time-html/index.js')
   ```
   ## 格式化时间
   ```js
   // 调用datay1对时间格式化
   const dt=ith.datay1(new Date())
   console.log(dt)
   ```
   ## 转义HTML中的特殊字符
   ```js
   // HTML字符串
   const s='<h1 style="text-align:center">点名系统<span>123&nbsp;</span></h1>'
   const f='&lt;h1 style=&quot;text-align:center&quot;&gt;点名系统&lt;span&gt;123&amp;nbsp;&lt;/span&gt;&lt;/h1&gt;'
   
   // 打印转义方法Hstr处理后的html内容,转换后结果为f的内容
   console.log(ith.Hstr(s))
   
   // 打印反转义方法HstrF处理后的html内容,转换后结果为s的内容
   console.log(ith.HstrF(f))
   ```
   ## 开源协议
   ISC



###  4.6 发布包

1. 注册npm账号,邮箱链接验证成功后即可。

2. 在终端执行**`npm login `**命令,按照提示依次输入用户名 、密码 、邮箱后回车  登录npm账号。

 **注意:**必选确保下包服务器地址为npm官方服务器。

 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6NmtQMWb-1674715884110)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221021153230983.png)]

 

3. 将终端切换到包的根目录。运行`npm publish`命令,即可把包发布到npm上。

 **注意:**包名不能与npm服务器里现有的包重名。

4. 可以浏览器登录https://www.npmjs.com ,选择用户下拉菜单里的pachages查看自己上传的包信息。

​	**删除已发布的包,终端执行命令`npm unpublish [包名] --force` 即可从npm删除已发布的包。**

### 4.7 npm命令的使用

1. 包的初始化

```bash
npm init -y  //这里文件夹名字不能有 中文 空格 纯数字
//  快速创建 package.json文件,里面会记录npm包管理的名称和版本号
  1. 安装包
npm i 包名
npm install 包名
npm i  包名@版本号  => npm i jquery@2.22.2
  1. 安装全局包
npm i 包名 -g
  1. 卸载包
npm uninstall 包名
npm uninstall 包名@版本号
npm uninstall 包名 -g # 卸载全局包
  1. devDependencies 节点
# 用于记录项目上线之后不会用到的包
npm i 包名 -D  //简写方式

npm install 包名 --save-dev
  1. 解决包下载慢的问题
# 淘宝 NPM 镜像服务器
# 查看当前的下包镜像源
npm config get registry
#设置为淘宝镜像(切换镜像)
npm config set registry https://registry.npm.taobao.org
#检查是否设置成功
npm config get registry
  1. nrm的使用
1. npm i nrm -g # 安装nrm
2. nrm ls # 展示可用的镜像链接
3. nrm use 镜像名字
  1. 包的发布
1. nrm use npm #切换为官方镜像
2. npm login #登陆输入账号和密码
3. npm  publish #注意终端要在项目的根目录
4. npm  unpublish 包名 --force #删除已发布的包
  1. 安装nodemo的使用
npm i nodemon -g # nodemon+项目文件的相对路径启动运行服务器,如:nodemon hello.js

5. 模块的加载机制

5.1优先从缓存加载

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

内置模块,用户自定义模块,第三方模块,都会优先从缓存加载,以提高模块的加载效率。

5.2内置模块的加载机制

内置模块的加载优先级最高

例如,第三方模块和内置模块同名都叫fs,require(‘fs’)始终返回内置模块

5.3 自定义模块的加载机制

require()自定义模块是=时,必须指定路径标识符 ./ or …/,否则会被node当作内置或者第三方模块加载

require()导入时,省略了文件拓展名,node将按顺序加载以下文件:

  1. 按照确切的文件名进行加载
  2. 按照.js后缀进行加载
  3. 按照.json后缀进行加载
  4. 按照.node后缀进行加载
  5. 加载失败,终端报错
5.4 第三方模块的加载机制

require()不包含路径标识符,且不是内置模块,node将会从/node_modules文件夹中加载第三方模块。如果没有找到对应模块,将返回上级模块查找,直到文件系统根目录。

5.5 目录作为模块时的加载机制

require(‘…/iset-time-html/’),将目录作为参数时,有三种情况:

  1. 该目录下有packeag.json文件,以main属性作为加载入口
  2. 该目录没有packeag.json文件,或main属性无效,node将会加载该目录下的index.js文件
  3. 以上两步都失败,终端报错信息 Error:Cannot find module ‘XXXX’

6. Express

6.1 初始Express

Express是基于node.js平台,快速,开放,极简的web开发框架。是专门用来创建web服务器的npm包,可以使我们快速创建web服务器和API接口服务器。

Express基于node.js内置的http模块进一步封装,比http模块更简洁,可以极高的提高开发效率。

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

6.1.1 安装及基本使用

项目目录中npm i express@4.17.1执行该终端命令即可安装express到项目中

  1. 创建服务器
// 1.导入express
const express= require('express')
// 2.创建web服务器
const app = express()
// 3.启动web服务器
app.listen(80,()=>{console.log('服务器已启动')})
  1. 监听客户端的get和post请求,并向客户端响应具体内容

    const express= require('express')
    const app = express()
    // 监听客户端的get和post请求,并向客户端响应具体内容
    app.get('/user',(req,res)=>{
        // 调用express提供的res.send方法,向客户端相应一个json对象
        res.send({name:'二狗子',age:'20',gender:'男'})
    })
    
    app.post('/user',(req,res)=>{
        //调用express提供的res.send方法,向客户端相应一个字符串
        res.send('请求成功')
    })
    
    
    app.listen(80,()=>{console.log('服务器已启动')})
    
  2. req.query对象可以获取url中携带的查询参数

    req.query对象可以访问客户端通过http://127.0.0.1/?name=xm&age=20“查询字符串”的形式,发送到服务器的参数

    const express= require('express')
    const app = express()
    
    app.get('/user',(req,res)=>{
        res.send({name:'二狗子',age:20,gender:'男'})
    })
    
    app.post('/user',(req,res)=>{
        res.send('请求成功')
    })
    
    app.get('/',(req,res)=>{
        // 通过req.query可以获取客户端发过来的查询参数
        // 默认情况下req.query是一个空对象
        console.log(req.query)
        res.send(req.query)
    })
    
    app.listen(80,()=>{console.log('服务器已启动')})
    
    
  3. req.params对象获取URL中的动态参数

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

    :后面的名字根据需要写,URL中可以包含多个动态参数如:

    app.get('/user/:id:name',(req,res)=>{ })

    const express= require('express')
    const app = express()
    
    app.get('/user/:id',(req,res)=>{
        //req.params是动态匹配到的URL参数,默认是一个空对象
        console.log(req.params)
        res.send(req.params)
    
    
    })
    
    app.listen(80,()=>{console.log('服务器已启动')})
    
6.1.2 express.static()方法,快速对外提供静态资源
const express= require('express')
const app = express()

// express.static()函数,快速对外提供静态资源(html,css,js,txt)
// 敲黑板,路径css必须与js在同一级目录下才有效,否则无法向客户端提供静态资源
//网址后拼接文件名加后缀就可以访问,http://127.0.0.1/index.html

//托管多个静态资源目录,即多次调用express.static()函数,以下为托管css和js两个资源目录
app.use(express.static('./css'))
app.use(express.static('./js'))

app.listen(80,()=>{console.log('服务器已启动')})
//express.static()方法挂载路径前缀
const express= require('express')
const app = express()
//网址 + 路径前缀 +带后缀的文件名
//http://127.0.0.1/abc/index.html
app.use('/abc',express.static('./css'))

app.listen(80,()=>{console.log('服务器已启动')})

6.2 Express路由

6.2.1 Express路由介绍

Express中路由是指客户端请求和服务器函数之间的映射关系。 Express终端路由分三部分:请求类型、url地址、处理函数。

//app.METHOD(path,handler)
//METHOD/请求类型可以是get或者post
//path/url地址
//handler/处理函数

//匹配GET请求,请求的URL为/,处理函数打印一句话
app.get('/',function(){consloe.log(这是GET类型的请求)})
//匹配post请求,请求的URL为/,处理函数打印一句话
app.post('/',function(){consloe.log(这是POST类型的请求)})
app.listen(80,()=>{console.log('服务器已启动')})

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tkhCfwVm-1674715884114)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221022114827319.png)]

//Express路由的最简单用法
const express= require('express')
const app = express()
//挂载路由
app.get('/',(req,res)=>{res.send('哈喽这是 GET 类型的请求')})
app.post('/',(req,res)=>{res.send('哈喽这是 POST 类型的请求')})
app.listen(80,()=>{console.log('服务器已启动')})
6.2.2 Express路由的使用

为了提高效率路由很少会挂载到app上,推荐将路由抽离为单独的模块,步骤如下:

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

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

  3. 向路由对象挂载具体路由

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

    // #1.创建路由 自定义路由模块 #
    
    //  2.调用express.Router()函数创建路由对象
    const express= require('express')
    const router = express.Router()
    // 3.挂载具体路由
    router.get('/list',(req,res)=>{res.send('get user list-- 获取用户')})
    router.post('/add',(req,res)=>{res.send('add new user--添加用户成功')})
    // 4.向外共享路由对象
    module.exports=router
    
  5. 使用app.use()函数注册路由模块

    const express= require('express')
    const app = express()
    // 1.导入自定义路由模块
    const router = require('./11')
    // 2.注册路由模块
    app.use(router)
    //为路由模块添加访问前缀的方法
    app.use('/bac',router)
    
    app.listen(80,()=>{console.log('服务器已启动')})
    

6.3 Express中间件

Express中间件:值业务流程的中间处理环节。中间件可以共享一份req,res,在上游为req,res设置自定义方法和属性,可供下游的中间件和路由使用。

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

6.3.1 局部中间件
const express= require('express')
const app = express()

//局部中间件函数
const m1 = function(req,res,next){    
    console.log('局部生效的第一个中间件函数')
    next()
}

app.get('/',m1,(req,res)=>{
    res.send('局部中间件演示效果')
})
app.listen(80,()=>{console.log('服务器已启动')})
6.3.2 使用多个局部中间件
//局部中间件函数
const m1 = function(req,res,next){    
    console.log('局部生效的第一个中间件函数')
    next()
}
const m2 = function(req,res,next){    
    console.log('第2个局部中间件函数')
    next()
}
//m1,m2 也可以写成[m1,m2],效果一样
app.get('/',m1,m2,(req,res)=>{
    res.send('局部中间件演示效果')
})
app.get('/user',(req,res)=>{
    res.send('user page.')
})
6.3.3 全局中间件的作用

在上游为req,res 设置自定义方法和属性,可供下游的中间件和路由使用

const express= require('express')
const app = express()

app.use((req,res,next)=>{
    //获取请求到服务器的时间
    //为req对象挂载自定义属性time,供res.send响应到客户端
    const time=Date.now()
    req.startTime=time    
    console.log('这是最简单的中间件函数')
    next()
})

app.get('/',(req,res)=>{
    res.send('请求到服务器的时间:' + req.startTime)
})

app.listen(80,()=>{console.log('服务器已启动')})
6.3.4 使用多个全局中间件

可以使用app.use()连续定义多个全局中间件,执行顺序为定义的先后顺序进行调用。

//console.log会按照顺序1、2、打印内容,并且返回'用时:Time' 到客户端
app.use((req,res,next)=>{ 
    req.startTime=time 
    console.log('1、调用第一个全局中间件')
    next()
})
app.use((req,res,next)=>{   
    console.log('2、调用第二个全局中间件')
    next()
})
app.get('/',(req,res)=>{
    res.send('用时:' + req.startTime)
})
6.3.5 中间件的4个注意事项
  1. 必须在路由之前注册中间件
  2. 客户端请求,可以连续调用多个中间件处理
  3. function中必须要有next(),且必须是function语句的末尾
  4. 连续调用多个中间件,req,res对象是共享的
6.3.6 中间件的分类
  1. 应用级别的中间件

    绑定到app实例上的中间件叫做应用及中间件,包括全局中间件、局部中间件;

    全局中间件:app.use()

    局部中间件:app.get() app.post()

  2. 路由级别的中间件

    绑定在express.Router()上的中间件叫路由中间件

    const express= require('express')
    const app = express()
    const router = require('./11')
    // 路由级别的中间件
    router.use((req,res)=>{
        console.log('时间:'+ Date.now())
        next()
    })
    app.use('/',router)
    
    app.listen(80,()=>{console.log('服务器已启动')})
    
  3. 错误级别的中间件

    错误级别的中间件必须在所有路由之后

    app.get('/',(req,res)=>{
        throw new Error('未知错误!')//认为制造一个Error错误信息
        res.send('Home page')
    })
    //定义错误级别的中间件
    app.use((err,req,res,next)=>{
        console.log('发生错误:'+ err.message)
        res.send('Error:'+ err.message)
        next()
    })
    
  4. Express内置中间件

    • express.static 快速托管静态资源:HTMML,CSS,JS,图片,txt等(无兼容性)
    app.use(express.static({需要托管的资源目录如:'./index'}))
    
    • express.json 解析json格式的请求体数据(有兼容性,仅支持4.16.0以上版本)

    req.body 可以获取客户端发送的Json和URL—encoded格式的数据

    不配置解析表单数据的中间件,req.body 默认为undefined

    //配置json中间件,解析Json格式的数据
    app.use(express.json())
    //post路由
    app.post('/',(req,res)=>{
        console.log(req.body)
    })
    
    • express.urlencoded 解析URL—encoded格式的请求体数据(有兼容性,仅支持4.16.0以上版本)
    //配置express.urlencoded ,解析URL—encoded格式的数据
    app.use(express.urlencoded({extended:false}))
    app.post('/',(req,res)=>{
        console.log(req.body)
    })
    
  5. 第三方中间件

    第三方包工具安装,看说明使用即可

6.3.7 自定义中间件
6.37.1 监听req的data事件

在中间件中,需要监听req的data事件,获取客户端发送到服务端的数据,若数据量较大,通常客户端会把数据切割后分期发送到服务端,所以data事件可能会多次被触发。每次出发都会收到一部分数据。需要手动拼接。

const express= require('express')
const app = express()
const router = require('./11')
// 路由级别的中间件
app.use((req,res,next)=>{
    let str=''
    req.on('data',(chunk)=>{str+=chunk})
    next()
})
app.listen(80,()=>{console.log('服务器已启动')})
6.37.2 监听req的end事件

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

因此我们可以在req的end事件中,拿到并处理完整的请求体数据。

const express= require('express')
const app = express()
//2.导入querystring解析请求体模块
const qs =require('querystring')

app.use((req,res,next)=>{
    let str = ''
    //监听req的data事件
    req.on('data',(chunk)=>{
        str += chunk       
        console.log('data:'+ str)
    })
    //1.通过end事件,拿到完整的请求体数据,为了解决乱码问题,使用querystring的parss方法解析请求体
    req.on('end',()=>{ 
        //3.querystring解析请求体
        const sstr= qs.parse(str) 
        //为req挂载自定义body属性,这样下游就可以使用sstr常量的数据
        req.body=sstr
        console.log(sstr )
    	next()
        })
})

app.post('/',(req,res)=>{
    //调用req.body属性
    console.log(req.body)
    res.send(req.body)
})
app.listen(80,()=>{console.log('服务器已启动:http://127.0.0.1/')})
6.37.2 将中间件封装为独立的模块

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

//1.创建独立的js文件,本例取名body.js
const qs =require('querystring')
const body =(req,res,next)=>{
    let str = ''
    req.on('data',(chunk)=>{
        str += chunk       
        console.log('data:'+ str)
    })
    req.on('end',()=>{ 
        const sstr= qs.parse(str) 
        req.body=sstr
        console.log(sstr )
    	next()
        })
}
module.exports=body

优化后的代码结构就非常简洁


const express= require('express')
const app = express()
//2.导入自定义模块
const body =require('./body.js')
app.use(body)
app.post('/',(req,res)=>{
    console.log(req.body)
    res.send(req.body)
})
app.listen(80,()=>{console.log('服务器已启动:http://127.0.0.1/')})

6.4 使用Express写接口

6.4.1 创建API路由模块
//1.创建基础服务篇
const express= require('express')
const app = express()
//app.use(express.json())
app.use(express.urlencoded({extended:false}))//解析需要放在在路由之前

//3.导入路由模块
const router= require('./router.js')

//4.创建API路由接口为api
//http://127.0.0.1/api/get 网址需要加上/api/get,因为路由模块中定义了/get
app.usr('/api',router)

app.listen(80,()=>{console.log('服务器已启动:http://127.0.0.1/')})
//2.创建API路由模块,文件名router.js
const express= require('express')
const router = express.Router()

router.get('/get',(req,res)=>{
    //通过req.query属性 获取客户端通过查询字符串发送到服务器的数据
    const query=req.query
    res.send({
        status:0,  //0表示处理成功,1表示失败
        msg:'GET请求成功',
        data:query //需要响应客户端的数据
    })
    console.log(query)
})

router.post('/post',(req,res)=>{
    //通过req.body属性 获取请求体包含的URL-encoded格式的数据
    const body=req.body
    res.send({
        status:0,  //0表示处理成功,1表示失败
        msg:'post请求成功',
        data: body//需要响应客户端的数据
    })
    console.log(body)
})

router.delete('/delete',(req,res)=>{
    
    res.send({
        status:0,  
        msg:'post请求成功'
    })
    console.log('delete')
})

module.exports=router

**注意:**如果接口定义的是http协议,而打开网页使用的是file:打开网页,网站提示如下错误,就表示存在接口跨域问题。

已拦截跨源请求:同源策略禁止读取位于 http://127.0.0.1/api/get?name=%E5%B0%8F%E6%98%8E&shenf=%E5%AD%A6%E7%94%9F 的远程资源。(原因:CORS 头缺少 ‘Access-Control-Allow-Origin’)。状态码:200

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset='UTF-8'>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>接口跨域测试</title>   
    <script src="https://cdn.staticfile.org/jquery/3.6.1/jquery.min.js"></script>
</head>
<body>   
      <button id="g">GET</button>
      <button id="p">POST  </button>
      <button id="d">delete</button>
</body>
    <script>
    $(function(){
        // 为GET按钮绑定点击事件处理函数
    $('#g').on('click',function(){
        $.ajax({
            type:'GET',
            url:'http://127.0.0.1/api/get',
            data:{name:'小明',shenf:'学生'},
            success:function(res){
                console.log(res)
            },
        })
    })
        // 为POST按钮绑定点击事件处理函数
        $('#p').on('click',function(){
        $.ajax({
            type:'POST',
            url:'http://127.0.0.1/api/post',
            data:{bookname:'水浒传',zuozhe:'施耐庵'},
            success:function(res){
                console.log(res)
            },
        })
    })
		// 为DELETE按钮绑定点击事件处理函数
    $('#d').on('click',function(){
        $.ajax({
            type:'DELETE',
            url:'http://127.0.0.1/api/delete',
            success:function(res){
                console.log(res)
            },
        })

    })
})
    </script>
</html>

6.5 通过CORS解决API路由接口的跨域问题

6.5.1 API接口的跨域问题

在线版 jquery 工具:Staticfile CDN

cors是Express的一个第三方中间件。通过安装和配置cors中间件,可以方便的处理接口的跨域问题。

使用步骤:

  1. 运行npm i cors安装
  2. 使用const cors= require('cors')导入
  3. 在路由之前调用app.use(cors())进行配置
const express= require('express')
const app = express()
// 通过导入并配置CORS,解决API路由接口的跨域问题
 const cors= require('cors')
 app.use(cors())

app.use(express.urlencoded({extended:false}))//需要放在在路由之前
const body =require('./router.js')
app.use('/api',body)


app.listen(80,()=>{console.log('服务器已启动:http://127.0.0.1/')})
6.5.2 cors跨域资源共享之响应头部

cors跨域资源共享之响应头部:

  1. Access-Control-allow-Origin

Origin参数指定允许访问该资源的URL,如:

//只允许来自http://itcast.cn的请求
res.setHeader('Access-Control-allow-Origin','http://itcast.cn')
  1. Access-Control-allow-headers

    CORS 默认仅支持9 个请求头:

    • Accept
    • Accept-LanguageC
    • ontent-Language
    • DPR
    • DownlinkSa
    • ve-Data
    • Viewport-Width
    • Width
    • Content-Type
  2. 1 值仅限于下列三个中的一个:

    • text/plain
    • multipart/form-data
    • application/x-www-form-urlencoded

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

// 允许客户端额外发送向服务器发送 Content-Type 请求头和 X-Custom-Header 请求头
// 多个请求头之间使用英文的逗号进行分割
res.setHeader(' Access-Control-Allow-Headers','Content-Type , X-Custom-Header')
  • Access-Control-allow-Methods

    CORS 默认仅支持GET、POST、HEAD 请求。
    PUT、DELETE等方式请求服务器的资源,则需要在服务器端,通过Access-Control-Alow-Methods 来指明实际请求所允许使用的 HTTP 方法。
    示例代码如下

    // 只允许 POST,GET,DELETE,HEAD 请求方法
    //* 是允许所有的 HTTP请求方法
    res.setHeader('Access-Control-Allow-Methods', 'POST,GET,DELETE,HEAD')
    

根据请求方式和请求头的不同,可以将CORS请求分为简单请求和预检请求。

6.4.3 简单请求

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

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

  • HTTP 头部信息不超过以下几种字段:

无自定义头部字段、Accept、Accept-Language、Content-Language、DPR、 Downlink、Save-Data、Viewport-Width、Width 、Content-Type(只有三个值application/x-www-formurlencoded、multipart/form-data、text/plain)

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

6.4.4 预检请求

只要符合以下任何一个条件的请求(非简单请求就是预检请求),都需要进行预检请求:

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

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

6.6 JSONP的概念和特点

由于浏览器的安全性限制,不允许 AJAX 访问协议不同、域名不同、端口号不同的数据接口,浏览器认为这种访问不安全。为了解决这种跨域限制的问题,可以通过动态创建 script 标签的形式,把 script 标签的 src 属性,指向数据接口的地址,因为 script 标签不存在跨域限制,这种数据获取方式,称作 JSONP

JSONP只支持GET请求

6.6.1 JSONP接口

如果项目中已经配置了CORSS跨域资源共享,为了防止冲突,必须在配置CORS中间件之前声明josnp接口,否则会被处理成cors接口。

// 
const express= require('express')
const app = express()
app.use(express.urlencoded({extended:false}))//需要放在在路由之前

// 必须在配置 cors 中间件之前,配置 JSONP 的接口
app.get('/api/jsonp', (req, res) => {
    // 定义 JSONP 接口具体的实现过程
    // 1. 得到客户端发送的回调函数的名称
    const funcName = req.query.callback
    // 2. 定义要发送到客户端的数据对象
    const data = { name: '小明', age: 11 ,sex:0}
    // 3. 拼接出一个函数的调用,JSON.stringify函数把data数据转换成json格式的字符串
    const scriptStr = `${funcName}(${JSON.stringify(data)})`
    // 4. 把拼接的字符串,响应给客户端
    res.send(scriptStr)
  })
const cors= require('cors')
app.use(cors())

const body =require('./11')
app.use('/api',body)
app.listen(80,()=>{console.log('服务器已启动:http://127.0.0.1/')})
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset='UTF-8'>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>接口跨域测试</title>

   
    <script src="https://cdn.staticfile.org/jquery/3.6.1/jquery.min.js"></script>
</head>

<body>
   
      <button id="g">GET</button>
      <button id="p">POST  </button>
      <button id="d">delete</button>
      <button id="jsonp">JSONP</button>

</body>
    <script>
    $(function(){
        // 为GET按钮绑定点击事件处理函数
    $('#g').on('click',function(){
        $.ajax({
            type:'GET',
            url:'http://127.0.0.1/api/get',
            data:{name:'小明',shenf:'学生'},
            success:function(res){
                console.log(res)
            },
        })
    })
        // 为测试POST按钮绑定点击事件处理函数
        $('#p').on('click',function(){
        $.ajax({
            type:'POST',
            url:'http://127.0.0.1/api/post',
            data:{bookname:'水浒传',zuozhe:'施耐庵'},
            success:function(res){
                console.log(res)
            },
        })

    })
    // 为delete按钮绑定点击事件处理函数
    $('#d').on('click',function(){
        $.ajax({
            type:'DELETE',
            url:'http://127.0.0.1/api/delete',
            success:function(res){
                console.log(res)
            },
        })

    })
    
    $('#jsonp').on('click',function(){
        $.ajax({
            type:'get',
            url:'http://127.0.0.1/api/jsonp',
            dataType:'jsonp',
            success:function(res){
                console.log(res)
            },
        })

    })


})
    </script>

</html>

7. Mysql数据库

7.1 数据库概念

数据库:用来组织,存储,管理数据的仓库

数据库管理系统:用户可以对数据库中的数据进行增、删、改、查操作的操作软件。

关系型数据库(又叫传统型数据库,或者SQL数据库):Mysql、Oracie、 SQL server

非关系型数据库(又叫新型数据库,no SQL数据库):Mongodb

Community(支持社区版)的数据库:Mysql、Mongodb

关系型数据库组织结构是由: database(数据库)、table(表)、row(数据行)、field(字段)四部分组成,和Excel的工作簿、工作表,行,列概念一样

7.2 安装并配置Mysql

前端进阶教程node.js入门到精通.Windows10系统安装MySQL

7.3 Mysql的基本使用

前端进阶教程node.js入门到精通.MySQL Workbench组成部分

DataTypr:

  1. int 整数

  2. VARCHAR(45) 字符串 limit45位

  3. tinyint 布尔值

字段的约束标识:

  1. PK(Primary key)主键
  2. NN(Not Null)不允许空值
  3. UQ(Unique)唯一值
  4. Default/Expression 默认值
  5. UN(unsigned )整数
  6. AI(Auto Increment)值自动增长
  7. G(generated)自动生成
  8. ZF(zero fill) 值中最有意义的字节总为0,并且不保存
  9. B(binary) 二进制
--  #查询内容;
SELECT * FROM users;
SELECT id AS'序号' ,name AS'姓名',password AS '密码',status AS '状态' FROM users
SELECT * FROM users WHERE status=0;
SELECT id,name FROM users;
SELECT id,name FROM  my_db_01.users;
--  #指定表内插入新数据;
INSERT INTO users VALUES( 9,'s11ll','adkfj','1');
INSERT INTO users(name,password) VALUES('AAS','ADSDFSDFF');
-- #更新语句;
UPDATE users SET status='1' WHERE id!=0;
-- ORDER BY 对结果进行排序     参数:降序为DESC。默认值为ASC升序
SELECT * FROM users ORDER BY id DESC;

-- 多重排序
SELECT * FROM users ORDER BY status DESC, name asc;
-- SQL数字函数之COUNT
SELECT COUNT(*)as'统计' FROM users WHERE status=0;
-- #删除表内指定数据:
DELETE FROM users WHERE id=7;
-- #比较运算符:
等号:= 
小于:< 
大于:>
小于等于:<=
大于等于:>=
不等于:<> or !=

-- 逻辑运算符:
ANDORNOTXOR	异或

7.4 在Express中操作Mysql

  1. 安装第三方模块Mysql

    //npm安装Mysql模块
    npm install mysql
    
  2. 通过Mysql模块连接到数据库

    // 配置MySQL模块
    // 1.导入Mysql模块
    const mysql=require('mysql')
    // 2.建立数据连接
    const db=mysql.createPool({
        host:'127.0.0.1', 	//指定IP
        user:'root',		//指定账号
        password:'root',	//指定密码
        database:'my_db_01'	//指定要操作的数据库
    })
    // 测试mysql 能否正常工作
    db.query('select 1',(err,results)=>{
        // 执行失败时报错
        if(err) return console.log(err.message)
        // sql语句执行成功
        console.log(results)
    })
    

    通过node执行以上代码命令:node .\mysql.js

  3. 通过Mysql模块执行SQLl语句

    • 查询语句select * from
    const mysql=require('mysql')
    const db=mysql.createPool({
        host:'127.0.0.1', 	
        user:'root',		
        password:'root',	
        database:'my_db_01',	
    })
    // 查询表中所有的数据
    const sqlstr='select * from users'
    db.query(sqlstr,(err,results)=>{
        if(err) return console.log(err.message)
        console.log(results)
        //执行查询语句,返回的是数组
    
    • 插入数据语句:INSERT INTO
    const mysql=require('mysql')
    const db=mysql.createPool({
        host:'127.0.0.1', 	
        user:'root',		
        password:'root',	
        database:'my_db_01',	
    })
    const user= {name:'小花',password:'123'}
    // 定义待执行的SQL语句,	SQL语句中可以使用?占位符进行展位
    const sqlstr='INSERT INTO users(name,password) VALUES(?,?)'
    // 执行SQL语句
    db.query(sqlstr,[user.name,user.password],(err,results)=>{
        if(err) return console.log(err.message)
        // 判断插是否入成功,执行INSERT INTO语句,results则是一个对象,可以使用results.affectedRows属性,判断是否插入成功
        if(results.affectedRows ===1 ){console.log('数据插入成功')}
    })
    
    // 插入数据的便捷方式
    const user= {name:'小花1',password:'123'}
    const sqlstr='INSERT INTO users SET ?'
    db.query(sqlstr,user,(err,results)=>{
        if(err) return console.log(err.message)    
        if(results.affectedRows ===1 ){console.log('数据插入成功')}
    })
    
    • 更新数据:UPDATE users SET
    const mysql=require('mysql')
    const db=mysql.createPool({
        host:'127.0.0.1', 	
        user:'root',		
        password:'root',	
        database:'my_db_01',	
    })
    const user= {id:16,name:'小花哗啦啦',password:'000000'}
    const sqlstr='UPDATE users SET name=?,password=? WHERE id=?'
    db.query(sqlstr,[user.name,user.password,user.id],(err,results)=>{
        if(err) return console.log(err.message)
        if(results.affectedRows ===1 ){console.log('数据插入成功')}
    })
    
    // 更新数据的便捷方式
    const user= {id:16,name:'啦啦',password:'111111'}
    const sqlstr='UPDATE users SET ? WHERE id=?'
    db.query(sqlstr,[user,user.id],(err,results)=>{
        if(err) return console.log(err.message)    
        if(results.affectedRows ===1 ){console.log('数据插入成功')}
    })
    
    
    • 删除数据语句:

      const sqlstr='DELETE FROM users WHERE id=?'
      db.query(sqlstr,16,(err,results)=>{
          if(err) return console.log(err.message)    
          if(results.affectedRows ===1 ){console.log('数据插入成功')}
      })
      
      // 标记删除,指定一个字段,通过0和1表示删除与否,比直接删除更安全,
      const sqlstr='UPDATE users SET status=? WHERE id=?'
      db.query(sqlstr,[1,9],(err,results)=>{
          if(err) return console.log(err.message)    
          if(results.affectedRows ===1 ){console.log('标记删除成功')}
      })
      

8. 前后端的身份认证

8.1 目前主流的Web开发模式

8.1.1 基于服务端渲染的传统Web开发模式

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

app.get('index.html',(req,res)=>{
    const user={name:'zs',age='10'}
    const html='<h1>姓名:${user.name},年龄:${user.age}</h1>'
    res.send(html)
})

优点:

  1. 前端耗时少。浏览器只负责渲染,更节能。
  2. 有利于SEO。客户端接收的是完整的HTML内容,方便爬虫获取信息

缺点:

  1. 占用服务器资源,造成服务器的访问压力。

  2. 不利于前后端分离,开发效率低。

8.1.2 基于前端渲染的新型Web开发模式

前后端分离的开发模式,依赖于Ajax技术的广泛应用。后端只负责提供API接口,前端使用Ajax调用接口的模式

优点:

  1. 开发体验好。前端专注于UI页面的开发,后端专注于API的开发,且前端有更多的选择性。
  2. 用户体验好。Ajax技术可以实现页面的局部刷新,极大地提高了用户的体验。
  3. 减轻了服务器的渲染压力。

缺点:

  1. 不利于SEO。html内容需要在客户端拼接,所以爬虫无法获取完整有效内容。(通过Vue、Reaxt等前端框架的SSR(sever side render)技术能够很好的解决SEO问题)
7.5.1.3 如何选择Web开发模式

服务端渲染:需要良好的SEO,没有复杂的交互。

前端渲染:交互性强,不考虑SEO,如:后台管理系统。

服务端渲染+前端渲染:根据实际需求而定,如:首页服务端渲染+其他页面前端渲染。

8.2 身份认证

身份认证又称为身份验证,鉴权,是通过一定的手段对用户身份的确认。如:邮箱密码验证,手机验证码验证,二维码验证等。

开发模式不同认证方案也不一样:

  • 服务端渲染推荐Session认证机制
  • 前端渲染推荐使用JWT认证机制
8.2.1 Session认证机制
  1. HTTP协议的无状态性

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

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

Cookie是存储在用户浏览器中的一段不超过4KB的字符串,它是由Name,Value和他他几个用于控制cookie有效期,安全性,适用范围的可选属性组成。

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

Cookie特性:

  • 自动发送

  • 域名独立

  • 4KB限制

  • 存在有效期

  1. 在身份认证中的作用

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

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

  1. Cookie没有安全性密码等身份信息禁止存放在Cookie

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

8.2.2 在Express中使用Session
  1. 配置express-session中间件

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

const express=require('express')
const app=express
// 1.导入session中间件
const session=require('express-session')
// 2.配置session中间件
app.use(session({
    secret:'keyboard cat',	//该属性的值可以为任意字符串
    resave:false,			//固定写法    	
    saveUninitialized:true  //固定写法
}))
  1. 向session中存储数据

    express-session中间件配置成功后,就能通过req.session来访问session对象,从而存储用户的关键信息;

const express=require('express')
const app=express
// 1.导入session中间件
const session=require('express-session')
// 2.配置session中间件
app.use(session({
    secret:'keyboard cat',	//该属性的值可以为任意字符串
    resave:false,			//固定写法    	
    saveUninitialized:true  //固定写法
}))

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

// 02:将登录成功后的信息保存到Session中
// 只有配置session中间件后req.session属性才有效
 req.session.user=req.body  //用户信息
 req.session.islogin=true   //用户登陆状态

 res.send({status:0,msg:'登陆成功'})
})

// 获取用户名的接口
app.get('/api/username',(req,res)=>{
    // 03: 从session中获取用户名称,响应给客户端
    if(!req.session.islogin){
        return res.send({status:1,msg:'fail'})
    }
    res.send({
        status:0,
        msg:'success',
        username: req.session.user.username,
    })

//  退出登录接口
app.post('/api/logout',(req,res)=>{
    // 04:清空session信息
    // req.session.destroy()函数仅清空当前用户的session信息
    req.session.destroy()
    res.send({status:0,msg:'退出登录成功',
    })
})
// 调用app.listen方法,指定端口号,并启动服务器
app.listen(80,()=>{console.log('服务器已启动:http://127.0.0.1:80')})
})
8.2.3 JWT认证原理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pTTGZAyb-1674715884119)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221114204018721.png)]

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

JWT的组成部分

JWT通常由三部分组成:分别是Header(头部)、Payload(有效荷载)、Signature(签名)。

三者之间使用英文的“.”分隔,格式如下:

Header.Payload.Signature

JWT的三部分各自代表什么含义:

  • Payload部分才是真正的用户信息,他是用户信息经过加密之后生成的字符串
  • Header和Signature是安全相关的部分,只是为了保证Token的安全性

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fieIFKvq-1674715884148)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221114205136404.png)]

8.2.4 在Express中使用JWT

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-00paV506-1674715884161)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221114205451783.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UO3IpItE-1674715884167)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221114205614125.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fQUqEjuK-1674715884173)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221114210004048.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bRx8RRvt-1674715884181)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221114210323597.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ll0CwRkp-1674715884185)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221114210915032.png)]

注意:执教配置成功了expressJWT中间件,就以可以把解析出来的用户信息挂载到req.use属性上

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hzTbXsC6-1674715884190)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221114211827807.png)]
以为任意字符串
resave:false, //固定写法
saveUninitialized:true //固定写法
}))




2. 向session中存储数据

express-session中间件配置成功后,就能通过req.session来访问session对象,从而存储用户的关键信息;



```js
const express=require('express')
const app=express
// 1.导入session中间件
const session=require('express-session')
// 2.配置session中间件
app.use(session({
 secret:'keyboard cat',	//该属性的值可以为任意字符串
 resave:false,			//固定写法    	
 saveUninitialized:true  //固定写法
}))

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

// 02:将登录成功后的信息保存到Session中
// 只有配置session中间件后req.session属性才有效
req.session.user=req.body  //用户信息
req.session.islogin=true   //用户登陆状态

res.send({status:0,msg:'登陆成功'})
})

// 获取用户名的接口
app.get('/api/username',(req,res)=>{
 // 03: 从session中获取用户名称,响应给客户端
 if(!req.session.islogin){
     return res.send({status:1,msg:'fail'})
 }
 res.send({
     status:0,
     msg:'success',
     username: req.session.user.username,
 })

//  退出登录接口
app.post('/api/logout',(req,res)=>{
 // 04:清空session信息
 // req.session.destroy()函数仅清空当前用户的session信息
 req.session.destroy()
 res.send({status:0,msg:'退出登录成功',
 })
})
// 调用app.listen方法,指定端口号,并启动服务器
app.listen(80,()=>{console.log('服务器已启动:http://127.0.0.1:80')})
})
8.2.3 JWT认证原理

[外链图片转存中…(img-pTTGZAyb-1674715884119)]

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

JWT的组成部分

JWT通常由三部分组成:分别是Header(头部)、Payload(有效荷载)、Signature(签名)。

三者之间使用英文的“.”分隔,格式如下:

Header.Payload.Signature

JWT的三部分各自代表什么含义:

  • Payload部分才是真正的用户信息,他是用户信息经过加密之后生成的字符串
  • Header和Signature是安全相关的部分,只是为了保证Token的安全性

[外链图片转存中…(img-fieIFKvq-1674715884148)]

8.2.4 在Express中使用JWT

[外链图片转存中…(img-00paV506-1674715884161)]

[外链图片转存中…(img-UO3IpItE-1674715884167)]

[外链图片转存中…(img-fQUqEjuK-1674715884173)]

[外链图片转存中…(img-bRx8RRvt-1674715884181)]

[外链图片转存中…(img-ll0CwRkp-1674715884185)]

注意:执教配置成功了expressJWT中间件,就以可以把解析出来的用户信息挂载到req.use属性上

[外链图片转存中…(img-hzTbXsC6-1674715884190)]

### Node.js 学习笔记与教程 #### 初识 Node.js 和其核心概念 Node.js 是一种基于 Chrome V8 引擎构建的 JavaScript 运行环境,允许开发者使用 JavaScript 创建高性能服务器端应用程序。它通过事件驱动 I/O 模型提供高效的性能表现[^1]。 在浏览器环境中,JavaScript 的组成主要包括 ECMAScript 标准、DOM(文档对象模型)、BOM(浏览器对象模型),以及一些特定于浏览器的功能扩展。而在 Node.js 中,则引入了诸如 `fs` 文件系统模块、`http` HTTP 请求处理模块等内置工具来补充功能需求。 #### 内置模块介绍 以下是几个常用的 Node.js 内置模块及其基本用途: - **FS 模块**: 提供文件系统的访问能力,可以实现同步或异步方式下的文件读取/写入操作。 ```javascript const fs = require('fs'); // 同步读取文件内容 let data = fs.readFileSync('./example.txt', 'utf8'); console.log(data); ``` - **Path 模块**: 处理并转换文件路径字符串。 ```javascript const path = require('path'); let filePath = path.join(__dirname, 'subdir', 'file.txt'); console.log(filePath); ``` - **HTTP 模块**: 构建 Web 服务的基础组件之一。 ```javascript const http = require('http'); const server = http.createServer((req, res) => { res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Hello World\n'); }); server.listen(3000, 'localhost'); console.log('Server running at http://localhost:3000/'); ``` #### 模块化开发 为了更好地组织代码结构,在 Node.js 开发过程中推荐采用模块化的编程方法。目前主流有两种规范被广泛接受和支持——CommonJS 和 ES Modules (ECMAScript Module)[^3]。 对于 CommonJS 来说,默认情况下每个 `.js` 文件都是独立的一个模块;如果想让其他地方能够调用当前定义好的函数或者变量的话就需要显式地将其暴露出去(`module.exports`) 并且由另一方接收 (`require()`) 。而对于 ES6+ 版本则提供了更加简洁优雅的方式来进行默认导出(default export)/命名导出(named exports),并通过 import 关键字完成相应加载过程。 #### NPM 工具链概述 NPM 不仅作为全球最大的开源库生态系统存在,同时也是 Node.js 应用程序的主要包管理解决方案。利用 npm 我们不仅可以轻松安装第三方依赖项还可以发布自己的作品到社区共享给更多的人群受益。 另外值得一提的是 nodemon —— 一款非常实用的小插件用于监控源码变动自动重启应用进程从而极大提升调试效率。 ```bash # 安装 nodemon npm install --save-dev nodemon # 使用 nodemon 启动项目 npx nodemon app.js ``` #### 部署至云端平台 当本地测试完成后可以选择合适的服务提供商将自己的站点部署上线运行起来。比如 Heroku 就是非常受欢迎的选择因为它简单易用而且免费额度足以满足初期阶段的需求[^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值