Node.js-1

news2024/11/15 15:58:48

Node.js 简介

定义Node.js 是一个跨平台 JavaScript 运行环境,使开发者可以搭建服务器端的 JavaScript 应用程序

为什么 Node.js 能执行 JS 代码

  • Chrome 浏览器能执行 JS 代码,依靠的是内核中的 V8引擎(即:C++程序)

在这里插入图片描述

  • Node.js 是基于 Chrome V8 引擎进行封装的
  • 注意:Node.js 环境没有 DOM 和 BOM 等内容,但是它也有自己独立的 API

模块化

基本认知

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

  • 内置模块(内置模块是由 Node.js 官方提供的,例如fs、path、http等)
  • 自定义模块(用户创建的每一个 .js 文件,都是自定义模块)
  • 第三方模块(由第三方开发出来的模块,并非官方提供的内置模块,也不是用户创建的自定义模块,使用前需要提前下载)

加载模块

使用强大的 require() 方法,可以加载需要的内置模块、用户自定义模块、第三方模块进行使用(注意:使用 require() 方法加载其他模块时,会执行被加载模块中的代码)

// 加载内置模块
const fs = require('fs')

// 加载用户自定义的模块
const myModule = require('./myModule.js')

// 加载第三方模块
const moment = require('moment')

module 对象

  • module 对象

每个自定义的模块中都有一个 module 对象,它里面存储了和当前模块有关的信息(打印结果,如下所示)

Module {
  id: '.',
  path: 'E:\\Desktop\\jsProject',
  exports: {},
  filename: 'E:\\Desktop\\jsProject\\test.js',
  loaded: false,
  children: [],
  paths: [
    'E:\\Desktop\\jsProject\\node_modules',
    'E:\\Desktop\\node_modules',
    'E:\\node_modules'
  ]
}
  • module.exports 对象
在自定义模块中,可以使用 module.exports 对象,将模块内的成员共享出去,供外界使用

外界用 require() 方法导入自定义模块时,得到的就是 module.exports 所指向的对象
【myModule.js文件】

module.exports.username = 'Jack'
module.exports.sayHello = () => { console.log('Hello') }
【text.js文件】

const m = require('./myModule')
console.log(m)	// { username: 'Jack', sayHello: [Function (anonymous)] }

模块化规范

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

CommonJS 规定:

  • 每个模块内部,module 变量代表当前模块
  • module 变量是一个对象,它的 exports 属性(即:module.exports)是对外的接口
  • 加载某个模块,其实是加载该模块的 module.exports 属性。require() 方法用于加载模块

npm与包

基本介绍

  • Node.js 中的第三方模块又叫做包
  • npm 公司提供了一个包管理工具,名字叫做 Node Package Manager(简称 npm 包管理工具),这个工具随着 Node.js 的安装被一起安装到了用户的电脑上
  • npm 包管理工具能够从 https://registry.npmjs.org/ 服务器把用户需要的包(第三方模块)下载到本地使用
  • 可以在终端执行 npm -v 命令,来查看自己电脑上所安装的 npm 包管理工具的版本号
  • 如果有需要的话,可以从"全球最大的包共享平台"搜索自己想要的包:https://www.npmjs.com/

npm 体验

目标:通过npm包管理工具,下载 moment 包,并输出格式化时间

  • 在终端输入npm命令,下载 moment 包
npm install moment
  • 简写
npm i moment
  • 指定版本号(不指定版本号,默认安装最新版本的包)
npm i moment@2.22.2
  • 输出格式化时间(详细操作,自行查阅官方文档)
// 1. 导入 moment 包
const moment = require('moment')

// 2. 使用包里面的方法
const dt = moment.format('YYYY-MM-DD HH:mm:ss')
console.log(dt)	// 输出格式化时间

注意事项

  • 初次装包完成后,在项目文件夹下多一个叫做 node_modules 的文件夹和 package-lock.json 的配置文件

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

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

  • 注意:不要手动修改 node_modules 或 package-lock.json 文件中的任何代码,npm 包管理工具会自动维护它们

  • npm i moment@2.22.2 中的版本号由三位数字组成

每一位数字所代表的含义:
    第一位数字:大版本(例如:底层重构)
    第二位数字:功能版本(例如:新功能的推出)
    第三位数字:Bug修复版本(例如:修复某个Bug)

版本号提升的规则:
	只要前面的版本号增长了,后面的版本号"归零"

包管理配置文件

基本认知

npm 规定,在项目根目录中,必须提供一个叫做 package.json 的包管理配置文件。用来记录与项目有关的一些配置信息,例如:
	- 项目的名称、版本号、描述等
	- 项目中都用到了哪些包
	- 哪些包只在开发期间会用到
	- 哪些包在开发和部署时都需要用到
问题:
	在多人协作时,第三方包的体积过大,不方便团队成员之间共享项目源代码

解决方案:
	共享时剔除 node_modules 目录,让其他成员根据 package.json 配置文件,自行下载第三方模块(注意:今后在项目开发中,一定要把 node_modules 文件夹,添加到 .gitignore 忽略文件中)

快速创建

  • npm 包管理工具提供了一个快捷命令,可以在执行命令时所处的目录中,快速创建 package.json 配置文件
  • 该命令只能在英文的目录下成功运行
  • 当我们后续运行 npm install 命令去安装其他的包时,npm 包管理工具会自动更新配置文件的信息,我们不需要手动修改它
npm init -y

一次性安装所有包

  • 执行 npm install 命令时,npm 包管理工具会先自动读取 package.json 配置文件中的 dependencies 节点
  • 读取到记录的所有依赖包名称和版本号之后,npm 包管理工具会把这些包一次性下载到项目中
npm install

卸载包

  • 命令执行成功后,会把卸载的包,自动从 package.json 的 dependencies 中移除掉
npm uninstall 包的名称

devDependencies 节点

  • 如果某些包只在项目开发阶段用到,在项目上线之后不会使用,建议把这些包记录到 devDependencies 节点中
npm i 包的名称 -D

或者

npm i 包的名称 --save-dev

npm 镜像源

# 查看当前的下包镜像源
npm config get registry

# 将下包的镜像源切换成淘宝镜像源
npm config set registry https://registry.npmmirror.com

# 检查镜像源是否下载成功
npm config get registry

Node.js 内置模块

fs 文件系统模块

// 1. 导入 fs 模块对象
const fs = require('fs')

// 2. 写入文件
//      参数1:要写入内容的文件的存放路径
//      参数2:表示要写入的内容
//      参数3:写入的格式(默认utf8)
//      参数3:回调函数
const info = 'Hello World'
fs.writeFile('./test.txt', info, 'utf8', function (err) {
    if (err) console.log(`错误信息${err}`)
    else console.log(`写入数据成功(数据信息:${info}`)

    // 3. 读取文件
    //      参数1:读取文件的存放路径
    //      参数2:读取文件时候采用的编码格式(默认utf8)
    //      参数3:回调函数,拿到读取失败和成功的结果
    fs.readFile('./test.txt', 'utf8', (err, data) => {
        if (err) console.log(`错误信息${err}`)
        else console.log(`读取数据成功(数据信息:${data.toString()}`) // data 是 buffer 16 进制数据流对象,需要转换为字符串格式
    })
})

path 路径模块

  • 获取当前文件所处的目录
// __dirname 表示获取当前文件所处的目录(文件夹)
console.log(__dirname)
  • 路径拼接
// 导入 path 模块
const path = require('path')

// 注意: ../会抵消前面的路径
const pathStr = path.join('/a', '/b/c', '../', '/d', '/e')
console.log(pathStr)    // 输出路径为:\a\b\d\e
  • 获取文件名、扩展名
// 导入 path 模块
const path = require('path')

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

// 从"存放路径"中获取文件名(包含扩展名部分)
const fileName1 = path.basename(filePath)
console.log(fileName1)   // 输出结果:index.html

// 从"存放路径"中获取文件名(不包含扩展名部分)
const fileName2 = path.basename(filePath, '.html')
console.log(fileName2)   // 输出结果:index

// 从"存放路径"中获取文件扩展名
const extName = path.extname(filePath)
console.log(extName)    // 输出结果:.html

http 模块

// 1. 导入 http 模块
const http = require('http')
const { resolve } = require('path')

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

// 3. 为服务器实例绑定 request 事件
// req 是请求对象,包含了与客户端相关的数据和属性
// res 是响应对象
// res.end() 方法用于向客户端发送指定的内容,并结束本次请求的处理过程
server.on('request', (req, res) => {
    // req.url 是客户端请求的 URL 地址
    const url = req.url
    // req.method 是客户端请求的 method 类型
    const method = req.method
    // 为了防止中文乱码的问题,需要设置响应头
    res.setHeader('Content-Type', 'text/html; charset=utf-8')
    // res.end() 方法
    res.end(`<h1>您的请求方式为:${method} 请求</h1>`)
})

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

第三方 API 模块

express

基本介绍

  • Express 是基于 Node.js 平台的 Web 开发框架,专门用来创建 Web 服务器
  • Express 本质上就是一个第三方模块,相较与 Node.js 内置的 http 模块,它使用起来更简单,开发效率更高(Express 是基于 http 模块进一步封装出来的)
  • Express 中文官网:https://www.expressjs.com.cn/

模块使用

  • 安装
npm i express@4.17.1
  • 创建 Web 服务器
// 1. 导入 express
const express = require('express')

// 2. 创建 web 服务器
const app = express()

// 4. 监听客户端的 GET 和 POST 请求
// GET      app.get('请求URL', (请求对象, 响应对象) => { 处理函数 })
// POST     app.post('请求URL', (请求对象, 响应对象) => { 处理函数 })
app.get('/user', (req, res) => {
    res.send({ name: 'Jack', age: 20, gender: '男' })    // 响应一个 JSON 对象
})

app.post('/user', (req, res) => {
    res.send('订单提交成功,请等待商家发货')    // 响应
})

// 5. 获取URL携带的查询参数
app.get('/', (req, res) => {
    // 默认情况下,req.query 是一个空对象
    // 查询参数 ?name=Jack&age=20
    // 可通过 req.query.name 与 req.query.age 获得其值
    console.log(req.query)
    res.send(req.query)
})

// 6. 获取URL中的动态参数
app.get('/user/:id/:name', (req, res) => {
    // req.params 默认是一个空对象,里面存放着,通过冒号识别出来的参数值
    res.send(req.params)
})

// 3. 调用 app.listen(端口号, 启动成功后的回调函数)
app.listen(80, () => {
    console.log('Server started successfully at http://127.0.0.1/')
})

托管静态资源

  • 认识 express.static()
介绍:
    express 提供了一个非常好用的函数,叫做 express.static()
    通过它,我们可以非常方便地创建一个静态资源服务器

例如:
	项目目录下有一个 public 文件夹,里面存放了图片、html、css、js文件,通过下面的代码就可以将它们对外开放访问了

注意:
	express 在指定的静态文件中查找文件,并对外提供资源的访问路径,因此,存放静态文件的目录名(本例中为public)是不会出现在URL中的
    http://127.0.0.1/images/img0.jpg
    http://127.0.0.1/html/index.html
    http://127.0.0.1/css/index.css
    http://127.0.0.1/js/index.js
app.use(express.static('public'))
  • 托管多个静态资源目录
app.use(express.static('public'))
app.use(express.static('files'))
......
  • 挂载路径前缀
如果希望在托管的静态资源访问路径之前,挂载路径前缀,可以使用下面的代码
	http://127.0.0.1/public/images/img0.jpg
    http://127.0.0.1/public/html/index.html
    http://127.0.0.1/public/css/index.css
    http://127.0.0.1/public/js/index.js
app.use('public', express.static('public'))

nodemon

  • 为什么要使用 nodemon
在编写调试 Node.js 项目的时候,如果修改了项目的代码,则需要频繁的手动 close 掉,然后再重新启动,非常繁琐

为了解决这个问题,我们可以使用 nodemon 这个工具,它能够监听项目文件的变动,当代码修改后,nodemon 会自动帮我们重启项目,极大方便了开发和调试
  • 安装 nodemon
在终端中运行下面命令,即可将 nodemon 安装为全局可用的工具

npm install -g nodemon
  • 使用 nodemon
nodemon 要执行的js文件的路径
例如:nodemon E:\Desktop\jsProject\test.js

路由

直接挂载路由

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

// 直接在app上挂载路由
app.get('/', (req, res) => { res.send('this is get') })
app.post('/', (req, res) => { res.send('this is post') })

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

模块化的方式,挂载路由,实现解耦合

  • 文件名:routerModule
// 这是路由模块

// 1. 导入 express 模块
const express = require('express')

// 2. 创建路由对象
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
  • 文件名:myApplication
// 这是 app (application-应用)

// 1. 导入 express 模块
const express = require('express')

// 2. 创建应用
const app = express()

// 4. 导入路由模块
const router = require('./routerModule')

// 5. "注册"路由模块
app.use('/user', router)
// app.use(express.static('public'))
// 注意:app.use() 函数的作用,就是用来"注册"全局中间件

// 3. 运行服务器,监听应用的80端口
app.listen(80, () => { console.log('Server is running at http://127.0.0.1/') })

中间键

  • 基本认识
// 1. 导入 express 模块
const express = require('express')

// 2. 创建应用
const app = express()

// 4. 定义一个中间件函数
const mw = (req, res, next) => {
    console.log('这是最简单的中间件函数')
    next()      // 把流转关系,转交给下一个中间件或路由
}

// 5. 将 mw 注册为全局生效的中间件
// 当注册成功后,用户每次请求服务时,都会先执行中间键函数里面的内容,再执行路由函数里面的内容
app.use(mw)

// 6. 创建路由
app.get('/', (req, res) => { res.send('home page') })
app.get('/user', (req, res) => { res.send('user page') })

// 3. 运行服务器,监听应用的80端口
app.listen(80, () => { console.log('Server is running at http://127.0.0.1/') })
  • 体验它的作用
// 1. 导入 express 模块
const express = require('express')

// 2. 创建应用
const app = express()

// 4. 注册全局中间件
app.use((req, res, next) => {
    // 获取请求到达服务器的时间
    const time = Date.now()
    // 为req对象,挂载自定义属性,从而把时间共享给后面的所有路由
    req.startTime = time
    next()
})

// 5. 创建路由
app.get('/', (req, res) => { res.send('home page ' + req.startTime) })
app.get('/user', (req, res) => { res.send('user page ' + req.startTime) })

// 3. 运行服务器,监听应用的80端口
app.listen(80, () => { console.log('Server is running at http://127.0.0.1/') })
  • 定义多个全局中间件
// 1. 导入 express 模块
const express = require('express')

// 2. 创建应用
const app = express()

// 4. 注册全局中间件
// 4.1 注册第一个中间件
app.use((req, res, next) => {
    console.log('调用了第1个中间件')
    next()
})

// 4.2 注册第二个中间件
app.use((req, res, next) => {
    console.log('调用了第2个中间件')
    next()
})

// 4.3 注册第三个中间件
app.use((req, res, next) => {
    console.log('调用了第3个中间件')
    next()
})

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

// 3. 运行服务器,监听应用的80端口
app.listen(80, () => { console.log('Server is running at http://127.0.0.1/') })
  • 局部生效的中间件
// 1. 导入 express 模块
const express = require('express')

// 2. 创建应用
const app = express()

// 4. 定义一个中间件函数
const mw = (req, res, next) => {
    console.log('调用了局部生效的中间件')
    next()
}

// 5. 创建路由
app.get('/', mw, (req, res) => { res.send('home page') })  // 使用了局部中间件
app.get('/user', (req, res) => { res.send('user page') })   // 没有使用局部中间件

// 3. 运行服务器,监听应用的80端口
app.listen(80, () => { console.log('Server is running at http://127.0.0.1/') })
  • 定义多个局部中间件
// 以下两种写法是等效的
app.get('/', mw1, mw2, (req, res)=>{ res.send('home page') })
app.get('/', [mw1, mw2], (req, res)=>{ res.send('home page') })
  • 错误级别的中间件
// 1. 导入 express 模块
const express = require('express')

// 2. 创建应用
const app = express()

// 4. 定义路由
app.get('/', (req, res) => {
    throw new Error('The Error made by People.')     // 人为制造错误
    res.send('home page')   // 此处无法执行
})

// 5. 定义错误级别的中间件,捕获整个项目的异常错误,从而防止程序的崩溃
app.use((err, req, res, next) => {
    console.log('警告!服务器发生了错误。\n错误信息:' + err.message)
    res.send('尊敬的用户,很抱歉,服务器出错了~')
})	// 错误级别的中间件一定要放在所有路由之后

// 3. 运行服务器,监听应用的80端口
app.listen(80, () => { console.log('Server is running at http://127.0.0.1/') })
  • express 内置的中间件
自从 Express 4.16.0 版本开始,Express 内置了3个常用的中间件,极大的提高了 Express 项目的开发效率和体验
	1. express.static 快速托管静态资源的内置中间件,例如:HTML文件、CSS样式、图片等
	2. express.json 解析 JSON 格式的请求体数据 (4.16.0+ 版本可用)
	3. express.urlencoded 解析 URL-encoded 格式的请求体数据 (4.16.0+ 版本可用)
// 1. 导入 express 模块
const express = require('express')

// 2. 创建应用
const app = express()

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

// 5. 创建路由
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)
    res.send('ok')
})

// 3. 运行服务器,监听应用的80端口
app.listen(80, () => { console.log('Server is running at http://127.0.0.1/') })

跨域访问

  • 下载并安装 cors 中间件
npm i cors@2.8.5
  • 使用 cors 中间件
// 导入 cors 中间件
const cors = require('cors')
// 将 cors 注册为全局中间件
app.use(cors())

跨域资源共享

点击链接:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS

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: 'admin123',   // 登录数据库的密码
    database: 'myDatabase'  // 指定要操作哪个数据库
})
  • 测试
// 检测 mysql 模块能否正常工作
db.query('select 1', (err, results) => {
    if (err) return console.log(err.message)    // mysql 模块工作期间报错了
    console.log(results)    // 能够成功的执行SQL语句
})

查询与插入数据

  • 查询数据
// 1. 导入 mysql 模块
const mysql = require('mysql')

// 2. 建立与 MySQL 数据库的连接
const db = mysql.createPool({
    host: '127.0.0.1',      // 数据库的IP地址
    user: 'root',           // 登录数据库的账号
    password: 'admin123',   // 登录数据库的密码
    database: 'myDatabase'  // 指定要操作哪个数据库
})

// 3. 查询 users 表中所有的数据
const sqlStr = 'select * from users'
db.query(sqlStr, (err, results) => {
    // 查询数据失败
    if (err) return console.log(err.message)
    // 查询数据成功
    console.log(results)
})
  • 插入数据
// 1. 导入 mysql 模块
const mysql = require('mysql')

// 2. 建立与 MySQL 数据库的连接
const db = mysql.createPool({
    host: '127.0.0.1',      // 数据库的IP地址
    user: 'root',           // 登录数据库的账号
    password: 'admin123',   // 登录数据库的密码
    database: 'myDatabase'  // 指定要操作哪个数据库
})

// 3. 向 users 表中插入数据
const user = { username: 'Jack', password: '123456' }
const sqlStr = 'insert into users set ?'
db.query(sqlStr, user, (err, results) => {
    if (err) return console.log(err.message)    // 执行SQL语句失败了
    if (results.affectedRows === 1) { console.log('插入数据成功!') }
})

更新与删除数据

  • 更新数据
// 1. 导入 mysql 模块
const mysql = require('mysql')

// 2. 建立与 MySQL 数据库的连接
const db = mysql.createPool({
    host: '127.0.0.1',      // 数据库的IP地址
    user: 'root',           // 登录数据库的账号
    password: 'admin123',   // 登录数据库的密码
    database: 'myDatabase'  // 指定要操作哪个数据库
})

// 3. 更新 users 表中数据
const user = { id: 6, username: 'Jack', password: '123456' }
const sqlStr = 'update users set ? where id=?'
db.query(sqlStr, [user, user.id], (err, results) => {
    if (err) return console.log(err.message)    // 执行SQL语句失败了
    if (results.affectedRows === 1) { console.log('更新数据成功!') }
})
  • 删除数据
// 1. 导入 mysql 模块
const mysql = require('mysql')

// 2. 建立与 MySQL 数据库的连接
const db = mysql.createPool({
    host: '127.0.0.1',      // 数据库的IP地址
    user: 'root',           // 登录数据库的账号
    password: 'admin123',   // 登录数据库的密码
    database: 'myDatabase'  // 指定要操作哪个数据库
})

// 3. 删除 users 表中的数据(删除id=5的用户数据)
const sqlStr = 'delete from users where id=?'
db.query(sqlStr, 5, (err, results) => {
    if (err) return console.log(err.message)    // 执行SQL语句失败了
    if (results.affectedRows === 1) { console.log('删除数据成功!') }
})
标记删除法:(扩展)
	使用 delete 语句,是真的会把数据从表中抹除掉。为了保险起见,推荐使用标记删除法,来模拟删除的动作。
	什么是标记删除法呢?就是在创建表的时候,提前建立好一个 status 状态字段,用来表示当前这条数据是否被删除掉,当我们使用标记删除法后,哪怕用户删除数据了,有一天用户需要找回来,也能找到这条数据。

Web 开发模式

目前主流的开发模式有两种

  • 基于服务端渲染的传统 Web 开发模式
  • 基于前后端分离的新型 Web 开发模式

服务端渲染

概念

服务端发送给客户端的HTML页面,是在服务器通过字符串的拼接,动态生成的。因此,客户端不需要使用Ajax这样的技术额外请求页面的数据。

优点

  • 前端耗时少(因为服务器端负责动态生成HTML内容,浏览器只需要直接渲染即可,尤其是移动端,更省电)
  • 有利于SEO(因为服务器端响应的是完整的HTML页面内容,所以爬虫更容易获取信息,更有利于SEO)

缺点

  • 占用服务器资源(服务器端完成HTML页面内容的拼接,如果请求较多,会对服务器造成一定的访问压力)
  • 不利于前后端分离,开发效率低(使用服务器端渲染,则无法分工合作,尤其对于前端项目复杂度高的项目,不利于项目高效开发)

前后端分离

概念

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

优点

  • 开发体验好。(前端专注于 UI 页面的开发,后端专注于 API 的开发,而且前端有更多的选择性)
  • 用户体验好。(Ajax 技术的广泛应用,极大的提高了用户的体验 ,可以轻松实现页面的局部刷新)
  • 减轻了服务器的压力(页面最终是在每个用户的浏览器中生成的)

缺点

  • 不利于 SEO (因为完整的HTML页面需要在客户端动态拼接完成,所以爬虫难以爬取页面的有效信息)
解决方案:
	利用 Vue、React 等前端框架的 SSR (server side render) 技术能够很好的解决 SEO 问题

身份认证

基本认识

介绍:
	身份认证(Authoritarian)又称"身份验证"、"鉴权",它是指通过一定的手段,完成对用户身份的确认

例如:
	手机验证码登录、邮箱密码登录、二维码登录等
不同开发模式下的身份认证
	1.服务端渲染推荐使用 Session 认证机制
	2.前后端分离推荐使用 JWT 认证机制

Session 认证机制

Cookie

  • HTTP 无状态性质
HTTP 协议的无状态性质,指的是客户端每次的 HTTP 请求都是独立的,连续多个请求之前没有直接的关系,服务器不会主动保留每次 HTTP 请求的状态
  • 突破 HTTP 无状态的限制
使用 Cookie 可以弥补 HTTP 无法记录状态的问题
  • 什么是 Cookie
Cookie 是存储在用户浏览器中的一段不超过 4KB 的字符串
它由一个名称(Name)、一个值(value)和其他几个用于控罪 Cookie 有效期、安全性、使用范围的可选属性组成
不同域名下的 Cookie 各自独立,每当客户端发起请求时,会自动把当前域名下所有未过期的 Cookie 一同发送到服务器
  • Cookie 的几大特性
1.自动发送
2.域名独立
3.过期时限
4.4KB限制
  • Cookie 在身份认证中的作用
客户端第一次请求服务器的时候,服务器通过响应头的形式,向客户端发送一个身份认证的 Cookie,客户端会自动将 Cookie 保存在浏览器中
随后,当客户端浏览器每次请求服务器的时候,浏览器会自动将身份认证相关的 Cookie,通过请求头的形式发送给服务器,服务器即可验证客户端的身份
  • Cookie 不具有安全性
由于 Cookie 是存储在浏览器中的,而且浏览器也提供了读写 Cookie 的 API,因此 Cookie 很容易被伪造,不具有安全性。因此不建议服务器将重要的隐私数据,通过 Cookie 的形式发送给浏览器

Session

  • Session = Cookie + 认证
  • Session 的工作原理

在这里插入图片描述

实现 Session 认证

  • 安装 express-session 中间件
npm install express-session
  • 配置 express-session 中间件
// 1. 导入 session 中间件
const session = require('express-session')

// 2. 配置 Session 中间件
app.use(session({
    secret: 'keyboard cat',     // secret 属性的值可以为任意字符串
    resave: false,              // 固定写法
    saveUninitialized: true     // 固定写法
}))
  • session 操作
当 express-session 中间件配置成功后,即可通过 req.session 来访问和使用 session 对象,从而存储用户的关键信息
// 1. 创建服务器实例
const express = require('express')
const app = express()

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

// 4. 创建路由

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

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

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

// 2. 启动服务器
app.listen(80, (req, res) => {
    console.log('Server is running at http://127.0.0.1/')
})
  • Session 认证的局限性
Session 认证机制需要配合 Cookie 才能实现。
由于 Cookie 默认不支持跨域访问,所以,当涉及到前端跨域请求后端接口时,需要做很多额外配置,才能实现跨域 Session 认证
当前端请求后端接口不存在跨域问题的时候,推荐使用 Session 身份认证机制
当前端需要跨域请求后端接口的时候,不推荐使用 Session 身份认证机制,推荐使用 JWT 认证机制

JWT 认证机制

简介

  • JWT(JSON Web Token)是目前最流行的跨域认证解决方案
  • 工作原理

在这里插入图片描述

  • **JWT **通常由三部分组成
Header		(头部)
Payload		(有效载荷)
Signature	(签名)
三者之间使用英文的"."进行分隔
Payload 部分才是真正的用户信息,它是用户信息经过加密之后生成的字符串
Header 和 Signature 是安全性相关的部分,只是为了保证 Token 的安全性
  • JWT 的使用方式
客户端收到服务器返回的 JWT 之后,通常会将它存储在 localStorage 或 sessionStorage 中
此后,客户端每次与服务器通信,都要带上这个 JWT 的字符串,从而进行身份认证
推荐的做法是把 JWT 放在 HTTP 请求头的 Authoritarian 字段中
格式如下:
Authorization: Bearer <token>

实现 JWT 认证

  • 安装 JWT 相关的包
jsonwebtoken 用于生成 JWT 字符串
express-jwt 用于将 JWT 字符串解析还原成 JSON 对象
npm install jsonwebtoken express-jwt
  • 使用 JWT
// 1. 创建服务器实例
const express = require('express')
const app = express()

// 3. 使用 JWT
// 3.1 安装并导入 JWT 相关的两个包
const jwt = require('jsonwebtoken')
const expressJWT = require('express-jwt')

// 3.2 设置 secret 密钥,保证 JWT 字符串的安全
const secretKey = 'text use ^_^'

// 3.4 将 JWT 字符串解析还原成 JSON 对象
// 注意:只要配置成功了 express-jwt 这个中间件,就可以把解析出来的用户信息,挂载到 req.user 属性上
const mw = expressJWT({ secret: secretKey }).unless({ path: [/^\/api\//] })
app.use(mw)     // 将这个中间件注册为全局中间件

// 获取用户信息的接口
app.get('/admin/getInfo', (req, res) => {
    // 3.5 使用 req.user 获取用户信息,并使用 data 属性将用户信息发送给客户端
    console.log(req.user)
    res.send({
        status: 200,
        message: "获取用户信息成功",
        data: req.user
    })
})

// 登录接口
app.post('api/login', (req, res) => {
    const userinfo = req.body
    // 登录失败
    if (userinfo.username !== 'admin' || userinfo.password !== '123456') {
        return res.send({ status: 400, message: '登录失败' })
    }
    // 登录成功

    // 3.3 登录成功后,生成 JWT 字符串,并通过 token 属性发送给客户端
    // 参数1:用户的信息对象
    // 参数2:加密的密钥
    // 参数3:配置对象,可以配置当前的 token 的有效期
    const tokenStr = jwt.sign({ username: userinfo.username }, secretKey, { expiresIn: '60s' })
    req.send({ status: 200, message: '登录成功', token: tokenStr })
})

// 3.6 使用全局错误处理中间件,捕获解析 JWT 失败后产生的错误
app.use((err, req, res) => {
    // 判断错误是否为 token 解析失败导致
    if (err.name === 'UnauthorizedError') return res.send({ status: 401, message: '无效的token' })
    res.send({ status: 500, message: '未知的错误' })
})

// 2. 启动服务器
app.listen(80, (req, res) => {
    console.log('Server is running at http://127.0.0.1/')
})

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1430949.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

react 使用react-seamless-scroll实现无缝滚动

文章目录 1. 实现无缝滚动效果2. react-seamless-scroll 无缝滚动案例介绍3. react 项目集成3.1 项目引入 cssSeamlessScroll 滚动组件3.2 完整代码3.2.1 newBet.tsx 代码3.2.2 index.module.scss 1. 实现无缝滚动效果 实现单步向下滚动点击更多展开&#xff0c;收起&#xff0…

[Angular 基础] - Angular 渲染过程 组件的创建

[Angular 基础] - Angular 渲染过程 & 组件的创建 之前的笔记为了推进度写的太笼统了&#xff08;只有功能没有其他&#xff09;&#xff0c;当时学的时候知道是什么东西&#xff0c;但是学完后重新复习发现有些内容就记不清了&#xff0c;所以重新用自己的语言总结一下 …

[晓理紫]每日论文分享(有中文摘要,源码或项目地址)--强化学习、模仿学习、机器人

专属领域论文订阅 关注{晓理紫}&#xff0c;每日更新论文&#xff0c;如感兴趣&#xff0c;请转发给有需要的同学&#xff0c;谢谢支持 如果你感觉对你有所帮助&#xff0c;请关注我&#xff0c;每日准时为你推送最新论文。 为了答谢各位网友的支持&#xff0c;从今日起免费为3…

基于YOLOv8算法的照片角度分类项目实践

目录 一、任务概述二、YOLOv8算法简介2.1 算法改进2.2 算法特点2.3 网络结构2.4 性能比较 三、工程实践3.1 安装算法框架库ultralytics3.2 库存照片预处理3.2.1 提取所有图片3.2.2 去除冗余的相同照片3.2.3 去除无车辆照片3.2.4 随机提取指定数量的图片 3.3 照片朝向分类3.3.1 …

项目02《游戏-06-开发》Unity3D

基于 项目02《游戏-05-开发》Unity3D &#xff0c; 接下来做 背包系统的 存储框架 &#xff0c; 首先了解静态数据 与 动态数据&#xff0c;静态代表不变的数据&#xff0c;比如下图武器Icon&#xff0c; 其中&#xff0c;武器的名称&#xff0c;描述&#xff…

宠物空气净化器哪个牌子好?除猫毛好的猫用空气净化器牌子推荐

大家都知道&#xff0c;宠物掉毛的情况有多么严重。特别是在换毛的季节&#xff0c;简直就是毛发遍地飞。这给家里有小孩和老人的人带来了很多困扰&#xff0c;他们可能会流鼻涕、过敏等等。而且&#xff0c;宠物有时候也会随地大小便&#xff0c;那个味道真的很难闻。家里的人…

【揭秘】JMeter JDBC脚本实战,让你的性能测试更高效!

Jmeter使用jdbc的场景&#xff1a; 1、接口功能测试时&#xff0c;需要查询验证码 2、通过数据库查询已经注册的手机号码 3、性能测试时&#xff0c;直接对某个SQL做性能测试&#xff0c;快速的发现性能问题 添加一个jdbc的配置元件 配置jdbc连接信息 配置说明&#xff1a; 1…

如何看待敏捷

局部清晰&#xff0c;循序渐进&#xff0c;整体清晰增量型 考试要么预测&#xff08;传统&#xff0c;瀑布&#xff09;&#xff0c;要么敏捷&#xff0c;要么就用混合方法 项目生命周期两种&#xff1a;预测型、敏捷型 开发生命周期四种&#xff1a;预测型、迭代型、增量型、…

JVM工作原理与实战(三十四):解决GC问题的方法

专栏导航 JVM工作原理与实战 RabbitMQ入门指南 从零开始了解大数据 目录 专栏导航 前言 一、常见的垃圾回收&#xff08;GC&#xff09;模式 二、解决GC问题的方法 1.优化基础JVM参数 2.更换垃圾回收器 3.优化垃圾回收器的参数 总结 前言 JVM作为Java程序的运行环境&a…

龙龙送外卖pta[代码+讲解]

题目 题解 代码 题目 龙龙是“饱了呀”外卖软件的注册骑手&#xff0c;负责送帕特小区的外卖。帕特小区的构造非常特别&#xff0c;都是双向道路且没有构成环 —— 你可以简单地认为小区的路构成了一棵树&#xff0c;根结点是外卖站&#xff0c;树上的结点就是要送餐的地址…

钓鱼攻击:深度解析与防范策略

一、引言 在当今的网络世界中&#xff0c;钓鱼攻击已经成为一种日益猖獗的威胁。这种攻击方式利用电子邮件、社交媒体或其他在线平台&#xff0c;伪装成可信赖的来源&#xff0c;诱导受害者点击恶意链接或下载恶意附件&#xff0c;进而窃取个人信息或实施其他恶意行为。本文将…

关于Clone

关于Clone 一般情况下&#xff0c;如果使用clone()方法&#xff0c;则需满足以下条件。 1、对任何对象o&#xff0c;都有o.clone() ! o。换言之&#xff0c;克隆对象与原型对象不是同一个对象。 2、对任何对象o&#xff0c;都有o.clone().getClass() o.getClass()。换言之&a…

VC++中使用OpenCV绘制直线、矩形、圆和文字

VC中使用OpenCV绘制直线、矩形、圆和文字 在VC中使用OpenCV绘制直线、矩形、圆和文字非常简单&#xff0c;分别使用OpenCV中的line、rectangle、circle、putText这四个函数即可。具体可以参考OpenCV官方文档&#xff1a;https://docs.opencv.org/4.x/index.html 下面的代码展…

9、C语言复习

目录 1、位操作 2、define宏定义关键词 3、ifdef条件编译 4、extern变量申明 5、typedef类别别名 6、结构体 7、static关键字 1、位操作 &&#xff1a;按位与 |&#xff1a;按位或 ^&#xff1a;按位异或 ~&#xff1a;取反 <<&#xff1a;左移 >>…

JAVA Web 学习(四)RabbitMQ、Zookeeper

十、消息队列服务器——RabbitMQ RabbitMQ是使用Erlang语言开发的开源消息队列系统&#xff0c;基于AMQP协议来实现。AMQP的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、 安全。AMQP协议更多用在企业系统内&#xff0c;对数据一致性、稳定性和可靠性要求…

会声会影2024破解版如何下载?

要下载会声会影2024&#xff0c;您可以按照以下步骤进行操作&#xff1a; 会声会影2024安装包下载如下: https://wm.makeding.com/iclk/?zoneid55677 1. 访问会声会影官方网站&#xff1a;在您的网络浏览器中&#xff0c;输入"会声会影2024官方网站"进行搜索&…

前后端数据校验

前端校验内容 前端开发中的必要校验&#xff0c;可以保证用户输入的数据的准确性、合法性和安全性。同时&#xff0c;这些校验也有助于提供良好的用户体验和防止不必要的错误提交到后端。 1、必填字段校验&#xff1a; 对于必填的字段&#xff0c;需确保用户输入了有效的数据…

图论练习1

内容&#xff1a;&#xff0c;拆点&#xff0c;分层&#xff0c;传递&#xff0c;带限制的最小生成树 [HNOI2015]菜肴制作 题目链接 题目大意 有个限制&#xff0c;号菜肴在号前完成在满足限制的条件下&#xff0c;按照出菜( 是为了满足的限制 ) 解题思路 由限制&#xf…

Github 2024-02-04 开源项目日报 Top9

根据Github Trendings的统计&#xff0c;今日(2024-02-04统计)共有9个项目上榜。根据开发语言中项目的数量&#xff0c;汇总情况如下&#xff1a; 开发语言项目数量Python项目6Ruby项目1HTML项目1C项目1Go项目1TypeScript项目1 Windows 终端、控制台和命令行存储库 创建周期…

一个 WPF + MudBlazor 的项目模板(附:多项目模板制作方法)

最近做了几个 WPF MudBlazor 的小东西&#xff0c;每次从头搭建环境比较繁琐&#xff0c;然鹅搭建过程还没啥技术含量&#xff0c;索性就直接做了个模板&#xff0c;方便以后使用。 1. 介绍 一个用来创建 .NET 8 WPF MudBlazor 的项目模板 适用于 VS2022 用法&#xff1a;…