node.js 学习笔记

发布时间 2023-03-26 18:18:36作者: 小明与小红

阶段一

1 初始Node.js

image-20221203104324222

javascript 运行环境

image-20221203105047380

1.2 Node.js中的javacript 运行环境

image-20221203105651639

image-20221203105937211

1.3 Node.js环境安装

百度

1.4 node.js 执行javaScript 代码

image-20221203111154509

image-20221203111410052

2 fs文件系统模块

2.1 fs文件系统模块概念

image-20221203135613934

导入文件系统模块:

const fs = require('fs')
  1. 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)
    })
    

    效果:

    image-20221203140930750

    判断文件读取成功还是失败(err.message)

    const fs = require('fs')
    
    fs.readFile('text.txt','utf8',function(err,res){
      if(err) {
        console.log("读取文件失败!"+err.message)
      }else {
        console.log("读取文件成功!"+res)
      }
    })
    
  2. 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('写入成功!')
  })
})

效果:

image-20221203145033095

2.3 路径拼接问题

image-20221203150144488

image-20221203150249392

注意:在js中,/ 是转义字符的意思

直接提供完整路径,缺点:移植性非常差,不利于维护

解决:__dirname (表示当前文件所处的目录)

image-20221203150949826

示例:

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 路径模块概念

image-20221203153925790

​ 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')

image-20221203154540007

示例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 综合案例 - 页面拆分

image-20221203201206641


// 导入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的概念

image-20221203201712779

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 模块化的定义

image-20221203112020937

4.5.2 node.js 中的模块化

加载模块

image-20221203112259338

image-20221203113041671

模块作用域

image-20221203113307212

模块作用域的好处

防止全局变量污染

image-20221203113954917

每个模块都有一个 module 对象

image-20221203114427998

笔记: 在外界使用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

效果:

image-20221203120427813

写法2 (建议)

image-20221203120729992

module.exports = {
  username : 'zhanghua',
  sayme() {
    console.log('my name is '+ this.username)
  }
}

4.5.4 exports 与 module.exports指向的对象

image-20221203121558779

// 不行
// 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
}

效果:

image-20221203133829130

image-20221203133448698

因为引用数据类型,地址指向问题

误区3

exports.username = 'ls'
exports.age= 19

效果:

image-20221203134452038

原因:没有定义引用数据类型,不会改变地址

误区4

exports = {
  username : 'zs',
  gender: "女"
}
module.exports = exports
module.exports.age = 22

效果: image-20221203134922308

4.5.5 node.js 模块化规范

image-20221203135106440

问题:为什么nodejs不支持import语法

nodejs不支持import语句,原因:nodejs采用的是CommonJS的模块化规范,使用require语句引入模块;而import是ES6的模块化规范关键字。

解决:babel的安装

阶段二

1 npm与包

概念:

image-20221204121538952

介绍:

image-20221204121702628

1.2 npm 导入moment包

image-20221204122429681

  1. npm install moment 
    // 安装包
    

1.3 包的语义化管理规范

image-20221204124035714

1.4 包的分类

image-20221204124251013

npm intall 包名 -D 	// devDependencies
npm intall 包名		// dependencies 

全局包

npm intall 包名 -g
npm unintall 包名 -g		// 卸载包

1.5 推荐包(i5ting_toc) - xx.md文件转 xx.html 件

image-20221204125118487

image-20221204125137919

1.6 规范的包内部结构

image-20221204130152612

1.7 开发自己的包

  1. 文件结构

    image-20221204164426697

  2. 文件中代码结

    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 '&lt;'
          case '>':
            return '&gt;'
          case '"':
            return '&quot;'
          case '&':
            return '&amp;'
        }
      })
    }
    
    function htmlUnEscape(str) {
      return str.replace(/&lt;|&gt;|&quot;|&amp;/g, (match)=> {
        switch(match) {
          case '&lt;':
            return '<'
          case '&gt;':
            return '>'
          case '&quot;':
            return '"'
          case '&amp;':
            return '&'
        }
      })
    }
    
    // 导出
    module.exports = {
      dateFormat,
      htmlEscape,
      htmlUnEscape
    }
    
  3. 分模块

    image-20221204230924826

    使用:...

    image-20221204231906828

    const date = require('./src/dateFormat.js')
    const escape = require('./src/htmlEscape.js')
    
    module.exports = {
      ...date,
      ...escape
    }
    
  4. 编写包的开源文档

    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)
    // 结果 &lt;h2&gt;hello world !&lt;/h2&gt;
    console.log(newStr)
    ```
    
    ## 还原 html 中的特殊字符
    ```js
    // 待还原的字符
    const newStr = codexx.htmlEscape(str)
    // 结果 <h2>hello world !</h2>
    console.log(codexx.htmlUnEscape(newStr))
    ```
    
    ## 开源协议
    ISC
    

1.8 发布包

image-20221205200512789

1.8.1 发布包 删除包

百度

1.9 模块的加载机制

1.9.1 优先从缓存中加载

image-20221205201008209

1.9.2 内置模块的加载机制

image-20221205201145605

1.9.3 自定义模块的加载机制

image-20221205201425010

1.9.4 第三方模块的加载机制

image-20221205202254323

1.9.5 把目录作为模块加载

image-20221205202415752

image-20221205203202799

image-20221205203226924

阶段三:Express

1 express 的初步使用

image-20221205203713411

image-20221205203809290

安装

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()

image-20221205214309818

app.use(express.static('./indexItem'))
// express.static('路径') 方法中的路径不会出现在客户端的访问路径中

注意:自动托管文件夹,并在其目录下自动寻找index.html/htm 等文件

1.3.2托管多个静态资源目录


app.use(express.static('./indexItem'))
app.use(express.static('./images'))
// 直接重开一行 写就行

image-20221205215049702

1.3.3 挂载路径前缀

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

1.4 nodemon 的使用

1.4 介绍

image-20221205215522243

安装

npm install nodemon -g

使用

// 终端中
nodemon xx.js

2 express 路由

2.1 路由概念

image-20221205220631797

// 示例
app.post('/user',(req, res) => {})

2.2 路由的使用

2.2.1 路由最简单的使用

image-20221205221014865

// 挂载路由
app.post('/user',(req, res) => {})

2.2.2 重点 - 模块化路由

image-20221205221402017

示例:

image-20221205221313987

示例代码:

文件结构

image-20221205225719534

// 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 中间件的概念

image-20221206141711691

3.1.2 express中间件的格式

app.get('/',(req, res, next)=>{
	next()
})

image-20221206165518977

3.1.3 中间件 - next 函数的作用

image-20221206170040901

image-20221206170056927

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')
})

image-20221206172251746

3.2.2 中间件的作用

image-20221206181353693

......
// 定义中间件
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()
})

image-20221206184326710

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 中间件的注意事项

image-20221207135041843

3.3 中间件的分类

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

image-20221207135158930

3.3.1 应用级别中间件:

image-20221207144343297

3.3.2 路由级别中间件:

image-20221207144626702

var app = express()
var router = experss.Router()

// 路由级别的中间件
router.use((req,res,next)=> {
	console.log('Time',Date.now())
	next()
})

app.use('/',router)

3.3.3 错误级别的中间件

image-20221207150705151

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 内置的中间件

image-20221207152437243

// 配置 application/json 格式数据的内置中间件
app.use(express.json())
// 配置解析 application/x-www-form-urlencoded 格式数据的内置中间件
app.use(express.urlencoded({extended: false}))

express.json 的使用

在软件,模拟发起post请求:

image-20221207153445451

注意:默认情况下 如果不解析 表单数据的中间件 则 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 第三方中间件

image-20221207163255605

image-20221207163322422

image-20221207163356259

3.4 自定义中间件

3.4.1 需求与步骤

image-20221207163626136

3.4.2 自定义中间件

image-20221207165134810

// 解析表单数据的中间件
app.use((req,res,next)=> {
  // 具体
})

3.4.3 监听req的data事件

image-20221207165549968

// 定义变量 用来存客户端发送过来的请求数据
  let str = ''
  // 监听 req 对象的 data 事件(客户端发送的新的请求数据)
  req.on('data', (chunk) => {
    // 拼接请求体数据 隐式转换为字符串
    str += chunk
  })

3.4.4 监听req的end事件

image-20221207170120454

   // 监听 req 对象的 end 事件 (请求体发送数据完成后自动触发)
  req.on('end',()=>{
    // 打印完整的请求数据
    console.log(str)  // 结果: bookname=%E7%9C&author=%E6%
  })

3.4.5 querystrign 解析请求体数据

image-20221207170936027

问题:“querystring”已弃用

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

image-20221207172416997

// 导入处理 querystring 
const qs = require('querystring');
......
// 调用 qs.parse() 方法 把查询字符串解析为对象
    const body = qs.parse(str)
    console.log(body)

3.4.6 将解析出来的数据挂载在 req.body

image-20221207172718210

 // 调用 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 自定义中间件封装为独立模块

image-20221207175246284

补充(未分模块的代码):

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基本创建

  1. 创建基本服务器

    // 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')
    })
    
  2. 路由模块

    // 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 跨域资源共享

解决跨域问题:

image-20221207195016492

4.5.2 使用

image-20221207195316655

4.5.3 cors 介绍

image-20221207200915361

4.5.4 cors 注意事项

image-20221207201327900

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

image-20221207201612048

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

image-20221207202004494

4.5.7 cors 响应头部 Access-Control-Allow- Methods

image-20221207202242520

4.5.8 cors 请求的分类

简单请求:

image-20221207202527226

预检请求:

image-20221207202655745

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

image-20221207202939392

示例:

// 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 请求成功!'
  })
})
...

效果:

image-20221207204902205

image-20221207204929360

4.6 JSONP的概念与特点及使用

image-20221207204600588

4.6.2 创建JSONP接口的注意事项

image-20221207205523735

4.6.3 实现JSONP接口的具体代码

image-20221207205325694

4.6.4 在jquery 中 发起jsonp请求

image-20221207205847050

阶段四

4 在项目中操作mysql

4.1/2 安装与配置mysql 模块

  1. 安装

    npm install mysql
    
  2. 配置

     // 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'
      })
    
  3. 测试mysql 模块是否工作正常

    image-20221207211744475

      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('插入数据成功')
    }
  })

方式二

image-20221207221943928

  // 插入数据的便捷方式
  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 删除数据

image-20221207225134869

  // 删除数据
  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 标记删除 - 用户体验思维

image-20221207225712858


  // 标记删除
  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 服务端的优缺点

image-20221208130350615

5.1.3 前后端分离的优缺点

image-20221208130554556

5.1.4 选择web开发的模式

image-20221208130827905

5.2 身份认证

5.2.3 不同的开发模式下 选择不同

image-20221208131349767

5.2.4 Session 认证机制

image-20221208132551449

image-20221208132814514

image-20221208133332603

image-20221208133355670

提高身份认证的安全性

在客户端出示cookie后,还需要在服务端进行cookie的认证

5.4 在express中使用Session认证

5.4.1 安装express-session中间件

npm install express-session	

5.4.2 配置express-session中间件

通过app.use()注册中间件

image-20221208153849272


5.4.3 向session中存数据

image-20221208155323293

5.4.4 从session中取数据-验证是否已经登录

image-20221208155505112

5.4.5 清空session的数据-退出登录

image-20221208155633586

5.5 JWT认证机制

5.5.1 jwt 使用时机

image-20221208205804918

image-20221208210011908

5.5.4 jwt 的组成

image-20221208210242984

image-20221208210325582

5.5.7 jwt的使用方式

image-20221208210449659

5.6 在express 中使用jwt - (令牌)

5.6.1 安装相关的包

image-20221208210814540

npm install jsonwebtoken express-jwt

5.6.2 导入包

const jwt = require('jsonwebtoken')
const expressJWT = require('express-jwt')

5.6.3 定义secret密钥

image-20221208211316875

// secret 的本质是 一个字符串 越复杂越安全
// 建议命名  secretKey
const secretKey =  'lilisi ok'

5.6.4 在登录成功后生成JWT字符串

image-20221208224707240

image-20221208224817518

5.6.5 将jwt字符串还原成json对象

image-20221208224956274

注意:更新后语法问题 - 解决Jwt遇到algorithms should be set报错的解决方法

5.6.6 用req.user 获取express-jwt解析的用户信息

image-20221208225444038

5.6.7 捕获解析jwt失败后产生的错误

image-20221208225551657

5.6.8 实机 - 测试 - 令牌验证代码

image-20221208230926031

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')
})