Express进阶升级🆙
本篇文章,学习记录于:尚硅谷🎢 文章简单学习总结:如有错误 大佬 👉点.
前置知识:需要掌握了解: JavaScript基础语法 、Node.JS环境API 、前端工程\模块化、Express、MongoDB
好久没更新了,焦虑、迷茫、一瞬间又到了四月,重温了《四月是你的谎言》
😭 又一次被二刺螈感动,有点想换个方向了 …
Express 模板引擎:EJS
EJS – 嵌入式 JavaScript 模板引擎 | EJS 中文文档 (bootcss.com)
模板引擎: 是一种分离用户界面和业务数据的技术,在许多语言中都有应用
随着,前后端分离导致该技术使用减少,但它仍然存在于许多网站中:Java的JSP\Thymeleaf
…
<%= EJS %>模板引擎: Embedded JavaScript Templates
是一种简单而灵活的模板引擎,用于将数据动态渲染到网页上
EJS的核心特性: 嵌入JavaScript代码、支持变量、自定义过滤器和函数、条件判断和循环、模板的复用和组合,本章简单了解即可
EJS 初体验:
初始化项目结构:
npm i -y #npm构建项目
npm i ejs #npm安装EJS库
01EJS初体验.JS: EJS本质是对模板字符串的拼接,提供比原始操作具有更高效的方式👇
使用 ejs.render(str, data, options)
直接渲染模板字符串、<%= %>
:输出数据到模板响应;
/**原始字符拼接: */
let str = "wsm";
let str2 = `我叫${str}`;
console.log("原始模板字符拼接: "+str2); //我叫wsm
/**EJS render拼接数据|模板: */
//1.安装EJS包
//2.导入EJS模块
const ejs = require('ejs');
//3.使用EJS render进行渲染
let result = ejs.render("我叫<%= str %>", {str});
console.log("使用EJS render函数拼接渲染结果: "+str2); //我叫wsm
EJS文件模板:
EJSEmbedded JavaScript
: 是一款高效的嵌入式 JavaScript 模板引擎,用于生成 HTML 页面:
- 使用
<% %>
标签来包裹 JavaScript 代码,输出结果或执行逻辑:if-else
、for
等; <%= 变量 %>
:输出指定变量数据到模板;
02EJS文件模板.js:
//EJS文件模板
//1.安装EJS包
//2.导入EJS模块
const ejs = require('ejs'); //ejs模板模块;
const fs = require('fs'); //fs文件模块;
//声明变量
let isl = true;
let sejs = "Hello EJS";
let title = "ejsDemo";
const xiyou = ['唐僧','孙悟空','猪八戒','沙僧'];
let templateFile = fs.readFileSync('./views/index.ejs').toString();
//3.使用EJS render进行渲染
let result = ejs.render(templateFile, {sejs, isl, title, xiyou});
console.log(result);
/views/index.ejs: 文件后缀 .ejs
用于标识这是一个 EJS模板文件,开发者在项目中可以轻松识别和区分模板文件
<!DOCTYPE html>
<html lang="en">
<head>
<title><%= title %></title>
</head>
<body>
<h1><%= sejs %></h1>
<% if(isl){ %>
<span>登录成功: 输出遍历西游数组:</span>
<ul>
<% xiyou.forEach(item => { %>
<li><%= item %></li>
<% }) %>
</ul>
<% }else{ %>
<span>登录失败</span>
<% } %>
</body>
</html>
Express结合EJS:
🆗,经过上述代码,我们可以看到通过 ejs
可以完美的生成一个前端页面数据;
那么使用,Express+EJS
就像早期Java+JSP
快速创建单体项目结构; ⚙️项目构建:
npm i -y #npm构建项目
npm i ejs #npm安装EJS库
npm i express #npm安装Express库
/** Express结合EJS */
const express = require('express'); //导入 express
const path = require('path'); //导入 path
//创建应用对象
const app = express();
//1.设置 EJS 作为视图引擎
app.set('view engine', 'ejs');
//2.设置模板文件存放位置,模板文件: 具有模板语法内容的文件
app.set('views', path.resolve(__dirname, './views'));
//创建文件路由
app.get('/index', (req, res) => {
//声明变量
let isl = true;
let sejs = "Hello EJS";
let title = "ejsDemo";
const xiyou = ['唐僧','孙悟空','猪八戒','沙僧'];
//3.render渲染.ejs视图并响应结果:
res.render('index', {sejs, isl, title, xiyou});
});
//监听端口, 启动服务
app.listen(5400, () => {console.log('服务已经启动, 端口 5400 正在监听中....')});
Express—generator构造器:
Express 应用程序生成器
Express Generator 是一个用于快速创建 Express 应用程序骨架的工具:
帮助开发者快速创建Express应用程序的基本结构,包括目录结构、基本配置等,使开发者能够更专注于程序的业务逻辑
Express—Generator安装:
#方式一: npx命令来运行 Express 应用程序生成器,包含在 Node.js 8.2.0 及更高版本中)
npx express-generator
#方式二: 对于较老的 Node 版本,请通过 npm 将 Express 应用程序生成器安装到全局环境中并使用
npm install -g express-generator
安装成功:常用的命令配置、使用Express—Generator构建Express项目:
express -h
参数可以列出所有可用的命令行参数express -e 目录名
在指定目录下快速构建express项目结构
,目录不存在则创建;
#Express Generator 创建的应用程序通常具有以下目录结构:
├── app.js #app.js 是 Express 应用的主要文件,支持设置中间件、路由等配置;
├── bin #bin/www 文件是用于启动应用的脚本
│ └── www #它会创建一个 HTTP 服务器并监听指定的端口
├── package.json #package.json 文件包含应用的依赖和其他配置信息
├── public #public 目录用于存放静态资源
│ ├── images #如图像、JavaScript文件和样式表: 这些资源可以直接通过 URL 访问;
│ ├── javascripts
│ └── stylesheets
│ └── style.css
├── routes #routes 目录包含路由文件
│ ├── index.js #这里,你可以定义应用的不同路由和对应的处理函数
│ └── users.js
└── views #views 目录用于存放视图模板文件
├── error.pug #视图模板可以使用模板引擎(如 Pug、EJS 等)渲染动态内容
├── index.pug
└── layout.pug
小技巧tisp🔖: 学习一个陌生项目,无从下手情况可以查看它的:package.JSON
=》scripts
查看它的启动配置;
从而定位到主配置文件: bin/www
进而分析内部的功能配置、端口、文件作用;
Generator项目构建:
Generator_路由配置:
🆗,到此已经完美的构建了一个Express项目,那么接下来如何使用呢?还是有点无从下手📻
经过上述文件分析,我们大致了解如何定义自己的路由规则了:
- /routes 中定义路由文件——>并配置在
app.JS
中进行引用、暴漏 - /views 中定义ejs等模板资源——>
app.JS
中已经配置完毕
Generator_静态资源:
Express Generator 创建应用程序骨架时,Public目录
负责托管静态资源(例如图像、样式表、脚本等)
├── public #public 目录用于存放静态资源
│ ├── images #如图像、JavaScript文件和样式表: 框架启动后可以直接通过URL访问:
│ ├── ├──01.png #即可通过: http://127.0.0.1:3000/images/01.png 进行直接访问;
│ ├── javascripts
│ └── stylesheets
│ └── style.css #即可通过: http://127.0.0.1:3000/stylesheets/style.css 进行直接访问;
涉及到页面资源引用404问题:可以参考:邂逅Node.JS的那一夜
如果是在:HTML、EJS文件中引用则直接: /images/01.png
、/stylesheets/style.css
即可;
因为: 路径在浏览器中会自动拼接全局路径:/xxx/xx;
=自动拼接IP+端口=> http://xxx:xxx/xxx/xx;
lowdb JSON本地库:
lowdb - npm (npmjs.com) NPM官网:不过多介绍了解即可;
Lowdb 是一个轻量级、简单易用的本地 JSON 数据库,适用于 Node.js、Electron 和浏览器环境
它的设计理念是使用一个 JSON 文件作为数据库,实现基本的增删改查操作,以下是关于 Lowdb 的一些重要信息:
├── lowdbTest #lowdbTest 临时学习lowbd测试文件夹
│ ├── db.json #db.json 临时存储数据目录
│ ├── lowdbDemo.js #lowdb学习DemoJS
#安装lowdb包依赖
npm i lowdb@1.0.0 #因为不同版本有改变,所以选择了一个最常见的版本;
//导入lowdb、FileSync模块
const low = require('lowdb')
const FileSync = require('lowdb/adapters/FileSync')
//获取db对象
const adapter = new FileSync('db.json'); //FileSync模块获取|创建文件对象,不存在则创建;
const db = low(adapter); //low模块根据文件对象,创建文件的操作对象;
//初始化|定义JSON数据结构:
db.defaults({ key1: [], key2: {} }).write();
//写入数据: 给指定的的key属性中写入数据
//因为key1是一个数组,所以以 push添加元素、unshift追加元素
db.get('key1').push({id: 1, title: '今天天气还不错~~'}).write();
db.get('key1').unshift({id: 2, title: '今天天气还不错~~'}).write();
//获取数据:
console.log(db.get('key1').value());
//获取单条数据:
console.log(db.get('key1').find({id: 1}).value());
//更新数据: 仅更新匹配的第一条数据;
db.get('key1').find({id: 1}).assign({title: '今天下雨啦!!!'}).write();
//删除数据: 并返回删除的数据;
// console.log(db.get('key1').remove({id: 2}).write(););
描述 | 示例代码 |
---|---|
初始化数据 | db.defaults( {posts:[]} ).write() |
插入一条或多条数据 | db.get('posts').push({ id: 1, title: 'lowdb is awesome' }).write() |
更新数据,仅匹配的第一条 | db.get('posts').find({ id: 1 }).assign({ title: 'lowdb is great' }).write() |
删除一条或多条数据,同上 | db.get('posts').remove({ id: 1 }).write() |
获取数据,可以链式调用查询 | db.get('posts').value() |
查找满足条件的多条数据 | db.get('posts').filter({ published: true }).value() |
查找特定条件的单条数据 | db.get('posts').find({ id: 1 }).value() |
结束链式调用并返回结果 | db.get('posts').value() |
检查是否存在某条数据 | db.has('posts').value() |
获取数据的条数 | db.get('posts').size().value() |
设置属性的值 | db.set('user.name', 'typicode').write() |
对数据进行升序排序 | db.get('posts').sortBy('title').value() |
对数据进行降序或自定义排序 | db.get('posts').orderBy('views', 'desc').value() |
ExpressGenerator➕lowdb
🙂🆗,上述已经了解到了,ExpressGenerator+lowdb
那么就可以简单的开发一套API了:
ExpressGenerator
支持快速构建一个,Node的Express环境便于快速开发lowdb
可以用于简单的数据存储,以JSON形式进行保存|读取记录数据
不同是人对框架有不同的使用方式,此处是本人记录的一个使用Demo:
- 首先:定义一个data 用户存放管理自己的数据文件
- 其次:在routes中 定义配置自己的路由规则,并定义自己的代码、操作lowdb存取数据
- 最后:通过app.JS 文件管理配置路由封装暴漏路由请求API,如下是核心的routes中的文件📃:
//01_lowdbAPI:
//导入Express配置
var express = require('express');
var router = express.Router();
//导入lowdb操作数据配置
const low = require('lowdb')
const FileSync = require('lowdb/adapters/FileSync') //指定定义的db.JSON数据文件路径;
const adapter = new FileSync(__dirname+'/../data/db.json'); //FileSync模块获取|创建文件对象,不存在则创建;
//获取db文件的操作对象;
const db = low(adapter);
//初始化|定义db.JSON数据结构:
db.defaults({shopGoods: []}).write(); //这个是一个记录超市商品的数据.JSON
/* Post 新增商品记录 */
//此处ExpressGenerator构造的框架是不是很熟悉;
router.post('/addGoods', function(req, res, next) {
//req: 请求对象信息
//res: 响应对象信息
//next: 指向下一个中间件、路由函数;
db.get('shopGoods').unshift(req.body).write();
res.send('添加记录成功!!!');
});
/* GET 获取所有的商品 */
router.get('/goods', function(req, res, next) {
const goods = db.get('shopGoods').value();
res.send(goods);
});
module.exports = router;
🥳Perfect🎉很完美,如此我们就可以快速的开始我们的CRUD
编码✍
shortid 优化ID
优化: 上述,新增商品,还需要手动添加ID:我们都知道世纪情况下ID一般都是自动生成的;
而: lowdb 本身其实是一个快速操作JSON的包,并没有默认ID的功能;
so: 万能的NPM仓库,就有一款ID生成包:shiortid 🆔
🆗 welcome let’s go 让我们开始吧!🪶 npm i shortid
安装包依赖!!!
//优化: /addGoods shortid设置默认ID
const shortid = require("shortid");
router.post('/addGoods', function(req, res) {
//生成ID
let id = shortid.generate();
//ES6语法: 解构赋值、...扩展运算符 真好用<^^>
db.get('shopGoods').unshift({ id,...req.body }).write();
res.send('添加记录成功!!!');
});
ExpressGenerator➕MongoDB
关于MongoDB的整合,前置知识: 前端工程\模块化、Node携手MongoDB探险旅行⛏️当然如大佬直接跳过
介绍一下 Node携手MongoDB探险旅行
: 这篇文章中:mongoose模块化
的目录结构;
-
config.JS 数据库连接配置文件
-
/db/dbutil.JS Mongodb的配置文件:
暴漏函数
function(成功,失败)
,函数内进行mongodb
数据库连接,连接成功调用success,失败调用error -
index.JS 主文件: Node项目启动的主文件,内部导入
dbutil.JS
、userModel.JS 声明MB的数据结构模型
调用
dbutil
模块函数数据库连接成功调用,success(){ 中使用userModel操作对应MB数据 }
🆗,了解了核心代码就开始构建整合自己的项目结构了: 别忘了启动Mongodb服务...
-
导入:db、models、config.JS 文件模块:
-
bin/www 是该框架的启动类型: 为了保证
mongodb连接成功
使用,将整个
www
启动类代码包装在dbutil
模块函数的success中进行启动配置, -
接下来就是正常的路由代码编写,app.JS路由配置 好像也不是很复杂,这里就简单介绍一下拉~
软件开发中的部分理论概念:
程序开发过程出现了很多理论、概念、名词有时候不懂是什么意思,而且随着时代发展,通常出现一词多意
情况:
如下简单介绍一下,因为已经工作一段时间所以部分基础概念可能介绍不详细:
什么是接口API
此处的接口是APIApplication Program Interface
接口: 前后端通信的桥梁
,某些编程语言也有接口概念是一种编码语法…
简单的理解: 一个接口就是 服务中的一个路由规则 ,根据请求响应结果;
接口的作用:
- 实现不同软件之间的连接和通信:通过API,软件可以在业务上实现数据共享和交换
- 提供软件开发人员的工具:API使开发人员可以快速设计和编写代码,简化软件的开发过程
- 提高软件应用程序的性能:API通过有效地传递和处理数据,缩短数据处理时间,提高软件的响应速度
接口的组成:
-
接口访问地址:由协议、IP地址或域名、端口号、应用名和功能名组成。例如,
http://127.0.0.1:8080/api/function
-
请求方法:
GET获取数据)
、POST提交数据)
、DELETE删除数据)
、PUT修改数据
等,定义了客户端与服务器交互的方式 -
请求参数:用户使用接口时,需要向接口提供的数据,参数可以通过URL传递,也可以在请求体中传递
-
返回值响应:接口处理请求后返回给用户的数据,通常包括状态码、数据内容和错误信息
RESTful 风格API:
RESTful API是一种基于REST(Representational State Transfer,表述性状态转移)架构风格的网络应用程序接口:
它利用HTTP协议的标准方法来组织和处理数据,使得Web服务更加清晰、简洁、有层次,且易于维护和扩展:
资源: RESTful API的核心概念是资源,它可以是服务器上的任何东西,如文档、图片或服务,进行标识;
HTTP: 使用标准的HTTP方法来执行对资源的操作:GET读、POST取、DELETE删、PUT改、PATCH...
无状态: 每个请求从客户端到服务器必须包含理解和处理请求所需的所有信息,与之前的请求无关
代码按需: 服务器可以提供可执行代码或脚本,客户端可以选择下载并执行,以扩展客户端功能
统一接口: 具有统一的接口,这意味着无论何种类型的资源,都使用相同的接口方法进行操作
层次化系统: 客户端通常不能直接与存储资源的服务器通信,而是通过中间层来实现交互
可缓存特性: 为了提高网络效率,响应应该定义为可缓存的或不可缓存的
以开发:图书API接口📖📖举例:
操作 | 请求类型 | 请求URL | 结果 |
---|---|---|---|
获取所有图书 | GET | /book | 返回图书列表数组 |
获取单个图书 | GET | /book/:id | 返回单个图书信息 |
新增图书 | POST | /book | 返回新生成的图书信息 |
修改图书 | PUT | /book/:id | 返回更新后的图书信息 |
修改图书 | PATCH | /book/:id | 返回更新后的图书信息 |
删除图书 | DELETE | /book/:id | 返回一个空文档 |
参数:支持多种查询参数,例如排序_sort
、分页_page
和_per_page
、 过滤views_gt
、范围_start
和_end
等
静态文件:如果你创建了一个./public
目录,json-server
将会除了REST API之外,还会提供该目录下的内容
快速搭建简单:RESTful
Node真的太神奇了,NPM包太强大了: 这个是我在学习Java从没有的体验,Java框架越来越多而变的复杂起来
而,Node中一个NPM包几个命令就可以搭建一个简易的:RESTful
API接口请求,实在是太方便了!🙌
Json-Server 本身是一个 JS 编写的工具包,可以快速搭建 RESTful API 服务:
//安装json-server
npm install -g json-server
//设置对应的数据文件:
//图书馆数据库: books 图书信息、users 用户信息
{ "books":[], "users":[] }
//启动json-server
json-server --watch xxx文件.json
😜如此简单:😽😽 就完成了一个简单RESTful 风格的接口开发,当然仅适合临时Demo,真正的项目还是建议MongoDB完善;
- GET
127.0.0.1:3000/books
查询,返回图书列表数组 - GET
127.0.0.1:3000/books/id
查询,返回单个图书信息 - POST
127.0.0.1:3000/books
➕{ "name":"www", "context":"今天天气真不错" }
新增图书信息,支持默认ID
会话控制💬:
关于Cookie、Session、Token以及后面的JWT 之间的关系对于初学者来说,肯定一下子理解不了,完全没关系;
因为: 随着技术迭代,实际开发中不同项目也有不同的处理方式,导致学起来本就很混乱:慢慢理解即可
什么是会话:
概述:会话是浏览器和服务器之间的多次请求↔响应: 很多情况通过HTTP进行 请求↔响应
HTTP 是一种无状态的协议,它没有记忆、没有办法区分多次的请求是否来自于同一个客户端, 无法区分用户...
- A、B 同时登录
BiliBili📺
A喜欢看鬼畜👻、B喜欢看番剧🍅【都收藏了很多视频…】 - 但是,HTTP是无状态的每次请求都是一个新的开始,从而无法记录收藏记录🪶…
我们迫切的需要一种东西,可以用来判断用户状态,记录用户信息;
常见的会话控制技术有三种: Cookie🍪
、Session
、Token
Cookie🍪
Cookie 是什么: Cookie
是 HTTP服务器发送到用户浏览器,并保存在本地的一小块数据、按照域名划分保存;
是一种在客户端和服务器之间传递数据的机制,它最早出现于1994年
由 Netscape 公司的工程师 Lou Montulli 提出并实现
Cookie 运行流程: 浏览器向服务器发送请求时:需要进行记录,服务器通过response
向浏览器发送一个Cookie请求头
浏览器会把Cookie保存起来
,当浏览器再次访问服务器的时候,浏览器会把请求的网址及Cookie 一同提交给服务器
Cookie大小上限为4KB、一个服务器最多在客户端浏览器上保存20个Cookie、浏览器最多保存300个Cookie
面的数据是HTTP对Cookie的规范,但是现在一些浏览器可能会对Cookie规范 做了一些扩展
浏览器中的Cookie设置: 生活中经常使用浏览器,应该总会看到Cookie
的标识,此处应该有所了解了吧!!
Cookie 本质是存储在浏览器本地的,所以要注意管理,经常使用的记住密码就是Cookie的功能!!
浏览器也可以通过设置进行:禁用🚫、删除🗑️、查看🔎 可能会影响部分网站的使用
对于早期Cookie会存在安全隐患,现在大型网站都会进行加密㊙️,不用太担心💔
同一设备不同浏览器的Cookie是不会共享的
Express设置Cookie
此处通过,Node+Express进行测试模拟:会话控制,但不要固定思维🧠,会话属于Web领域的技术概念;
任何的编程语言都可以对其进行实现,可能方式不同罢了,代码也不需要强制记忆,了解规律即可~
🥷初学者,此时一定很吃力也不必强迫症搞明白,很多框架都二次封装多很多概念🗐
//为了方便操作此处使用Npm中一个Cookie工具包: npm i cookie-parser
const cookieParser = require('cookie-parser')
const express = require('express');
//定义应用对象,并设置中间件;
const app = express();
app.use(cookieParser());
/**为了方便演示此处并没有加密: */
//服务器C端设置Cookie
//方式一: res.cookie(key,value); 会在浏览器关闭的时候, 销毁
//方式二: res.cookie(key,value,毫秒); 定义k,v 同时{maxAge: xxx} 设置Cookie最大过期时间;
//...实际开发中还有更多设置: Cookie不可读、不可写之类的控制,都是为了保证安全;
app.get('/setCookie', (req, res) => {
res.cookie('name1', 'zhangsan'); // 会在浏览器关闭的时候销毁
res.cookie('name2','lisi', {maxAge: 60 * 1000}) // 设置Cookie最大过期时间,同名Cookie Key后来者居上会进行覆盖;
res.send('成功!! 设置cookie');
});
//服务器C端查询Cookie
//🆗 成功设置了Cookie之后的任何一个请求都会携带这个Cookie进行请求了
app.get('/getCookie', (req, res) => {
console.log(req.cookies); //通过中间件进行过滤cookie则可以直接使用了;
res.send(`欢迎您 ${req.cookies}`);
})
//服务器C端删除Cookie
app.get('/removeCookie', (req, res) => {
res.clearCookie('name'); //删除指定的Key
res.send('删除成功~~');
});
//启动服务
app.listen(5400);
上述我们通过Node+Express完成了,简单的Cookie设置: 实际工作中常用场景 登录
😊用户A
在自己的电脑通过浏览器🪟
,注册登录网站🗔
网站🗔
—HTTP请求—服务器🗄️
,服务器内经过处理验证… 登录|注册成功!- 返回响应:
set-cookie:用户=信息
给浏览器🪟 Cookie🍪
,浏览器🪟
保存记录Cookie🍪
- 并在之后的每一次请求都会携带这个
Cookie🍪
,服务器可以获取Cookie,由此区分用户,实现持久会话机制;
😐用户B、C...
也有自己的电脑浏览器,每个人都有自己的Cookie,使服务器可以区分不同的用户,进行不同的内容展示;
Session
随着时间的推移,Cookie的很多缺点也出现了: 安全型、存储容量、生命周期
Session 由此诞生: 和Cookie一样也是为了解决优化:HTTP的无状态协议特性,实现持久会话;
和Cookie 不同: Session 是一种在服务端存储数据的技术,由服务端生成控制更安全、生命周期可控;
安全性:
- Cookie 存储在客户端,容易被篡改,且信息存储在文本文件中,任何人都可以查看
- Session 存储在服务端,相对更安全,通常数据以二进制或加密形式保存,只能在服务器上解码
存储容量:
- Cookie 的最大容量为 4KB,无法存储大量用户信息
- Session 可以存储更多数据,没有容量限制,但有一定的内存限制:
也有部分的解决措施
生命周期:
- Cookie 的生命周期由用户设置,当用户退出浏览器或注销登录时,Cookie 结束
- Session 的生命周期由服务器控制,可以持续一段时间,直到用户主动注销或关闭浏览器
扩展Tips🔖: Session很多情况下,依赖于Cookie使用弥补Cookie的缺点,Cookie 不依赖于 Session,可以独立使用;
-
Cookie+Session 实现会话登录:
😊用户A
在自己的电脑通过浏览器🪟
,注册登录网站🗔
网站🗔
—HTTP请求—服务器🗄️
,服务器内经过处理验证… 登录|注册成功!服务器内将用户信息、某些数据通过,服务器内的加密算法进行加密,并生成一个
Session🆔
返回响应:
set-cookie:SessionId=???
给浏览器🪟 Cookie🍪
,浏览器🪟
保存记录Cookie🍪
并在之后的每一次请求都会携带这个
Cookie🍪
,服务器通过Cookie获取Session,由此区分用户,实现持久会话优点:客户端仅存储了加密🔒的Session🆔保证了数据安全,节省了Cookie的容量小问题,服务器端控制生命周期
-
Session+Redis扩容优化:
随着技术成熟:前后端分离、后端压力过大,很多大型项目 📺前端——>对应多个服务端🖧
Session 由某一服务进行生成,仅存放在生成服务器的内存中,那个如何在多个服务端之间共享呢?当然有多种解决方案:
-
一: 通过特定的规则算法,在请求过程中,发送到对应的服务模块,但显然比较麻烦,影响程序效率;
-
二: Redis 作为中间服务器,所有的Session都放在里面,大家一起去存|取,几乎完美解决了!
且解决了Session过多,内存不足的情况,Redis可以随时进行扩充;
-
-
Session除了Cookie其他实现方式:
URL 中传递 Session ID
、JavaScript 变量存储 Session 数据
、Session Storage
、Local Storage
总之,Session 解决了 Cookie 在安全性、存储容量和生命周期等方面的局限性,提供了更好的用户状态管理和数据存储方式
//导入 express
const express = require('express');
//引入 npm i express-session connect-mongo
const session = require("express-session"); //NPM包 封装了对Session的设置操作
// const MongoStore = require('connect-mongo'); //NPM包 将Sessio存储在MongoDB数据库中
//创建应用对象
const app = express();
//设置 Session 的中间件
app.use(session({
name: 'sid', //设置cookie的name 默认: sid
secret: 'atguigu', //参与加密的字符串(又称签名\加盐: 确保Session加密安全
saveUninitialized: false, //是否为每次请求都设置一个cookie用来存储session的id,一般仅第一个请求如此
resave: true, //是否在每次请求时重新保存session 只要还在使用Session就不会过期
// store: MongoStore.create({ //[可选] 数据库的连接配置,Session保存入Mongodb中,默认保存至内存
// mongoUrl: 'mongodb://127.0.0.1:27017/bilibili'
// }
cookie: {
httpOnly: true, //开启后前端无法通过 JS 操作此cookie: 确保安全性;
maxAge: 1000 * 60 * 5 //控制 sessionID 的过期时间的!!!
},
}))
//session 的设置: 中间件内已经完成封装直接使用即可;
//假设登录: 用户名|密码:admin
app.get('/login', (req, res) => {
if(req.query.username === 'admin' && req.query.password === 'admin'){
//登录成功设置 session 信息
req.session.username = 'admin';
req.session.uid = '258aefccc';
res.send('登录成功');
}else{
res.send('登录失败~~');
}
})
//session 的读取: 中间件内已经完成封装直接使用即可;
app.get('/cart', (req, res) => {
if(req.session.username){ res.send(`购物车页面, 欢迎您 ${req.session.username}`)
}else{ res.send('您还没有登录~~'); }
});
//session 的销毁
app.get('/logout', (req, res) => {
req.session.destroy(() => { res.send('退出成功~~'); })
})
//启动服务
app.listen(5400);
http://127.0.0.1:5400/login?username=admin&password=admin
首先登录,服务端生成Session 并响应Cookie中http://127.0.0.1:5400/cart
前端通过浏览器查看Cookie:sid:xxx
、服务端中间件之间获取Session中的数据;- 只要Cookie|Session不销毁,该浏览器对该网站的每一次请求都会携带Sid,服务端就可以获取对应用户消息📢
http://127.0.0.1:5400/logout
接口,服务端销毁Session 则服务端无法之间获取用户消息;
Token
Session 对于多服务端,前后端分离等情况并不适合
因为: SessionID 属于非长期、且高频数据,一般都是存储在内存中,大量Session存储服务器端压力过大😓
Token 和Session 有异曲同工之妙,都是由服务器生成: Token是:一串加密字符串🧬, Token 中保存着用户信息
-
加密字符串: 通过某种算法将
数据
加密成一段无规则字符串,并可以反编译回原始数据
-
优点: 安全性更高:Token 无法被篡改,适用于前后端分离的应用;
Session 存储在服务端,相对安全,但有一定的内存限制,获取SessionID 查询到对应用户信息;
Token 存储在客户端,更灵活,适用于前后端分离的应用,前端请求头携带Token 反编译出用户信息;
JWT
JWTJSON Web Token
可以看作是 Token 的一种具体技术实现:
Token 是一个广义的术语,用来表示任何一种用于身份验证和授权的令牌
它可以指代各种类型的令牌,包括 JWT、OAuth 令牌等,💁很久以前写的一篇关于JWT的详细介绍:
我们可以使用 jsonwebtoken
NPM包 来操作 Token: npm i jsonwebtoken
//导入JWT
const JWT = require('jsonwebtoken');
//创建(生成) token
// let TokenStr = JWT.sign(加密用户数据, 加密字符串, 配置对象);
let TokenStr = JWT.sign(
{
username: 'zhangsan',
password: '123456wsm',
},
'wsm', //加密字符串,确保数据安全;
{ expiresIn: 60, } //配置对象: 设置Token有效时间单位是秒
);
console.log(TokenStr); //生成的Token
//校验: //解析Token
JWT.verify(TokenStr, 'wsm', (err, data) => {
if(err){ return console.log('校验失败~~'); }
console.log(data); //返回解析后的数据信息;
})
🆗:See you Next Time 终于写完了!!
最近喜欢的VUP毕业了伤心~