Express进阶升级

Express进阶升级🆙

本篇文章,学习记录于:尚硅谷🎢 文章简单学习总结:如有错误 大佬 👉点.

前置知识:需要掌握了解: JavaScript基础语法Node.JS环境API前端工程\模块化ExpressMongoDB

好久没更新了,焦虑、迷茫、一瞬间又到了四月,重温了《四月是你的谎言》😭 又一次被二刺螈感动,有点想换个方向了 …

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-elsefor 等;
  • <%= 变量 %>:输出指定变量数据到模板;

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.JSuserModel.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🍪SessionToken

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 IDJavaScript 变量存储 Session 数据Session StorageLocal 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毕业了伤心~
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

nvm基本使用

nvm基本使用 文章目录 nvm基本使用1.基本介绍2.下载地址3.常用指令 1.基本介绍 NVM是一个用于管理 Node.js 版本的工具。它允许您在同一台计算机上同时安装和管理多个 Node.js 版本&#xff0c;针对于不同的项目可能需要不同版本的 Node.js 运行环境。 NVM 主要功能&#xff…

【Redis | 第十篇】Redis与MySQL保证数据一致性(两种解决思路)

文章目录 10.Redis和MySQL如何保证数据一致性10.1双写一致性问题10.2数据高度一致性10.3数据同步允许延时10.3.1中间件通知10.3.2延迟双删 10.Redis和MySQL如何保证数据一致性 10.1双写一致性问题 Redis作为缓存&#xff0c;它是如何与MySQL的数据保持同步的呢&#xff1f;特…

PHP 错误 Unparenthesized `a ? b : c ? d : e` is not supported

最近在一个新的服务器上测试一些老代码的时候得到了类似上面的错误&#xff1a; [Thu Apr 25 07:37:34.139768 2024] [php:error] [pid 691410] [client 192.168.1.229:57183] PHP Fatal error: Unparenthesized a ? b : c ? d : e is not supported. Use either (a ? b : …

『FPGA通信接口』串行通信接口-SPI

文章目录 1.SPI简介2.控制时序3.Dual、Qual模式4.例程设计与代码解读5.SPI接口实战应用5.1时序要求5.2仿真时序图5.3代码设计 6.传送门 1.SPI简介 SPI是串行外设接口&#xff08;Serial Peripheral Interface&#xff09;的缩写&#xff0c;通常说SPI接口或SPI协议都是指SPI这…

将文件导入数据库

#include <stdio.h> #include <sqlite3.h> #include <string.h> int main(int argc, const char *argv[]) { //打开数据库 sqlite3 *db NULL; if(sqlite3_open("./dict.db",&db) ! SQLITE_OK){ printf("sqlite…

5G随身WiFi推荐测评:品速5G VS 格行5G随身WiFi,随身wifi哪个品牌网速好?性价比更高?

玩游戏卡顿遭吐槽&#xff0c;直播掉线成笑柄&#xff0c;4G网络已难满足需求。5G随身wifi虽受追捧&#xff0c;但价格较高令人犹豫。面对众多品牌&#xff0c;随身WiFi哪个品牌靠谱呢&#xff1f;性价比高呢&#xff1f;今天就来测评一下口碑最好的无线随身WiFi格行5G随身wifi…

新能源车载芯片分析

新能源汽车市场正迸发出巨大的活力&#xff0c;传统主机厂和新势力都纷纷推出各种车型&#xff0c;打起了价格战&#xff0c;各种新技术让人眼花缭乱。当前&#xff0c;战场硝烟弥漫&#xff0c;新能源汽车公司犹如春秋时期的各诸侯国。车载芯片作为新能源汽车的关键组成部分&a…

NDK 基础(一)—— C 语言知识汇总

1、数据类型 在 C 语言中&#xff0c;数据类型可以分为如下几类&#xff1a; 基本数据类型&#xff1a; 整数类型&#xff08;Integer Types&#xff09;&#xff1a;是算数类型&#xff0c;包括如下几种&#xff1a; int&#xff1a;用于表示整数数据&#xff0c;通常占用四个…

nvm 切换 Node 版本失败

创建vue3项目时&#xff0c;需要切换到更高版本的 node&#xff0c;于是使用 nvm (node 包版本管理工具)进行版本切换 切换版本时&#xff0c;显示成功&#xff0c;但再次查看当前 node 版本时&#xff0c;发现没切换过来。 解决方法&#xff1a; where node 查看node的安装…

车道分割YOLOV8-SEG

车道分割YOLOV8-SEG&#xff0c;训练得到PT模型&#xff0c;然后转换成ONNX&#xff0c;OPENCV的DNN调用&#xff0c;支持C,PYTHON,ANDROID开发 车道分割YOLOV8-SEG

深圳工厂车间降温通风设备

深圳工厂降温方案多种多样&#xff0c;可以根据工厂的具体情况和需求来选择合适的方案。以下是一些常见的降温方案&#xff1a; 通风换气&#xff1a;通过安装负压风机或冷风机等设备&#xff0c;加强通风换气&#xff0c;将室内热空气排出&#xff0c;吸入室外相对凉爽的空气…

零基础俄语培训哪家好,柯桥俄语培训

1、Мощный дух спасает расслабленное тело. 强大的精神可以拯救孱弱的肉体。 2、Единственное правило в жизни, по которому нужно жить — оставаться человеком в лю…

WSL及UBUNTU及xfce4安装

如何拥有Linux服务器&#xff1f; wsl 是适用于 Linux 的 Windows 子系统&#xff08;Windows Subsystem for Linux&#xff09;。是一个为在Windows 10和Windows Server 2019上能够原生运行Linux二进制可执行文件&#xff08;ELF格式&#xff09;的兼容层&#xff0c;可让开发…

Docker 的数据管理 端口映射 容器互联 镜像创建

一 Docker 的数据管理 1 管理 Docker 容器中数据主要有两种方式&#xff1a; 数据卷&#xff08;Data Volumes&#xff09; 数据卷容器&#xff08;DataVolumes Containers&#xff09;。 1.1 数据卷 数据卷是一个供容器使用的特殊目录&#xff0c;位于容器中。可将宿主机…

数据污染对大型语言模型的潜在影响

大型语言模型&#xff08;LLMs&#xff09;中存在的数据污染是一个重要问题&#xff0c;可能会影响它们在各种任务中的表现。这指的是LLMs的训练数据中包含了来自下游任务的测试数据。解决数据污染问题至关重要&#xff0c;因为它可能导致结果偏倚&#xff0c;并影响LLMs在其他…

linux 中 make 和 gmake的关系

1. 关系 gmake特指GNU make。 make是指系统默认的make实现; 在大多数Linux发行版中&#xff0c;make就是GNU make&#xff0c;但是在其他unix中&#xff0c;gmake可以指代make的某些其他实现&#xff0c;例如BSD make或各种商业unix的make实现。 gmake是GNU Make的缩写。 Linux…

【基础C-递归的易错思路】

目录 1. 分析2. 代码3. 结果&#xff1a; 1. 分析 现在要写一个小程序&#xff0c;实现输入整型&#xff1a;4268&#xff0c;输出字符:‘4’,‘2’,‘6’,‘8’,思路很简单&#xff0c;就是进行整数的除10&#xff0c;结果对10求模就行&#xff0c;但是得到的值是逆序排列&…

Vue 组件分类、局部注册和全局注册

文章目录 背景知识组件分类安装 vue-cli示例设置组件局部注册设置组件全局注册 背景知识 开发 Vue 的两种方式&#xff1a; 核心包传统开发模式&#xff1a;基于 html / css / js 文件&#xff0c;直接引入核心包&#xff0c;开发 Vue。工程化开发模式&#xff1a;基于构建工…

[c++]菱形继承解析

菱形继承 大概示意图&#xff1a; 菱形继承不一定只是标准的菱形&#xff0c;只要形似菱形的都可以叫菱形继承。 (以下说明都是默认公有继承&#xff0c;public和protected成员情况下) 菱形继承会造成数据的冗余和二义性&#xff1a; 冗余&#xff1a;一个Assitant对象里面有…

[C++基础学习]----03-程序流程结构之循环结构详解

前言 在C程序中&#xff0c;循环结构在用于重复执行一段代码块&#xff0c;直到满足某个条件为止。循环结构有多种形式&#xff0c;包括while循环、do-while循环和for循环。 正文 01-循环结构简介 1、while循环语句&#xff1a; while循环在每次循环开始前检查条件是否为真&a…