Node.js 个人笔记

文章目录

一、初识 Node.js

Node.js的官方地址: https://nodejs.org/zh-cn/

1.Node.js中的JavaScript 运行环境

请添加图片描述
注意:
①浏览器是JavaScript的前端运行环境。
②Node.js 是JavaScript的后端运行环境。
③Node.js 中无法调用DOM和BOM等浏览器内置API

2.Node.js的学习路径:

JavaScript基础语法+ Node.js内置API模块(fs、 path. http等) +第三方API模块(express、 mysql 等)

3.Node.js环境的安装

如果希望通过Node.js来运行Javascript代码,则必须在计算机上安装Node.js环境才行。
安装包可以从Node.js的官网首页直接下载,进入到Node.js的官网首页(https://nodejs.org/en/) ,击绿色的按钮,下载所需的版本后.双击直接安装即可。

4.区分LTS版本和Current版本的不同

①LTS 为长期稳定版,对于追求稳定性企业级项目来说,推荐安装LTS版本的Node.js.
②Current 为新特性尝鲜版,对热衷于尝试新特性的用户来说,推荐安装Current版本的Node.js。但是,Current 版本中可能存在隐藏的Bug或安全性漏洞,因此不推荐在企业级项目中使用Current版本的Node.js.

5.查看已安装的Node.js的版本号

打开终端,在终端输入命令node -v后,按下回车键,即可查看已安装的Node.js的版本号。
Windows系统快速打开终端的方式:
使用快捷键(Windows徽标键+ R)拼运行面板,输入cmd后直接回车,即可打开终端。

6.在Node.js环境中执行JavaScript代码

①打开终端
②输入node要执行的js文件的路径
或者
shift加鼠标右键
请添加图片描述

二、fs模块

1.引用

const fs = require('fs')

2.fs.readFile()

// options 代表编码格式读取
fs.readFile(path,[,options],callback)
// 示例 读取成功 err=null 读取失败 data=undefined
fs.readFile('./files/11.text','utf8',function(err,data){
    if(err){
        return console.log('读取失败'+err)
    }
    console.log(data)
})

向指定的文件中写入内容

请添加图片描述
请添加图片描述
请添加图片描述

3.fs.writeFile()

没有该文件就创建,但是不能创建目录

写入会覆盖原文件

// options 默认值utf8
fs.writeFile(path,data[,options],callback)
// options 默认值utf8
fs.writeFile(path,data[,options],callback)

请添加图片描述
核心实现步骤
①导入需要的fs文件系统模块
②使用fs.readFile()方法,读取素材目录下的成绩.txt文件
③判断文件是否读取失败
④文件读取成功后,处理成绩数据
⑤将处理完成的成绩数据,调用fs.writeFile()方法,写入到新文件成绩-ok.txt中
请添加图片描述
请添加图片描述
请添加图片描述

三、文件路径问题

在使用fs模块操作文件时,如果提供的操作路径是以./../开头的相对路径时,很容易出现路径动态拼接错误的问题。
原因:代码在运行的时候,会以执行node命令时所处的目录,动态拼接出被操作文件的完整路径。

1.__dirname

在使用 fs 模块操作文件时,如果提供的操作路径是以 ./ 或 …/ 开头的相对路径时,容易出现路径动态拼接错误的问题
原因:代码在运行的时候,会以执行 node 命令时所处的目录,动态拼接出被操作文件的完整路径
解决方案:在使用 fs 模块操作文件时,直接提供完整的路径,从而防止路径动态拼接的问题
__dirname 获取文件所处的绝对路径

fs.readFile(__dirname + '/files/1.txt','uft8',function(err,data){
    console.log(err)
    console.log(data)
})

2.path 模块

const path = require('path')
path.join()     				// 将多个路径片段拼接
path.basename(path[,ext]) 		// 从路径字符串中取出文件名
path.extname(path)				// 从路径字符串中取出文件扩展名

路径拼接path.join()

const path = require('path')
const fs = require('fs')

// 注意 ../ 会抵消前面的路径
// ./ 会被忽略
const pathStr = path.join('/a', '/b/c', '../../', './d', 'e')
console.log(pathStr) // \a\d\e

fs.readFile(path.join(__dirname, './files/1.txt'), 'utf8', function (err, dataStr) {
  if (err) {
    return console.log(err.message)
  }
  console.log(dataStr)
})

获取路径中文件名 path.basename()
使用 path.basename() 方法,可以获取路径中的最后一部分,常通过该方法获取路径中的文件名

const path = require('path')

// 定义文件的存放路径
const fpath = '/a/b/c/index.html'

const fullName = path.basename(fpath)
console.log(fullName) // index.html

const nameWithoutExt = path.basename(fpath, '.html')
console.log(nameWithoutExt) // index

获取路径中文件扩展名 path.extname()

const path = require('path')

const fpath = '/a/b/c/index.html'

const fext = path.extname(fpath)
console.log(fext) // .html

四、http 模块

const http = require('http')

1.http.createServer()

创建web服务器实例

const server = http.createServer()

2.server.on()

为服务器实例绑定 request 事件,监听客户端发送的请求

server.on('request',(req,res)=>{
    console.log(req.url)   		// 客户端请求地址
    console.log(req.method) 	// 客户端请求方法
    // 防止中文乱码,设置响应头
    res.setHeader('Content-type','text/html; charset=utf-8')
    res.end('Hello World')		// 给客户端发送指定内容并结束这次请求
})

3.server.listen()

启动web服务器监听端口
80端口可省略 http://127.0.0.1

server.listen(80,()=>{})

4.创建基本 Web 服务器

const http = require('http')

// 创建 web 服务器实例
const server = http.createServer()

// 为服务器实例绑定 request 事件,监听客户端的请求
server.on('request', function (req, res) {
  const url = req.url
  const method = req.method
  const str = `Your request url is ${url}, and request method is ${method}`
  console.log(str)

  // 设置 Content-Type 响应头,解决中文乱码的问题
  res.setHeader('Content-Type', 'text/html; charset=utf-8')
  // 向客户端响应内容
  res.end(str)
})

server.listen(8080, function () {
  console.log('server running at http://127.0.0.1:8080')
})

请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述

5.简单路由演示

1.核心实现步骤
(1)获取请求的url地址
(2)设置默认的响应内容为404 Not found
(3)判断用户请求的是否为/或/index.html首页
(4)判断用户请求的是否为/about.html关于页面
(5)设置Content-Type响应头,防止中文乱码
(6)使用res.end0把内容响应给客户端

const http = require('http')
const server = http.createServer()
server.on('request', (req, res) => {
  // 获取请求的 url 地址
  const url = req.url
  // 设置默认响应内容
  let content = '<h1>404 Not found!<h1>'
  // 判定路径设置返回内容
  if (url === '/' || url === '/index.html') {
    content = '<h1>首页</h1>'
  } else if (url === '/about.html') {
    content = '<h1>关于页面</h1>'
  }
  res.setHeader('Content-Type', 'text/html; charset=utf-8')
  res.end(content)
})
server.listen(80, () => {
  console.log('服务器启动:'+'http://127.0.0.1')
})

五、模块化

1.什么是模块化

编程领域中的模块化
编程领域中的模块化,就是遵守固定的规则,把一个大文件拆成独立并互相依赖多个小模块
把代码进行模块化拆分的好处:
(1)提高了代码的复用性
(2)提高了代码的可维护性
(3)可以实现按需加载

2.模块化规范

模块化规范就是对代码进行模块化的拆分与组合时,需要遵守的那些规则。
例如:
●使用什么样的语法格式来引用模块
●在模块中使用什么样的语法格式向外暴露成员
模块化规范的好处:大家都遵守同样的模块化规范写代码,降低了沟通的成本,极大方便了各个模块之间的相互调用,利人利己。

3.Node.js中模块的分类

Node.js中根据模块来源的不同,将模块分为了3大类,分别是:
内置模块(内置模块是由Node.js官方提供的,例如fs、path、 http 等)
自定义模块(用户创建的每个js文件,都是自定义模块)
第三方模块(由第三方开发出来的模块,并非官方提供的内置模块,也不是用户创建的自定义模块,使用前需要先下载)
请添加图片描述

4.加载模块

// 加载内置模块
const fs = require('fs')
// 加载用户的自定义模块
const custom = require('./custom.js')
// 加载第三方模块
const moment = require('moment')

5.模块作用域

导入模块时,里面的变量及函数只能有该模块 js 能访问。好处:防止变量污染
而在html中,导入 js 后能访问变量和函数,这样会导致多个js的变量重复,会被覆盖

// test.js
const name = 'lhd'
function sayHello(){
    console.log('Hello World ',name)
}
// index.js
const custom = require('./test')
console.log(custom)                // 输出:{} 访问不了里面的变量和函数

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

简称暴露
(1)module 对象
每个 .js 自定义模块中都有一个 module 对象,导入模块时,其实是导入module.exports指向的对象。默认值为 {}
(2)module.exports
在 module.exports 对象上挂载属性方法

// test.js
module.exports.name = 'lhd'
module.exports.sayHello = function(){
    console.log('Hello World ') 
}
// index.js
const custom = require('./test')
console.log(custom)				// {name:lhd,sayHello:[Function]}

请添加图片描述

(3)exports
简化 module.exports。默认情况下 exports 和 module.exports 指向同一个对象。最终结果以 module.exports 指向的对象为准

// test.js
exports.name = 'lhd'
exports.sayHello = function(){
    console.log('Hello World ') 
}
// index.js
const custom = require('./test')
console.log(custom)				// {name:lhd,sayHello:[Function]}

(4)同时使用注意事项
使用 require() 模块时,得到的永远是 module.exports 指向的对象
建议只用一种方式,以防混淆
在这里插入图片描述
(5)CommonJS 规范
(1)每个模块内部,module变量代表当前模块。
(2)module变量是一个对像,它的exports属性(即module.exports)是对外的接口。
(3)加载某个模块,其实是加载该模块的module.exports属性。require(0方法用于加载模块。

六、npm 与 包

1.什么是包

Node.js中的第三方模块又叫做
就像电脑和计算机指的是相同的东西,第三方模块和包指的是同一个概念,不过叫法不同。

2.包的来源

不同于Node.js中的内置模块与自定义模块,包是由第三方个人或团队开发出来的,免费供所有人使用。
注意: Node.js 中的包都是免费且开源的,不需要付费即可免费下载使用。

3.为什么需要包

由于Node.js的内置模块仅提供了一些底层的API,导致在基于内置模块进行项目开发的时,效率很低。
包是基于内置模块封装出来的,提供了更高级、更方便的API,极大的提高了开发效率
包和内置模块之间的关系,类似于jQuery和浏览器内置API之间的关系。

4.从哪里下载包

国外有一家IT公司,叫做npm, Inc.这家公司旗下有一个非常著名的网站: https://www.npmjs.com/ ,它是全球最大的包共享平台,你可以从这个网站上搜索到任何你需要的包,只要你有足够的耐心!
到目前位置,全球约1100多万的开发人员,通过这个包共享平台,开发并共享了超过120多万个包供我们使用。
npm, Inc. 公司提供了一个地址为 https://registry.npmjs.org/的服务器,来对外共享所有的包,我们可以从这个服务器上下载自己所需要的包
注意:
●从https://www.npmjs.com/网站上搜索自己所需要的包
●从https://reqistry.npmis.org/服务器上下载自己需要的包
请添加图片描述

5.安装包

在包共享平台中,每个包都有会使用说明

# 默认安装最新
npm install 包名称   
npm i 包名称

# 安装多个
npm i 包名称 包名称

# 指定安装版本
npm install 包名称@x.xx.x			// x.xx.x为版本
# 第一个x代表大版本,第二个代表功能版本,第三个代表 Bug 修复版本			 

举例格式化时间的高级做法
①使用npm包管理工具,在项目中安装格式化时间的包moment
②使用require()导入格式化时间的包
③参考moment的官方API文档对时间进行格式化

//1.导入需要的包
//注意: 导入的名称,就是装包时候的名称
const moment = require('moment')

const dt = moment().format('YYYY-MM-DD HH:mm:ss')
console.log(dt)

请添加图片描述

6.初次装包后多了哪些文件

初次装包完成后,在项目文件夹下多-一个叫做 node_ modules的文件夹和package lockjson的配置文件。
其中:
node_ modules文件夹用来存放所有已安装到项目中的包。require0 导入第三方包时,就是从这个目录中查找并加载包。
package-lock.json配置文件用来记录node_ modules目录下的每一个包的下载信息,例如包的名字、版本号、下载地址等。

7.切换下包镜像源

下载包速度慢,可以使用镜像服务器
注意:在cmd上运行

# 查看当前的下包镜像源
npm config get registry
# 将下包的镜像源切换为淘宝镜像源
npm config set registry=https://registry.npm.taobao.org/
# 检查
npm config get registry

8.nrm

为了更方便的切换下包的镜像源,我们可以安装 nrm,可快速查看和切换镜像源

# 通过 npm 包管理器,将 nrm 安装为全局可用的工具
npm i nrm -g
# 查看所有可用的镜像源 *号代表目前使用的镜像源
nrm ls
# 将下包镜像源切换为淘宝镜像
nrm use taobao

请添加图片描述

9.安装完包

初次装包完成后,在项目文件夹下多一个叫做 node modules 的文件夹和 package-lock.json 的配置文件。不要手动修改里面的东西,npm包管理工具会自动维护。

node modules 文件夹用来存放所有已安装到项目中的包。require() 导入第三方包时,就是从这个目录中查找并加载包。

package-lock.json 配置文件用来记录 node modules 目录下的晦一个包的下载信息,例如包的名字、版本号、下载地址等。

10.dependencies

作用:在 package.json 中记录核心依赖包信息
正常安装默认为核心依赖包

11.devDependencies

作用:在 package.json 中记录开发依赖包信息

果某些包只在项目开发阶段用到,在项目上线以后不会用到,建议把这些包记录在 devDependencies 中。相反,如果在开发及上线都用到,则记录在 dependencies 中。一般来说依据官方使用说明安装即可

# 安装到 devDependencies 
npm i 包名称 -D
npm install 包名称 --save-dev

12.共享代码

由于第三方包(node_modules)体积过大,不方便分享代码,所以共享时应剔除 node_modules

项目根目录下创建 package.json ,记录安装了哪些包。使用 git 时应把 node_modules 添加到 .gitignore 中。

# 编写代码前先创建 package.json 项目目录不能有中文和空格
# 安装包时,会自动记录包信息到 package.json
npm init -y

# 一次性安装所有的包	
npm install
npm i

请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述

13.卸载包

# 会自动处理 package.json(注意: npm uninstall命令执行成功后,会把卸载的包,自动从package.json的dependencies中移除掉。
)
npm uninstall 包名称
npm uni	包名称

14.包的分类

1.项目包
那些被安装到项目的node_ modules目录中的包,都是项目包。
项目包又分为两类,分别是:
开发依赖包 (被记录到devDependencies节点中的包,只在开发期间会用到)
核心依赖包 (被记录到dependencies节点中的包,在开发期间和项目.上线之后都会用到)
请添加图片描述
2.全局包
在执行npm install命令时,如果提供了-g 参数,则会把包安装为全局包。
全包会被安装到C:\Userss\用户目录\AppData\Roaming\npm\node. modules目录下。
请添加图片描述
请添加图片描述

15.规范的包结构

在清楚了包的概念、以及如何下载和使用包之后,接下来,我们深入了解一下包的内部结构
一个规范的包,它的组成结构,必须符合以下3点要求:
①包必须以单独的目录而存在
②包的顶级目录下要必须包含package.json 这个包管理配置文件
③package.json 中必须包含name, version, main 这三个属性,分别代表包的名字版本号包的入口
注意:以上3点要求是一个规范的包结构必须遵守的格式,关于更多的约束,可以参考如下网址:https://yarnpkg.com/zh-Hans/docs/package-json
开发属于自己的包
请添加图片描述

16.模块加载机制

模块在第一次加载后会被缓存。这也意味着多次调用require0不会导致模块的代码被执行多次。
注意:不论是内置模块、用户自定义模块、还是第三方模块,它们都会优先从缓存中加载,从而提高模块的加载效率。

  • 内置模块加载优先级最高。
  • 加载自定义模块时,应指定 ./ 或者 …/ 开头的路径,如果没有将被认为内置模块或者第三方模块。
  • 如果加载自定义模块时省略扩展名,则会以 .js.json.node 进行尝试加载,如果没有则加载失败。
  • 加载第三方模块时,会先从父级目录寻找 node_modules ,如果父级没有则依次往上寻找。
  • 把目录当作模块标识符加载时,会在被加载的目录下查找 package.json 并寻找 main 属性,作为加载的入口。如果没有或者无法解析,则会加载目录下的 index.js 文件。

七、Express

1.Express简介

什么是Express
官方给出的概念: Express 是基于Node.js平台,快速、开放、极简的Web开发框架
通俗的理解: Express 的作用和Node.js内置的http模块类似,是专用来创建Web服务器的。Express的本质:就是一个npm上的第三方包,提供了快速创建Web服务器的便捷方法。
Express的中文官网: http://www.expressjs.com.cn/
Express能做什么
对于前端程序员来说,最常见的两种服务器,分别是:
Web 网站服务器:专门]对外提供Web网页资源的服务器。
API接口服务器:专门对外提供API接口的服务器。
使用Express,我们可以方便、快速的创建Web网站的服务器或API接口的服务器。

2.安装

npm i express
npm i express@4.17.1

3.创建基本的 Web 服务器

// 导入express
const express = require('express')
// 创建 web 服务器
const app = express()

// 调用 app.listen
app.listen(80,()=>{
    console.log('express server running at http://127.0.0.1')
})

4.监听 GET 请求

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

// req:请求对象(包含了与请求相关的属性和方法)
// res:响应对象(包含了与响应相关的属性和方法)
app.get('请求url',function(req,res){
    // 处理函数
})

请添加图片描述

5.监听 POST 请求

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

// req:请求对象(包含了与请求相关的属性和方法)
// res:响应对象(包含了与响应相关的属性和方法)
app.post('请求url',function(req,res){
    // 处理函数
})

请添加图片描述

6.把内容响应给客户端

通过res.send()方法,可以把处理好的内容,发送给客户端:

app.get('/user',(req,res)=>{
    res.send({name:'lhd',age:'19'})
})

请添加图片描述

7.获取 URL 中携带的查询参数

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

app.get('/',(req,res)=>{
    console.log(req.query)
})

请添加图片描述

8.获取 URL 中的动态参数

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

// 访问 http://127.0.0.1/user/1874
app.get('/user/:id',(req,res)=>{
    res.send(req.params) // 返回 {id:'1874'}
})
// 访问 http://127.0.0.1/user/1874/lhd
app.get('/user/:id/:name',(req,res)=>{
    res.send(req.params) // 返回 {id:'1874',name:'lhd'}
})

请添加图片描述
请添加图片描述

9.托管静态资源

通过 express.static() 创建静态资源服务器。可将图片、css 文件、js 文件对外开放访问。

// 将 public 目录下资源提供访问。多个目录请多次调用
// 访问地址:http://localhost:端口号/xxx.html
app.use(express.static('./public'))

// 挂载路径前缀
// 访问地址:http://localhost:端口号/public/xxx.html
app.use('/public',express.static('./public'))

请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述

10.nodemon

编写调试 Node.js 项目时,如果修改了代码则需要重新启动项目才可以使用,使用 nodemon 工具修改代码时可以自动帮我们重启项目,方便开发
https://www.npmjs.com/package/nodemon
(1)安装
终端中,运行如下命令,即可将nodemon安装为全局可用的工具:

// 安装为全局可用工具
npm i nodemon -g

请添加图片描述
(2)使用
当基于Node.js编写了一个网站应用的时候,传统的方式,是运行node app.js令,来启动项目。这样做的坏处是:代码被修改之后,需要手动重启项目。

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

// 原本启动
node app.js
// nodemon 启动
nodemon app.js

八、Express 路由

在 Express 中,路由指的是客户端的请求与服务器处理函数之间的映射关系。
Express 中的路由分3部分组成,分别是请求的类型、请求的URL地址、处理函数。

// METHOD:请求方式,PATH:请求路径,HANDLER:处理函数
app.METHOD(PATH,HANDLER)

1.普通路由
挂载到app上

// 示例:使用 get 请求返回 get,使用 post 返回 post
app.get('/',(req,res)=>{
    res.send('This is GET request')
})
app.post('/',(req,res)=>{
    res.send('This is POST request')
})

请添加图片描述
2.模块化路由
为了方便对路由进行模块化管理,Express 不建议将路由直接挂载到app上,而是推荐将路由抽离为单独的模块。

步骤:

  1. 创建路由模块对应的 .js 文件
  2. 调用 express.Router() 创建路由对象
  3. 向路由对象上挂载具体的路由
  4. 使用 module.exports 向外共享路由对象
  5. 使用 app.use() 注册路由模块

演示:

// ./router/user.js
var express = require('express')	// 导入express
var router = express.Router()		// 创建路由对象

// 挂载具体路由
router.get('/user/list',(req,res)=>{
    res.send('Get user list')
})
router.post('/user/add',(req,res)=>{
    res.send('Add new user')
})
module.exports = router				// 向外导出路由对象

注册路由模块

// ./app.js
const express = require('express')
const app = express()
const userRouter = require('./router/user.js')    // 导入路由模块
app.use(userRouter)								  // 注册路由模块

app.listen(80,()=>{
    console.log('http://127.0.0.1')
})
// 访问地址: http://127.0.0.1/user/list

为路由模块添加前缀

// 访问地址: http://127.0.0.1/api/user/list
app.use('/api',userRouter)

请添加图片描述
请添加图片描述

九、Express 中间件

1.什么是中间件
中间件(Middleware ),特指业务流程的中间处理环节
请添加图片描述
请求到达 Express 服务器之后,可连续调用多个中间件,从而对这次请求进行预处理。
和vue的路由守卫一样,next()代表放行

1.过程

请添加图片描述

2.中间件格式

本质上就是一个 function 处理函数,格式如下:
请添加图片描述
注意:中间件函数的形参列表中,必须包含next参数。而路由处理函数中只包含req和res。

3.next() 函数

next() 函数是实现多个中间件连续调用的关键,它表示把流转关系转交给下一个中间件或者路由。
请添加图片描述

4.定义中间件函数

可以通过如下的方式,定义一个最简单的中间件函数: .

// mw 指向的就是一个中间件函数
const mw = function(req.res,next){
    console.log('这是个简单的中间件函数')
    next()
}

请添加图片描述
实例
请添加图片描述

5.全局生效的中间件

只要有请求到达服务器,必先经过mw中间件函数处理。相对于vue前置守卫,拦截器

// mw 指向的就是一个中间件函数
const mw = function(req.res,next){
    console.log('这是个简单的中间件函数')
    next()
}
// 全局生效的中间件
app.use(mw)

简化

app.use(function(req,res,next){
    console.log('这是给简单的中间件函数')
    next()
})

请添加图片描述

6.中间件作用

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

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

//定义全局生效的中间件的简化模式
app.use(function(req,res,next){
    // console.log('这是给简单的中间件函数')
    //获取到请求到达服务器的时间
    const time = Data.now()
    //为req对象挂载自定义属性,从而把时间共享给后面的所有路由
    req.startTime = time
    next()
})

// 挂载具体路由
router.get('/',(req,res)=>{
    res.send('Get' + req.startTime)
})
router.post('/',(req,res)=>{
    res.send('Post' + req.startTime)
})

app.listen(80,() =>{
    console.log('http://127.0.0.1')
})

7.定义多个全局中间件

可以使用app.use()连续定义多个全局中间件。客户端请求到达服务器之后,会按照中间件定义的先后顺序依次进行调用,且必须在路由上方。多个中间件共享req和res,示例代码如下:

// 完整示例
const express = require('express')
const app = express()

app.use((req, res, next) => {
  console.log('第一个中间件')
  next()
})
app.use((req, res, next) => {
  console.log('第二个中间件')
  next()
})

app.get('/', (req, res) => {
  res.send({ info: '请求了服务器', method: req.method })
})

app.listen(80, () => {
  console.log('http://127.0.0.1')
})

8.局部生效的中间件

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

const mw = function(req,res,next){
    console.log('中间件函数')
    next()
}
app.get('/',mw,(req,res)=>{
    res.send('Hellow World')
})
app.get('/user',(req,res)=>{
    res.send('Hellow World')
})
//导入express模块
const express = require('express')
//创建express的服务器实例
const app = express()

//定义中间件函数
const mw = function(req,res,next){
    console.log('中间件函数')
    next()
}

//创建路由
app.get('/',mw,(req,res)=>{
    res.send('Hellow World')
})
app.get('/user',(req,res)=>{
    res.send('Hellow World')
})


//调用app.listen方法 指定端口号并启动web服务器
app.listen(80,() =>{
    console.log('http://127.0.0.1')
})

9.多个局部中间件

// mw1与mw2 为中间件函数,两种方式等价
app.get('/',mw1,mw2,(req,res)=>{ xxx })
app.get('/',[mw1,mw2],(req,res)=>{ xxx })

请添加图片描述

//导入express模块
const express = require('express')
//创建express的服务器实例
const app = express()

//定义中间件函数
const mw1 = function(req,res,next){
    console.log('中间件函数')
    next()
}
//定义中间件函数
const mw2 = function(req,res,next){
    console.log('中间件函数')
    next()
}
//创建路由
app.get('/',mw1,mw2,(req,res)=>{
    res.send('Hellow World')
})
app.get('/user',(req,res)=>{
    res.send('Hellow World')
})


//调用app.listen方法 指定端口号并启动web服务器
app.listen(80,() =>{
    console.log('http://127.0.0.1')
})

10.中间件的分类

(1)应用级别的中间件
通过 app.use() 或者 app.get() 或 app.post() ,绑定到 app 实例上的中间件。
(2)路由级别的中间件
绑定到 express.Router() 实例上的中间件。
用法与应用级别中间件一致。

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

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

(3)错误级别的中间件
专门用来捕获项目中发生的异常错误,从而防止项目崩溃。

格式:处理函数中必须有4个形参 (err,req,res,next)

特点:必须注册在所有路由之后

// 示例
app.get('/',(req,res)=>{
    throw new Error('服务器出错')			// 错误发生后,下面的语句无法执行
    res.send('Hellow World')			    // 无法响应
})
app.use((err,req,res,next)=>{
	console.log('发生了错误'+err.message)
    res.send('Error!'+err.message)			// 捕获错误,向客户端响应
})

请添加图片描述
实例
请添加图片描述
请添加图片描述
请添加图片描述
(4)Express内置中间件
express@4.16.0版本开始,内置了3个常用的中间件
express.static 快速托管静态资源的内置中间件 (无兼容性)
express.json 解析 JSON 格式的请求体数据 (仅在4.16.0 +可用)
express.urlencoded 解析 URL-encoded 格式的请求体 (仅在4.16.0 +可用)

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

演示:

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

// 注意:除了错误级别的中间件,其他的中间件,必须在路由之前进行配置
// 通过express.json()这个中间件,解析表单中的JSON格式的数据
app.use(express.json())

// 通过express.urlencoded()这个中间件,来解析表单中的url-encoded格式的数据
app.use(express.urlencoded({ extended:false }))

app.post('/user',(req,res)=>{
	// 在服务器,可以使用req.body这个属性,来接收客户端发送过来的请求体数据
	// 默认情况下,如果不配置解析表单数据的中间件,则req.body默认等于undefined
	console.log(req.body)
	res.send('ok')
})
app.post('/book',(req,res)=>{
    // 在服务器端,可以通过req,body来获取JSON格式的表单数据和url-encoded格式的数据
    console.log(req.body)
})

请添加图片描述
请添加图片描述
(5)第三方中间件
非官方内置,由第三方开发出来的中间件叫第三方中间件,可用按需加载和配置
例如:在 express@4.16.0 之前的版本中,经常使用 body-parser 这个第三方中间件来解析请求体数据。
使用步骤如下:

  1. 使用 npm i body-parser 安装
  2. 使用 require 导入中间件
  3. 使用 app.use() 注册并使用中间件

演示:

const parser = require('body-parser')
app.use(parser.urlencoded({ extended:false }))

请添加图片描述
请添加图片描述

11.自定义中间件

自定义一个解析请求体数据的中间件并使用

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

// 自定义中间件函数,解析请求体数据
const parserBody = function (req, res, next) {
  let str = ''
  // 请求数据过大时,data会分段发送,所以用on绑定data事件,监听data返回的数据并组装
  req.on('data', (chunk) => {
    str += chunk
  })
  // 当数据发送完毕后,会触发end事件,所以在end处将数据挂载为body传给路由
  req.on('end', () => {
    req.body = str
    next()
  })
}
// 注册全局中间件
app.use(parserBody)

app.post('/', (req, res) => {
  // 在此可以拿到挂载的数据并返回数据
  res.send("返回的数据:",req.body)
})

app.listen(80, () => {
  console.log('http://127.0.0.1')
})

测试结果:
在这里插入图片描述

12.封装自定义中间件

// test.js
function test(req,res,next){ /*中间件处理函数*/ }
module.exports = test			// 暴露出去

// app.js
const test = require('./test')	// 导入自定义中间件
app.use(test)					// 注册

十、使用Express写接口

1.创建基本的服务器

请添加图片描述

2.创建API路由模块

请添加图片描述

3.编写GET接口

请添加图片描述

4.编写POST接口

请添加图片描述
请添加图片描述
请添加图片描述

编写GET接口案例

15.使用express写接口.js

//导入express
const express = require('express')
//创建服务器实例
const  app = express()

//导入路由模块
const router = require('./16.apiRouter')
//把路由模块 注册到APP上
app.use('/api',router)

//启动服务器
app.listen(80,()=>{
    console.log('express server running at http://127.0.0.1')
})

16.apiRouter.js

const express = require('express')
const router = express.Router()

//在这里挂载对应的路由
router.get('/get',(req,res)=>{
    //通过req.query获取客户端通过查询字符串,发送到服务器的数据
    const query = req.query
    //调用res.send()方法 向客户端响应处理的结果
    res.send({
        status: 0, //0表示处理成功   1表示处理失败
        msg: 'GET  请求成功', //状态的描述
        data: query //需要响应给客户端的数据
    })
})


module.exports = router

十一、CORS 中间件

1. 基本使用

安装

npm i cors

使用

// app.js
const cors = require('cors')			// 导入
app.use(cors())							// 配置中间件,需在路由之前

2. CORS 响应头部

(1)Access-Control-Allow-Origin
响应头部可以携带一个 Access-Control-Allow-Origin 字段,语法如下

// origin 参数的值指定了只允许访问该资源的外域 url
Access-Control-Allow-Origin:<origin>|*

示例:

// 只允许来自 http://127.0.0.1 的请求
res.setHeader('Access-Control-Allow-Origin','http://127.0.0.1')
// 允许任何域的请求
res.setHeader('Access-Control-Allow-Origin','*')

(2)Access-Control-Allow-Headers
默认情况下,CORS 仅支持客户端向服务器发送如下 9 个请求头:

Accept、Accept-Language、Content-Language、DPR、Downlink、Sava-Data、Viewport-width、Width、Content-Type(值仅限于 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')

(3)Access-Control-Allow-Methods
默认情况下,CORS仅支持客户端发起GET、POST、HEAD请求。
如果客户端希望通过PUT、DELETE等方式请求服务器的资源,则需要在服务器端,通过Access-Control-Alow-Methods 来指明实际请求所允许使用的HTTP方法。
示例:

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

3.CORS 请求分类

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

  • 请求方式:GET、POST、HEAD三者之一
  • HTTP头部信急不超过以下几种字段:无自定义头部字段、Accept、Accept-Language、Content-Language、DPR、Downlink、Save-Data、Viewport-Width、Width、Content-Type(只有三个值 application/x-www-form-urlencoded 、multipart/form-data、text/plain)

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

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

在浏览器与服务器正式通信之前,浏览器会先发送OPTION请求进行预检,以获知服务器是香允许该实际请求,所以这一次的OPTION请求称为“预检请求”。服务器成功响应预检请求后,才会发送真正的情求,并且携带真实数据。
(3)简单请求与预检请求区别
简单请求的特点:客户端与服务器之间只会发生一次请求。
预检请求的特点:客户端与服务器之间会发生两次请求,OPTION预检请求成功之后,才会发起真正的请求。

十二、JSONP

概念:浏览器端通过 <script> 标签的 src 属性,请求服务器上的数据,同时,服务器返回一个函数的调用。这种请求数据的方式叫做JSONP。
特点:

  • JSONP不属于真正的Ajax请求,因为它没有使用 XMLHttpRequest 这个对象,
  • JSONP仅支持GET请求,不支持POST、PUT、DELETE等请求。

1.实现 JSONP 接口

步骤:

  • 获取客户端发送过来的回调函数的名字
  • 得到要通过JSONP形式发送给客户端的数据
  • 根据前两步得到的数据,拼接出一个函数调用的字符串
  • 把上一步拼接得到的字符串,响应给客户端的 <script> 标签进行解析执行

演示:

// 服务器端
app.get('/api/jsonp',(req,res)=>
	//1.获取客户端发送过来的回调函数的名字
	const funcName req.query.callback
	/2.得到要通过JSONP形式发送给客户端的数据
	const data = { name:'lhd',age:19 }
	//3.根据前两步得到的数据,拼州接出一个函数调用的字符串
    // xxx = `$(funcName)(${JSON.stringify(data)})` md文档不识别``
	const scriptstr = xxx
	//4.把上步拼接得到的字符串,响应给客户端的<scr1pt>标签进行解析执行
	res.send(scriptstr)
})
// 客户端
$.ajax({
    method:'GET',
	url:'http://127.0.0.1/api/jsonp',
	dataType:'jsonp', 					//表示要发起J50NP的i情球
	success:function (res){
		console.log(res)
	}	
})

创建JSONP接口的注意事项
如果项目中已经配置了CORS跨域资源共享,为了防止冲突,必须在配置CORS中间件之前声明JSONP的接口。否则JSONP接口会被处理成开启了CORS的接口。示例代码如下:
请添加图片描述
实现JSONP接口的具体代码
请添加图片描述
在网页中使用jQuery发起JSONP请求
调用$.ajax()函数,提供JSONP的配置选项,从而发起JSONP请求,示例代码如下:
请添加图片描述

十三、MySQL

1.什么是数据库

数据库(database) 是用来组织存储管理数据的仓库。
当今世界是一个充满着数据的互联网世界,充斥着大量的数据。数据的来源有很多,比如出行记录、消费记录、浏览的网页、发送的消息等等。除了文本类型的数据,图像、音乐、声音都是数据。
为了方便管理互联网世界中的数据,就有了数据库管理系统的概念(简称:数据库)。用户可以对数据库中的数据进行新增查询更新删除等操作。
传统型数据库的数据组织结构
在传统型数据库中,数据的组织结构分为数据库(database).数据表(table). 数据行(row)、字段(field)这 4大部分组成。

2、安装并配置MySQL

了解需要安装哪些MySQL相关的软件
对于开发人员来说,只需要安装MySQL Server和MySQL Workbench这两个软件,就能满足开发的需要了。
●MySQL Server: 专门用来提供数据存储和服务的软件。
●MySQL Workbench:可视化的MySQL管理工具,通过它,可以方便的操作存储在MySQL Server中的数据。

3、基本使用

连接数据库
请添加图片描述
创建数据库
请添加图片描述
创建数据表
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
向表中写入数据
请添加图片描述

4、SQL语句:select、insert、 update,、delete

什么是SQL
SQL (英文全称: Structured Query Language)是结构化查询语言,专门用来访问和处理数据库的编程语言。能够让我们以编程的形式,操作数据库里面的数据。
三个关键点:
①SQL 是一门数据库编程语言
②使用SQL语言编写出来的代码,叫做SQL语询
③SQL语言只能在关系型数据库中使用(例如MySQL、Oracle、 SQL Server)。非关系型数据库(例如Mongodb)不支持SQL语言
SQL的SELECT语句
1.语法
SELECT语句用于从表中查询数据。执行的结果被存储在一个结果表中(称为结果集)。语法格式如下:
请添加图片描述
请添加图片描述
请添加图片描述
SQL的INSERT INTO语句
1.语法
INSERT INTO语句用于向数据表中插入新的数据行,语法格式如下:
请添加图片描述
请添加图片描述
SQL的UPDATE语句
1.语法
Update语句用于修改表中的数据。语法格式如下:
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
SQL的DELETE语句
1.语法
DELETE语句用于删除表中的行。语法格式如下:
请添加图片描述
请添加图片描述

5、SQL语句-where子句、and和or运算符

语法
WHERE子句用于限定选择的标准。在SELECT、UPDATE、DELETE 语句中,皆可使用WHERE子句来限定选择的标准。
请添加图片描述
可在WHERE子句中使用的运算符
下面的运算符可在WHERE子句中使用,用来限定选择的标准:
请添加图片描述
注意在某些版本的SQL中,操作符<>可以写为!=
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述

6、SQL语句—排序

SQL的ORDER BY(order by)子句
ORDER BY语句用于根据指定的列对结果集进行排序。
ORDER BY语句默认按照升序对记录进行排序。
如果您希望按照降序对记录进行排序,可以使用DESC关键字。
请添加图片描述
ORDER BY子句-多重排序
请添加图片描述

7、SQL语包-count函数和as关键字

COUNT(*)函数用于返回查询结果的总数据条数,语法格式如下:
请添加图片描述
请添加图片描述
使用AS为列设置别名
如果希望给查询出来的列名称设置别名,可以使用AS关键字,示例如下:
请添加图片描述

8、安装并配置mysq|模块

在项目中操作数据库的步骤
安装操作MySQL数据库的第三方模块(mysq|)
②通过mysql模块连接到MySQL数据库
③通过mysql模块执行SQL语句
请添加图片描述
安装mysq|模块
mysql模块是托管于npm上的第三方模块。它提供了在Node.js项目中连接和操作MySQL数据库的能力。想要在项目中使用它,需要先运行如下命令,将mysql安装为项目的依赖包:

npm i mysql -y

配置mysql模块
在使用mysql模块操作MySQL数据库之前,必须先对mysql模块进行必要的配置,主要的配置步骤如下:
请添加图片描述
测试mysql模块能否正常工作
调用db.query()函数,指定要执行的SQL语句,通过回调函数拿到执行的结果:
请添加图片描述

9、mysq|模块基本使用

// 1.导入 MySQL 模块
const mysql = require('mysql')
// 2.建立与 MySQL 数据车的连接
const db = mysql.createPool({
	host:'127.0.0.1:3306',	// 数据库的IP地址
	user:'root',			// 登录数据库的账号
	password:'123456',		// 登录数据的密码
	database:'my_db_01'		// 指定要操作哪个数据库
    // port:'3306'			   默认为3306,所以可以不写
})
// 检查 MySQL 模块能香正常工作
db.query('SELECT 1',(err,results)=>
	if (err)return console.log(err.message)
	// 只要能打印出【RowDataPacket{'1':1}】的结果,就证明数据库连接正常
	console.log(results)
})

10、查询数据

查询users表中所有的数据:

// 1.导入 MySQL 模块
const mysql = require('mysql')
// 2.建立与 MySQL 数据车的连接
const db = mysql.createPool({
	host:'127.0.0.1',	// 数据库的IP地址
	user:'root',			// 登录数据库的账号
	password:'123456',		// 登录数据的密码
	database:'my_db-01'		// 指定要操作哪个数据库
    // port:'3306'			   默认为3306,所以可以不写
})

const strSql = 'select * from users' // users是我的数据库表
db.query(strSql,(err,results)=>{
    //查询数据失败
    if(err) console.log(err)
    //查询数据失败
    console.log(results)
})

请添加图片描述

11、插入数据

//定义待执行的sql语句
const strSql = 'insert into users(username,password) value (?,?) ' // users是我的数据库表 ?代表占位符
//向users表中新增一条数据
const user = { username: 'ddd', password: '123456'}	
//执行sql语句				
db.query(strSql, [user.username,user.password], (err, results) => {
    //执行sql失败
  if (err) console.log(err)
  //成功
  console.log(results)
})

插入数据的便捷方式
向表中新增数据时,如果数据对象的每个属性和数据表的字段- --对应,则可以通过如下方式快速插入数据:
请添加图片描述
请添加图片描述

12、更新数据

可以通过如下方式,更新表中的数据:
请添加图片描述
请添加图片描述
更新数据的便捷方式
更新表数据时,如果数据对象的每个属性和数据表的字段一-对应,则可以通过如下方式快速更新表数据:
请添加图片描述

13、删除数据

在删除数据时,推荐根据id这样的唯一标识,来删除对应的数据。示例如下:
请添加图片描述
标记删除
使用DELETE语句,会把真正的把数据从表中删除掉。为了保险起见,推荐使用标记删除的形式,来模拟删除的动作
所谓的标记删除,就是在表中设置类似于status这样的状态字段,来标记当前这条数据是否被删除。
当用户执行了删除的动作时,我们并没有执行DELETE语句把数据删除掉,而是执行了UPDATE语询,将这条数据对应的status字段标记为删除即可。
请添加图片描述

14、简单封装

我以自己的思路封装一下

// mysql.js 封装的插入函数
const mysql = require('mysql')

// 连接数据库
const db = mysql.createPool({
  host: '127.0.0.1',
  user: 'root',
  password: '',
  database: 'bookshop',
})

// 向test表插入name
function add(data) {
  let strSql = 'insert into test (name) value (?)'
  return new Promise((resolve, reject) => {
    db.query(strSql, [data], async (err, results) => {
      if (err) reject(err.message)
      resolve(results)
    })
  })
}

module.exports = add

// app.js
const express = require('express')
const app = express()
const add = require('./mysql')

// 解析 json 数据
app.use(express.json())

app.post('/', (req, res) => {
  add(req.body.name)
    .then(() => {
      res.send({ code: '', message: '插入成功' })
    })
    .catch(() => {
      res.send({ code: '1001', message: '插入失败' })
    })
})

// 捕捉异常中间件
app.use((err, req, res, next) => {
  console.log('发生了错误!' + err.message)
  res.send('Error!' + err.message) // 捕获错误,向客户端响应
})

app.listen(80, () => {
  console.log('http://127.0.0.1')
})

在这里插入图片描述
在这里插入图片描述

十四、web开发模式与身份认证介绍

前后端分离的优缺点

优点:
开发体验好。 前端专注于UI页面的开发,后端专注于api的开发,且前端有更多的选择性。
用户体验好。 Ajax 技术的广泛应用,极大的提高了用户的体验,可以轻松实现页面的局部刷新。
减轻了服务器端的渲染压力。 因为页面最终是在每个用户的浏览器中生成的。
缺点:
不利于 SEO.因为完整的HTML页面需要在客户端动态拼接完成,所以爬虫对无法爬取页面的有效信息。(解决方案:利用Vue、React 等前端框架的SSR (server side render)技术能够很好的解决SEO问题! )

如何选择Web开发模式

●比如企业级网站,主要功能是展示而没有复杂的交互,并且需要良好的SEO,则这时我们就需要使用服务器端渲染;
●而类似后台管理项目,交互性比较强,不需要考虑SEO,那么就可以使用前后端分离的开发模式。
另外,具体使用何种开发模式并不是绝对的,为了同时兼顾了首页的渲染速度和前后端分离的开发效率,-些网站采用了首屏服务器端渲染+其他页面前后端分离的开发模式。

不同开发模式下的身份认证

对于服务端渲染和前后端分离这两种开发模式来说,分别有着不同的身份认证方案:
①服务端渲染推荐使用 Session认证机制
②前后端分离推荐使用JWT认证机制

十五、前后端身份认证

1. Session

中间件,配合cookie使用,默认不支持跨域
(1)安装

npm i express-session 

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

var session = require('express-session')

app.use(session({
    secret:'keyboard cat',    // secret值可为任意字符串
    resave:false,
    saveUninitialized:true
}))

请添加图片描述
(3)存储 session 数据
当express-session中间件配置成功后,即可通过req.session来访问和使用session对象,从而存储用户的关键信息:

app.post('/api/login',(req,res)=>{
	//判断用户提交的登录信息是否正确
	if (req.body.username !=='admin'|| req.body.password !=='000000'){
		return res.send({status:1,msg:'登录失败'})
    }
	req.session.user = req.body
	//将用户的信息,存储到Session中
	req.session.islogin = true
	//将用户的登录状态,存储到Session中
	res.send({status:0,msg:'登录成功'})
})

(4)读取 session 数据

//获取用户姓名的接口
app.get('/api/username',(req,res)=>{
	//判断用户是否登录
	if (!req.session.islogin){
		return res.send({status:1,msg:'fail'})
    }
	res.send({status:0,msg:'success',username:req.session.user.username }
})

(5)清空 session 数据
调用req.session.destroy()函数,即可清除服务器保存的session信息

//退出登录的接口
app.post('/api/logout',(req,res)=>
	//清空当前客户端对应的session信息
    req.session.destroy()
	res.send({
		status:0,
		msg:'退出登录成功'
	})
})

2. JWT

了解Session认证的局限性
Session认证机制需要配合Cookie才能实现。由于Cookie默认不支持跨域访问,所以,当涉及到前端跨域请求后端接口的时候,需要做很多额外的配置,才能实现跨域Session认证。
注意:
●当前端请求后端接口不存在跨域问题的时候, 推荐使用Session身份认证机制。
●)当前端需要跨域请求后端接口的时候,不推荐使用Session身份认证机制,推荐使用JWT认证机制。
JWT的工作原理
总结:用户的信息通过Token字符串的形式,保存在客户端浏览器中。服务器通过还原Token字符串的形式来认证用户的身份。
JWT的三个部分各自代表的含义
JWT的三个组成部分,从前到后分别是Header、Payload、Signature.
其中:
Payload 部分才是真正的用户信息,它是用户信息经过加密之后生成的字符串。
●Header 和Signature是安全性相关的部分,只是为了保证Token的安全性。

Header.Playload.Signature

(1)安装

# jsonwebtoken 用于生成 JWT 字符串
# express-jwt 用于将 JWT 字符串解析还原成JSON对像
npm i jsonwebtoken express-jwt

(2)导入

//1,导入用于生成 JWT 字符串的包
const createJWT = require('jsonwebtoken')
//2.导入用于将客户端发送过来的 JWT 字符串,解析还原成JSON对象的包
var { expressjwt: jwt } = require('express-jwt')

(3)定义 Secret 密钥
为了保证 JWT 字符串的安全性,防止 JWT 字符串在网络传输过程中被别人破解,我们需要专门定义一个用于加密解密的 secret 密钥:

  • 当生成 JWT 字符串的时候,需要使用secret密钥对用户的信息进行加密,最终得到加密好的 JWT 字符串
  • 当把 JWT 字符串解析还原成 JSON 对象的时候,需要使用 secret 密钥进行解密
// 密钥为字符串
const secretKey = 'dddhl ^_^'

(4)登录后生成 JWT 字符串

// 登录接口
app.post('/api/login',function(req,res){
	// 省略登录失败情况下的代码
	// 用户登录成功之后,生成WT字符串,通过token属性响应给客户端
	res.send({
		status:200,
		message:'登录成功',
		// 调用 jwt.sign() 生成 JWT 字符串,三个参数分别是:用户信息对象、加座密钥、配置对象
        // 2h 代表token有效期2小时,s为秒
		token:createJWT.sign({username:'lhd' },secretKey,{expiresIn:'2h'})
	})
})

(5)将 JWT 解析还原成 JSON 对象
请求时,请在 Authorization 字段将 token 发送到服务器

// 使用app.use()来注册中间件
// .unless({ path: [/^\/api\//] })用来指定哪些接口不需要访问权限 这里表示的是 api开头的接口都不需要token
app.use(
  jwt({
    secret: secretKey,
    algorithms: ['HS256'],
  }).unless({ path: [/^\/api\//] })
)

(6)获取解析还原的 JSON 对象
解析完成后,req.auth = 解析出来的对象

app.get('/admin/getinfo',(req,res)=>{
	console.log(req.auth)
	res.send({
		status:200,
		message:'获取用户信息成功l
		data:req.auth
    })
})

(7)处理token过期

app.use(function (err, req, res, next) {
  if (err.name === 'UnauthorizedError') {
    return res.send({ status: 401, message: '登录信息已过期' })
  }
  return res.send({ status: 400, message: '其他错误' })
})

(8)完整演示
请求登录时发送 username ,不用token。

服务端生成token返回给客户端。

客户端请求解析token接口,需要token。

注意:请求token接口时,Header里需要 Authorization: 'Bearer ’ + token 注意 Bearer后面有个空格隔开token

const express = require('express')
const createJWT = require('jsonwebtoken')
var { expressjwt: jwt } = require('express-jwt')
const app = express()

// token密钥
const secretKey = 'dddhl ^_^'

// json解析
app.use(express.json())

// token解析
app.use(
  jwt({
    secret: secretKey,
    algorithms: ['HS256'],
  }).unless({ path: [/^\/api\//] })
)

// 登录接口,自动生成token,接收一个username存进token
app.post('/api/login', (req, res) => {
  let token = createJWT.sign({ username: req.body.username }, secretKey, {
    expiresIn: '30s',
  })
  res.send({
    status: 200,
    message: '登录成功',
    token: token,
  })
})

// 测试token解析,发送解析后的数据,请求时需要token
app.get('/token', (req, res) => {
  console.log(req.auth)
  res.send({
    status: '200',
    message: '检测token解析成功',
    data: req.auth,
  })
})

// 处理token过期
app.use(function (err, req, res, next) {
  if (err.name === 'UnauthorizedError') {
    return res.send({ status: 401, message: '登录信息已过期' })
  }
  return res.send({ status: 400, message: '其他错误' })
})

app.listen(80, () => {
  console.log('http://127.0.0.1')
})

登录后生成token:
在这里插入图片描述
获取解析后的token数据:
在这里插入图片描述

文章来源

https://blog.csdn.net/DDDHL_/article/details/124390573
https://www.bilibili.com/video/BV1a34y167AZ
http://www.escook.cn:8088/

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值