阶段一
1 初始Node.js

javascript 运行环境

1.2 Node.js中的javacript 运行环境


1.3 Node.js环境安装
百度
1.4 node.js 执行javaScript 代码


2 fs文件系统模块
2.1 fs文件系统模块概念

导入文件系统模块:
const fs = require('fs')
-  fs.readFile() // 1 导入fs文件系统模块 const fs = require('fs') // 2 调用 fs.readFile() 方法读取文件 fs.readFile('text.txt','utf8',function(err,res){ // 打印结果:1 读取成功 err值为 null // 2 读取失败 err值为错误对象 res值为undefined console.log(err) console.log('----------') console.log(res) })效果:  判断文件读取成功还是失败(err.message) const fs = require('fs') fs.readFile('text.txt','utf8',function(err,res){ if(err) { console.log("读取文件失败!"+err.message) }else { console.log("读取文件成功!"+res) } })
-  fs.writeFile() const fs = require('fs') const str = '在长城 黄河' fs.writeFile('tex.txt',str,'utf8',function(err){ if(err) { console.log('写入失败!'+err.message) }else { console.log('写入成功!') } })
2.2 案例: 文件中成绩整理
score.txt 文件中:
小明=92 小兰=99 小绿=87 小马=77 小环=45
const fs = require('fs')
fs.readFile('score.txt','utf8',function(err,res) {
  if(err) {
    console.log('文件获取失败!',err.message)
  }
  console.log(res)
  let oldScore = res.split(' ')
  let newScore = []
  oldScore.forEach(item => {
    newScore.push(item.replace('=',':'))
  })
  newScore = newScore.join("/t/n")
  console.log(newScore)
  fs.writeFile('score.txt',newScore,function(err) {
    if(err){
      console.log('写入失败!'+err.message)
    }
    console.log('写入成功!')
  })
})
效果:

2.3 路径拼接问题


注意:在js中,/ 是转义字符的意思
直接提供完整路径,缺点:移植性非常差,不利于维护
解决:__dirname (表示当前文件所处的目录)

示例:
const fs = require('fs')
console.log(__dirname)
fs.readFile(__dirname+'//text.txt','utf8',function(err,res){
  if(err) {
    console.log("读取文件失败!"+err.message)
  }else {
    console.log("读取文件成功!"+res)
  }
})
3 path 路径模块
3.1 puth 路径模块概念

 path.extname()方法,获取路径中的扩展名
示例 1- path.join():
const path = require('path')
// 将多个路径片段拼接在一起
const pathStr = path.join('/a','/b/c', '../','/d','e')    // /a/b/d/e
console.log(pathStr)
const pathStr2 = path.join(__dirname, './files/1.txt')
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9Ghzs9vA-1673585563871)(null)]
示例2:path.basename()
const path = require('path')
const fpath ='/a/b/c/index.html'
var fullName = path.basename(fpath)
console.log(fullName) // 输出 index.html
var nametext = path.basename(fpath,'.html') // 输出 index
console.log(nametext)
示例3:path.extname - 获取路径中的扩展名
const path = require('path')
const fpath ='/a/b/c/index.html'
const fext = path.extname(fpath)  // .html
console.log(fext)
3.2 综合案例 - 页面拆分

// 导入fs 模块
const fs = require('fs')
// 导入 path 模块
const path = require('path')
// 使用正则表达式 分别匹配 标签 /s - 表示空白字符 /S - 表示非空白字符 
//  * - 表示任意匹配
const regStyle = /<style>[/s/S]*<//style>/
const resScript = /<script>[/s/S]*<//script>/
// 导出  模块
module.exports.separate = function(filename) {
  fs.readFile(path.join(__dirname,filename),'utf8',function(err,res) {
    if(err) {
      console.log('文件打开失败!',err.message)
    }else {
      // console.log('文件打开成功!')
      resolveCss(res)
      resolveJs(res)
      resolveHtml(res)
    }
  })
}
function resolveCss(str) {
  let regStr =regStyle.exec(str)
  // console.log(regStr[0].replace('<style>','').replace('</style>',''))
  let newRegStr = regStr[0].replace('<style>','').replace('</style>','')
  fs.writeFile(path.join(__dirname,'/indexItem/indexCss.css'),newRegStr,'utf8',function(err){
    if(err)
      console.log('写入失败',err.message)
    // else
      // console.log('写入成功')
  })
}
function resolveJs(str) {
  let regStr = resScript.exec(str)
  let newRegStr = regStr[0].replace('<script>','').replace('</script>','')
  fs.writeFile(path.join(__dirname,'/indexItem/indexJs.js'),newRegStr,'utf8',function(err){
    if(err)
      console.log('写入失败',err.message)
    // else
      // console.log('写入成功')
  })
}
function resolveHtml(str) {
  let replaceStr = str.replace(regStyle,'<link rel="stylesheet" href="indexCss.css">').replace(resScript,'<script src="indexJs.js"></script>')
  fs.writeFile(path.join(__dirname,'/indexItem/indexHtml.html'),replaceStr,'utf8',function(err){
    if(err)
      console.log('写入失败',err.message)
    // else
    //   console.log('写入成功')
  })
}
4 http 模块(一个web服务)
4.1 http的概念

const http = require('http')
http.createServer()
4.2 创建基本web服务器
// 1 导入 http 模块
const http = require('http')
// 2 创建 web 服务器实例
const server = http.createServer()
// 3 为服务器实例绑定request 事件 监听客户端的请求
server.on('request',function(req,res){
  console.log('someone visit out web server')
})
// 4 启动服务器
server.listen(8080,function(){
  console.log('server running at http://127.0.0.1:8080')
})
req 是请求的对象 包含与客户端相关的数据或属性
示例:
const http = require('http')
const server = http.createServer()
server.on('request',(req,res) => {
   // req.url 是客户端请求的URL地址
   // req.method 是客户端的method 请求类型
  const str = `your url is ${req.url}, method is ${req.method}`
  console.log( str)
})
server.listen(80,()=> {
  console.log('server at http://127.0.0.1')
})
res 响应对象 res.end()
示例:
const http = require('http')
const server = http.createServer()
server.on('request',(req,res) => {
  const str = `your url is ${req.url}, method is ${req.method}`
  console.log( str)
  // 调用 res.end 响应客户端并发送数据
  // const data = {
  //   id: 11011,
  //   name: 'liming',
  //   age: 18,
  //   address: 'wenli'
  // }
  res.end('test api')
})
server.listen(80,()=> {
  console.log('server at http://127.0.0.1')
})
4.3 解决res响应对象时,中文乱码
手动设置编码格式
示例:
const http = require('http')
const server = http.createServer()
server.on('request',(req,res) => {
  const str =  `url 是 ${req.url}, method 是 ${req.method}`
  // 防止中文乱码 设置响应头 'Content-type','text/html; charset=utf-8'
  res.setHeader('Content-type','text/html; charset=utf-8')
  res.end(str)
})
server.listen(80,()=> {
  console.log('at http://127.0.0.1')
})
4.4 根据不同的url响应不同的html内容
示例:
const http = require('http')
const server = http.createServer()
server.on('request',(req,res) => {
  const url = req.url
  console.log(url)
  let content = '<h2>404 not found!</h2>'
  if(url == '/' || url == '/index.html'){
    content = '<h2>首页</h2>'
  }else if(url == '/about.html') {
    content = '<h2>关于</h2>'
  }
  res.end(content)
})
server.listen(80,()=> {
  console.log('at http://127.0.0.1')
})
4.5 web服务器案例 - 优化资源请求
const http = require('http')
const fs = require('fs')
const path = require('path')
const server = http.createServer()
server.on('request',(req,res)=> {
  const url = req.url
  // /index.html /
  // const fpath = path.join(__dirname,url)
  let fpath = ''
  if(url === '/'){
    fpath = path.join(__dirname,'/indexItem/indexHtml.html')
  } else {
    // 重点 拼接成请求路径
    fpath = path.join(__dirname,'/indexItem'+url) 
  }
  // console.log(fpath)
  fs.readFile(fpath,'utf8',(err,dataStr)=>{
    if(err) 
      return res.end('404 not found !')
    else
      res.end(dataStr)
  })
})
server.listen(80,()=> {
  console.log('at http://127.0.0.1')
})
4.5 模块化 - common.js
4.5.1 模块化的定义

4.5.2 node.js 中的模块化
加载模块


模块作用域

模块作用域的好处
防止全局变量污染
 
每个模块都有一个 module 对象

笔记: 在外界使用require 导入一个自定义模块的时候,得到的成员,就是那个模块中,通过module.exports 指向的那个对象
4.5.3 在module.exports 对象上挂载属性和方法
写法1 (不建议)
const username = "lisi"
const age = 18
module.exports.sayme = function () {
  console.log('my name is' + username)
}
module.exports.age = age
效果:

写法2 (建议)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y8lrUmTe-1673585563131)(null)]
module.exports = {
  username : 'zhanghua',
  sayme() {
    console.log('my name is '+ this.username)
  }
}
4.5.4 exports 与 module.exports指向的对象
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uB2NPAJ6-1673585564158)(null)]
// 不行
// exports = {
//   username : 'zhanghua',
//   sayme() {
//     console.log('my name is '+ this.username)
//   }
// }
// 可以
const username = 'wangliu'
exports.username = username
exports.sayme = function () {
  console.log("my name is " + username)
}
注意:
exports 与 module.exports的使用误区?
误区1 :
exports.username = 'zs'
module.exports = {
  gender: '男',
  age:18
}
效果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5PDacLwK-1673585564042)(null)]
因为引用数据类型,地址指向问题
误区3
exports.username = 'ls'
exports.age= 19
效果:

原因:没有定义引用数据类型,不会改变地址
误区4
exports = {
  username : 'zs',
  gender: "女"
}
module.exports = exports
module.exports.age = 22
效果: 
4.5.5 node.js 模块化规范
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FSi5lLMu-1673585563536)(null)]
问题:为什么nodejs不支持import语法
nodejs不支持import语句,原因:nodejs采用的是CommonJS的模块化规范,使用require语句引入模块;而import是ES6的模块化规范关键字。
解决:babel的安装
阶段二
1 npm与包
概念:

介绍:

1.2 npm 导入moment包
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ABnhg984-1673585567860)(null)]
-  npm install moment // 安装包
1.3 包的语义化管理规范
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WU3Gr0UQ-1673585560542)(C:/Users/aaa/Desktop/image-20221204124035714.png)]
1.4 包的分类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jVTJ65ug-1673585560542)(C:/Users/aaa/Desktop/image-20221204124251013.png)]
npm intall 包名 -D 	// devDependencies
npm intall 包名		// dependencies 
全局包
npm intall 包名 -g
npm unintall 包名 -g		// 卸载包
1.5 推荐包(i5ting_toc) - xx.md文件转 xx.html 件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L3yc0r8l-1673585567253)(null)]

1.6 规范的包内部结构

1.7 开发自己的包
-  文件结构 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bnKoCeZs-1673585563679)(null)] 
-  文件中代码结 package.json { "name": "codexxx-tools", "version": "1.0.0", "main": "index.js", "description": "提供格式化时间, htmlEscape相关功能", "keywords": [ "codexxx", "dateFormat", "escape" ], "license": "ISC" }index.js // 格式化时间 function dateFormat(dataStr) { const dt = new Date(dataStr) const y = padZero(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 } // html 转义处理 function htmlEscape(str) { return str.replace(/<|>|"|&/g, (match)=> { switch(match) { case '<': return '<' case '>': return '>' case '"': return '"' case '&': return '&' } }) } function htmlUnEscape(str) { return str.replace(/<|>|"|&/g, (match)=> { switch(match) { case '<': return '<' case '>': return '>' case '"': return '"' case '&': return '&' } }) } // 导出 module.exports = { dateFormat, htmlEscape, htmlUnEscape }
-  分模块  使用:…  const date = require('./src/dateFormat.js') const escape = require('./src/htmlEscape.js') module.exports = { ...date, ...escape }
-  编写包的开源文档 README.md ## 安装 ``` npm install codexxx-tools ``` ## 导入 ```js const codexxx = require('codexxx-tools') ``` ## 格式化时间 ```js // 调用 dateFormat 格式化时间 const dt = codexx.dateFormat(new Date()) // 结果 2022-12-04 23:19:50 console.log(dt) ``` ## 转义 html 中的特殊字符 ```js // 待转义的字符 const str = '<h2>hello world !</h2>' // 调用 htmlEscape 转义字符 const newStr = codexx.htmlEscape(str) // 结果 <h2>hello world !</h2> console.log(newStr) ``` ## 还原 html 中的特殊字符 ```js // 待还原的字符 const newStr = codexx.htmlEscape(str) // 结果 <h2>hello world !</h2> console.log(codexx.htmlUnEscape(newStr)) ``` ## 开源协议 ISC
1.8 发布包

1.8.1 发布包 删除包
百度
1.9 模块的加载机制
1.9.1 优先从缓存中加载

1.9.2 内置模块的加载机制

1.9.3 自定义模块的加载机制

1.9.4 第三方模块的加载机制
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mKXVFFSH-1673585567759)(null)]
1.9.5 把目录作为模块加载

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J4DR6Xyq-1673585567921)(null)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3FNflaXo-1673585564223)(null)]
阶段三:Express
1 express 的初步使用

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6uwcrPoP-1673585568020)(null)]
安装
npm install express
1.2 express 的使用
1.2.1 创建 web 服务器
// 1 导入 express
const express = require('express')
// 2 创建 web 服务器
const app = express()
// 3 调用 app.listen(端口号 启动成功后返回回调函数)
app.listen(80,()=> {
  console.log('express server runing at http://127.0.0.1')
})
1.2.2 发起get和post请求
// 1 导入 express
const express = require('express')
// 2 创建 web 服务器
const app = express()
// 4 监听客户端 get 和 post 请求, 并向客户端响应数据
app.get('/user',(req, res)=> {
  // 调用 express 提供的res.send() 方法, 向客户端 响应一个json 对象 或 文本字符串
  res.send({name: 'zs', age: 20,gender: '男'})
})
app.post('/user',(req, res) => {
  res.send('请求成功')
})
// 3 调用 app.listen(端口号 启动成功后返回回调函数)
app.listen(80,()=> {
  console.log('express server runing at http://127.0.0.1')
})
1.2.3 获取url中携带的查询参数 - req.query
// url 是 http://127.0.0.1/user?username=aaa&age=18
app.post('/user',(req, res) => {
  console.log(req.query) // { username: 'aaa', age: '18' }
  res.send('请求成功')
})
1.2.4 获取url中携带的动态参数 - req.params
// 可以有多个动态参数
app.get('/user/:uname/:id',(req, res)=> {
  
  res.send(req.params)
})
1.3 托管静态资源 - express.static()
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UgaPNtTe-1673585564830)(null)]
app.use(express.static('./indexItem'))
// express.static('路径') 方法中的路径不会出现在客户端的访问路径中
==注意:==自动托管文件夹,并在其目录下自动寻找index.html/htm 等文件
1.3.2托管多个静态资源目录
app.use(express.static('./indexItem'))
app.use(express.static('./images'))
// 直接重开一行 写就行

1.3.3 挂载路径前缀
app.use('/images',express.static('./images'))
1.4 nodemon 的使用
1.4 介绍
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J0vJ3j71-1673585567352)(null)]
安装
npm install nodemon -g
使用
// 终端中
nodemon xx.js
2 express 路由
2.1 路由概念
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Lysk2um4-1673585564514)(null)]
// 示例
app.post('/user',(req, res) => {})
2.2 路由的使用
2.2.1 路由最简单的使用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Op9N9JGO-1673585564337)(null)]
// 挂载路由
app.post('/user',(req, res) => {})
2.2.2 重点 - 模块化路由

示例:

示例代码:
文件结构

// login.js
const express = require('express')
const router = express.Router()
router.get('/login', (req, res) => {
  res.send('welcome use API !')
})
router.post('/login', (req, res) => {
  res.send('login success !')
})
module.exports = router
// api.js
const express = require('express')
const app = express()
// 注册路由模块
const login = require('./login')
// 导入路由模块
app.use(login)
// app.use() 函数的作用 注册全局中间件
app.listen(80,()=> {
  console.log('at 127.0.0.1')
})
**注意:**为模块统一添加前缀
// 注册路由模块
const login = require('./login')
// 导入路由模块
app.use('/api',login)
// app.use() 函数的作用 注册全局中间件
3 express 中间件
3.1 中间件的概念
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7CsjRY2F-1673585564402)(null)]
3.1.2 express中间件的格式
app.get('/',(req, res, next)=>{
	next()
})

3.1.3 中间件 - next 函数的作用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yEbyxvP8-1673585563979)(null)]

3.2 expres 中间件的使用
3.2.1 全局生效的中间件
附:定义注册中间件 简化写法
示例:
const express = require('express')
const app = express()
// 定义中间件
const mw = function(req,res,next) {
  console.log('中间件')
  next()
}
// 将 mw 注册为全局生效的中间件
app.use(mw)
// 注册中间件 简化写法
//app.use(function(req,res,next) {
//  console.log('中间件')
//  next()
//})
app.get('/',(req,res)=> {
  console.log('/ 路由')
  res.send('index page')
})
app.get('/login',(req,res)=> {
  console.log('/login 路由')
  res.send('login page')
})
app.listen(80,()=> {
  console.log('at http://127.0.0.1')
})

3.2.2 中间件的作用

......
// 定义中间件
const mw = function(req,res,next) {
  console.log('中间件')
  const dt = new Date()
  // req 上添加属性
  req.newTime = dt
  next()
}
// 将 mw 注册为全局生效的中间件
app.use(mw)
......
3.2.3 定义多个全局中间件
// 定义多个全局中间件
app.use((req,res,next)=> {
	console.log('第一个中间件')
	next()
})
app.use((req,res,next)=> {
	console.log('第二个中间件')
	next()
})

3.2.4 局部生效的中件件
// 局部中间件
const mw1 = (req, res, next) => {
  console.log('中间件')
  next()
}
// 使用局部中间件
app.get('/',mw1,(req, res,next)=> {
  res.send('test file')
})
3.2.5 定义多个局部中间件
注册中间件的等价写法
// 局部中间件
const mw1 = (req, res, next) => {
  console.log('中间件')
  // 一定要调用 next() 执行下一个中间件
  next()
}
// 多个局部中间件
const mw2 = (req,res,next) => {
  console.log('中间件2')
  next()
}
// 使用局部中间件
// 等价写法
// app.get('/',[mw1,mw2],(req, res,next)=> {
//  res.send('test file')
//})
app.get('/',mw1,mw2,(req, res,next)=> {
  res.send('test file')
})
3.2.6 中间件的注意事项

3.3 中间件的分类
express 官方把常见的中间件分为五大类:

3.3.1 应用级别中间件:

3.3.2 路由级别中间件:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sACABCuR-1673585563365)(null)]
var app = express()
var router = experss.Router()
// 路由级别的中间件
router.use((req,res,next)=> {
	console.log('Time',Date.now())
	next()
})
app.use('/',router)
3.3.3 错误级别的中间件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-krFPfWQ9-1673585563811)(null)]
app.get('/',function(req,res){
  // 人为制造错误
  throw new Error('人为服务器错误')
  res.send('home page')
})
app.use((err,req,res,next)=>{
  console.log('发生错误:' + err.message)
  res.send('Eorror' + err.message)
})
注意:错误级别的中间件,必须注册在所有路由之后
3.3.4 express 内置的中间件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OolqxvPz-1673585563471)(null)]
// 配置 application/json 格式数据的内置中间件
app.use(express.json())
// 配置解析 application/x-www-form-urlencoded 格式数据的内置中间件
app.use(express.urlencoded({extended: false}))
express.json 的使用
在软件,模拟发起post请求:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cr25NIWt-1673585568184)(null)]
**注意:**默认情况下 如果不解析 表单数据的中间件 则 res.body 等于 undefined
// 通过 express.json 中间件, 解析 json 格式表单数据 
app.use(express.json())
app.post('/',(req,res,next)=> {
  // 默认情况下 如果不解析 表单数据的中间件 则 res.body 等于 undefined
  console.log(req.body)
  res.send('ok')
})
express.urlencoded({extended: false}) 的使用
app.use(express.urlencoded({extended: false}))
app.post('/',(req,res,next)=> {
  // 默认情况下 如果不解析 表单数据的中间件 则 res.body 等于 undefined
  console.log(req.body)
  res.send('ok')
})
3.3.5 第三方中间件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oqLX5Pbt-1673585567411)(null)]

3.4 自定义中间件
3.4.1 需求与步骤

3.4.2 自定义中间件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZszEPNdm-1673585564573)(null)]
// 解析表单数据的中间件
app.use((req,res,next)=> {
  // 具体
})
3.4.3 监听req的data事件

// 定义变量 用来存客户端发送过来的请求数据
  let str = ''
  // 监听 req 对象的 data 事件(客户端发送的新的请求数据)
  req.on('data', (chunk) => {
    // 拼接请求体数据 隐式转换为字符串
    str += chunk
  })
3.4.4 监听req的end事件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1kmZ4WK3-1673585565102)(null)]
   // 监听 req 对象的 end 事件 (请求体发送数据完成后自动触发)
  req.on('end',()=>{
    // 打印完整的请求数据
    console.log(str)  // 结果: bookname=%E7%9C&author=%E6%
  })
3.4.5 querystrign 解析请求体数据

问题:“querystring”已弃用
解决: Nodejs提取网址参数,解决“querystring”已弃用

// 导入处理 querystring 
const qs = require('querystring');
......
// 调用 qs.parse() 方法 把查询字符串解析为对象
    const body = qs.parse(str)
    console.log(body)
3.4.6 将解析出来的数据挂载在 req.body
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bevadg3J-1673585564992)(null)]
 // 调用 qs.parse() 方法 把查询字符串解析为对象
    const body = qs.parse(str)
    // console.log(body)
    // 将数据挂载在req.body 
    req.body = body
.....
app.post('/user',(req,res,next)=> {
  // req 对象的数据
  // console.log(req.body)
  res.send(req.body)
})
3.4.7 自定义中间件封装为独立模块

补充(未分模块的代码):
const express = require('express')
// 导入处理 querystring 
const qs = require('querystring');
const app = express()
// 解析表单数据的中间件
app.use((req, res, next) => {
  // 具体
  // 定义变量 用来存客户端发送过来的请求数据
  let str = ''
  // 监听 req 对象的 data 事件(客户端发送的新的请求数据)
  req.on('data', (chunk) => {
    // 拼接请求体数据 隐式转换为字符串
    str += chunk
  })
  // 监听 req 对象的 end 事件 (请求体发送数据完成后自动触发)
  req.on('end', () => {
    // 打印完整的请求数据
    // console.log(str) // 结果: bookname=%E7%9C&author=%E6%
    // ps : 
    // 调用 qs.parse() 方法 把查询字符串解析为对象
    const body = qs.parse(str)
    // console.log(body)
    // 将数据挂载在req.body 
    req.body = body
    next()
  })
})
app.post('/user',(req,res,next)=> {
  // req 对象的数据
  // console.log(req.body)
  res.send(req.body)
})
app.listen(80, () => {
  console.log('at http://127.0.0.1')
})
4 使用 express 写接口
4.1/2/3/4 express基本创建
-  创建基本服务器 // api.js 文件 const express = require('express') const app = express() app.use(express.urlencoded({extended: false})) // 导入路由模块 const login = require('./login') // 注册路由模块 app.use('/api',login) app.listen(80,()=>{ console.log('server at http://127.0.0.1') })
-  路由模块 // login.js 文件 const express = require('express') const apiRouter = express.Router() // 挂载对应的路由 // get 请求 apiRouter.get('/login',(req,res,next)=> { const query = req.query res.send({ status: 200, msg: 'get 请求成功!', data: query }) }) apiRouter.post('/login',(req,res,next)=> { const body = req.body res.send({ status: 200, msg: 'post 请求成功!', data: body }) }) module.exports = apiRouter
4.5 CORS 跨域资源共享
解决跨域问题:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2qbu5fVP-1673585567579)(null)]
4.5.2 使用

4.5.3 cors 介绍
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hLtgjeMb-1673585563191)(null)]
4.5.4 cors 注意事项

4.5.5 cors 响应头部 Access-Control-Allow- Origin

4.5.6 cors 响应头部 Access-Control-Allow- Headers

4.5.7 cors 响应头部 Access-Control-Allow- Methods
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BHe1jPZL-1673585568082)(null)]
4.5.8 cors 请求的分类
简单请求:

预检请求:

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

示例:
// login.html
...
// delete 请求
    $('.deleteBtn').on('click',function(){
      $.ajax({
        type: 'delete',
        url: 'http://127.0.0.1/api/login',
        // data: {data:'zs',age:18},
        success: function(res){
          console.log(res)
        }
      })
    })
...
// login.js
...
apiRouter.delete('/login',(req,res,next)=> {
  res.send({
    status: 200,
    msg: 'delete 请求成功!'
  })
})
...
效果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5kqmf2Cu-1673585564930)(null)]

4.6 JSONP的概念与特点及使用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2mojxgDi-1673585565784)(null)]
4.6.2 创建JSONP接口的注意事项

4.6.3 实现JSONP接口的具体代码

4.6.4 在jquery 中 发起jsonp请求

阶段四
4 在项目中操作mysql
4.1/2 安装与配置mysql 模块
-  安装 npm install mysql
-  配置 // 1 导入 mysql 模块 const mysql = require('mysql') // 2 建立与 mysql 之间的关系 const db = mysql.createPool({ host: '127.0.0.1', // 数据库的ip user: 'root', password: '123', database: 'my_db' })
-  测试mysql 模块是否工作正常 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pfwmf9zo-1673585567518)(null)] db.query('select 1',(err,res)=> { // 如果模块工作期间报错 if(err) return console.log(err.message) // 成功 console.log(res) // [ { '1': 1 } ] })==注意:==node.js 连接 mysql 8 失败问题 解决:Node.js 连接Mysql 8.0 (2)、在node项目中更换mysql连接器 npm un mysql && npm i mysql2 // 导入myslq模块 const mysql = require('mysql2')
4.3 使用MySQL操作数据库
4.3.1 查询数据
  const sqlStr = 'select * from users'
  db.query(sqlStr,(err,res)=> {
    if(err) return console.log(err.message)
    console.log(res)
  })
4.3.2 插入数据
方式一
  // 1 要插入到user 表中的数据对象
  const user = {username: 'xiaoming', password: '123123'}
  // 2 待执行 sql 语句 , 其中英文 ? 表示占位符
  const sqlStr = 'insert into users (username, password) values (?,?)'
  // 3 使用数组形式 , 依次为 ? 指定具体的值
  db.query(sqlStr,[user.username,user.password],(err,res)=> {
    if(err) return console.log(err.message)
    // affectedRows 表示执行这条 sql 语句,影响的行数
    if(res.affectedRows === 1) {
      console.log('插入数据成功')
    }
  })
方式二

  // 插入数据的便捷方式
  const user = {username: 'gangzi',password: 'pc4123'}
  // 编辑写法 set ?
  const sqlStr = 'insert into users set ?'
  // 互相对应
  db.query(sqlStr,user,(err,res)=> {
    if(err) return console.log(err.message)
    if(res.affectedRows === 1) {
      console.log('插入数据成功')
    }
  })
4.3.3 更新数据
写法一
  更新数据
  const user = {username: 'lilisi',password:'aa113',id: 6}
  const sqlStr = 'update users set username = ? , password = ? where id = ?'
  db.query(sqlStr,[user.username,user.password,user.id],(err,res)=> {
    if(err) return console.log(err.message)
    if(res.affectedRows === 1)
      console.log('数据更新成功')
  })
写法二(便捷)
  // 更新数据 - 便捷写法
  const user = {username: 'lihua',password:'lh122',id: 6}
  const sqlStr = 'update users set ? where id = ?'
  // 注意 写法
  db.query(sqlStr,[user,user.id],(err,res)=> {
    if(err) return console.log(err.message)
    if(res.affectedRows === 1)
      console.log('数据更新成功')
  })
4.3.4 删除数据

  // 删除数据
  const sqlStr = 'delete from users where id = ?'
  db.query(sqlStr,6,(err,res)=> {
    if(err) return console.log(err.message)
    if(res.affectedRows === 1)
      console.log('数据删除成功')
  })
4.3.5 标记删除 - 用户体验思维

  // 标记删除
  const sqlStr = 'update users set status = ? where id = ?'
  db.query(sqlStr,[1,20],(err,res)=> {
    if(err) return console.log(err.message)
    if(res.affectedRows === 1)
      console.log('数据更新成功')
  })
5 前后端的身份认证
5.1 前后端的概念
5.1.2 服务端的优缺点
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5ANcZite-1673585565325)(null)]
5.1.3 前后端分离的优缺点
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MNBmREjf-1673585567702)(null)]
5.1.4 选择web开发的模式

5.2 身份认证
5.2.3 不同的开发模式下 选择不同

5.2.4 Session 认证机制

5.2.5 cookie 在身份认证中的作用

5.3.5 cookie 不具有安全性

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U6L1gpiW-1673585568461)(null)]
提高身份认证的安全性
在客户端出示cookie后,还需要在服务端进行cookie的认证
5.4 在express中使用Session认证
5.4.1 安装express-session中间件
npm install express-session	
5.4.2 配置express-session中间件
通过app.use()注册中间件

5.4.3 向session中存数据
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2gDEaDsR-1673585565493)(null)]
5.4.4 从session中取数据-验证是否已经登录
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uB5yiWJV-1673585565432)(null)]
5.4.5 清空session的数据-退出登录

5.5 JWT认证机制
5.5.1 jwt 使用时机
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NaFzLdjY-1673585565678)(null)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BL9CMMmn-1673585565598)(null)]
5.5.4 jwt 的组成
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0gcXDEee-1673585564745)(null)]

5.5.7 jwt的使用方式

5.6 在express 中使用jwt - (令牌)
5.6.1 安装相关的包

npm install jsonwebtoken express-jwt
5.6.2 导入包
const jwt = require('jsonwebtoken')
const expressJWT = require('express-jwt')
5.6.3 定义secret密钥

// secret 的本质是 一个字符串 越复杂越安全
// 建议命名  secretKey
const secretKey =  'lilisi ok'
5.6.4 在登录成功后生成JWT字符串

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VPLUogk2-1673585565265)(null)]
5.6.5 将jwt字符串还原成json对象

注意:更新后语法问题 - 解决Jwt遇到algorithms should be set报错的解决方法
5.6.6 用req.user 获取express-jwt解析的用户信息

5.6.7 捕获解析jwt失败后产生的错误
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4CKmsE56-1673585565162)(null)]
5.6.8 实机 - 测试 - 令牌验证代码
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hr9rasYe-1673585568357)(null)]
key:
Authorization
value:
Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiaWF0IjoxNjcwNTExOTE5LCJleHAiOjE2NzA1MTE5Nzl9.tUg_VuYrusCyhFb3QLil-8aD17zsQKMbDFh0ZZTAsFo
代码:
// login.js
const express = require('express')
const app = express()
// jwt 认证
const jwt = require('jsonwebtoken')
const expressJWT = require('express-jwt')
// 定义密钥 secretKey
const secretKey = 'lilisi ok'
// 跨域
const cors = require('cors')
app.use(cors())
// 解析 urlcoded
app.use(express.urlencoded({
  extended: false
}))
app.use(express.json())
// 注册中间件 将jwt字符串解析还原成json对象的中间件  unless({path:[...]}) 规定不需要 token就可以请求
// 注册 expressJWT 错误原因,版本一样,语法有更新 express-jwt@6.1.1 - 加 algorithms:['HS256']
// 把解析出来的用户信息 挂载在req.user 上 
app.use(expressJWT({
  secret: secretKey,
  algorithms: ['HS256']
}).unless({
  path: [/^//api///]
}))
// 登录接口
app.post('/api/login', (req, res) => {
  const userinfo = req.body
  // 登录失败
  if (userinfo.username !== 'admin' || userinfo.password !== '000') {
    return res.send({
      status: 400,
      msg: '登录失败'
    })
  }
  // 登录成功
  // 使用 jwt.sign 进行配置
  // jwt.sign 里面的参数 不能对密码进行加密
  const tokenStr = jwt.sign({
    username: userinfo.username
  }, secretKey, {
    expiresIn: '60s'
  })
  res.send({
    status: 200,
    token: tokenStr,
    msg: '登录成功'
  })
})
// 一个有权限的 api
app.get('/admin/getinfo', (req, res) => {
  // expressJWT 解析出来的用户信息 挂载在req.userreq.user
  console.log(req.user)
  res.send({
    status: 200,
    msg: '获取信息成功',
    data: req.user
  })
})
// 全局错误捕获中间件 
app.use((err, req, res, next) => {
  if (err.name === 'UnauthorizedError') {
    return res.send({
      status: 401,
      msg: '无效的token'
    })
  }
  // 其他原因的错误
  res.send({
    status: 500,
    message: '未知错误'
  })
})
app.listen(80, function () {
  console.log('at http://127.0.0.1')
})
5.6.8 实机 - 测试 - 令牌验证代码
[外链图片转存中…(img-m9rGAmzZ-1673585560633)]
key:
Authorization
value:
Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiaWF0IjoxNjcwNTExOTE5LCJleHAiOjE2NzA1MTE5Nzl9.tUg_VuYrusCyhFb3QLil-8aD17zsQKMbDFh0ZZTAsFo
代码:
// login.js
const express = require('express')
const app = express()
// jwt 认证
const jwt = require('jsonwebtoken')
const expressJWT = require('express-jwt')
// 定义密钥 secretKey
const secretKey = 'lilisi ok'
// 跨域
const cors = require('cors')
app.use(cors())
// 解析 urlcoded
app.use(express.urlencoded({
  extended: false
}))
app.use(express.json())
// 注册中间件 将jwt字符串解析还原成json对象的中间件  unless({path:[...]}) 规定不需要 token就可以请求
// 注册 expressJWT 错误原因,版本一样,语法有更新 express-jwt@6.1.1 - 加 algorithms:['HS256']
// 把解析出来的用户信息 挂载在req.user 上 
app.use(expressJWT({
  secret: secretKey,
  algorithms: ['HS256']
}).unless({
  path: [/^//api///]
}))
// 登录接口
app.post('/api/login', (req, res) => {
  const userinfo = req.body
  // 登录失败
  if (userinfo.username !== 'admin' || userinfo.password !== '000') {
    return res.send({
      status: 400,
      msg: '登录失败'
    })
  }
  // 登录成功
  // 使用 jwt.sign 进行配置
  // jwt.sign 里面的参数 不能对密码进行加密
  const tokenStr = jwt.sign({
    username: userinfo.username
  }, secretKey, {
    expiresIn: '60s'
  })
  res.send({
    status: 200,
    token: tokenStr,
    msg: '登录成功'
  })
})
// 一个有权限的 api
app.get('/admin/getinfo', (req, res) => {
  // expressJWT 解析出来的用户信息 挂载在req.userreq.user
  console.log(req.user)
  res.send({
    status: 200,
    msg: '获取信息成功',
    data: req.user
  })
})
// 全局错误捕获中间件 
app.use((err, req, res, next) => {
  if (err.name === 'UnauthorizedError') {
    return res.send({
      status: 401,
      msg: '无效的token'
    })
  }
  // 其他原因的错误
  res.send({
    status: 500,
    message: '未知错误'
  })
})
app.listen(80, function () {
  console.log('at http://127.0.0.1')
})


















