Node.js
Node.js
课程链接:【黑马程序员Node.js全套入门教程,nodejs新教程含es6模块化+npm+express+webpack+promise等_Nodejs实战案例详解】
一.Node.js配置、基础模块
1.安装及检测
下载地址:https://nodejs.org/zh-cn/
查看版本号:
打开终端(Windows+R后输入cmd),在终端输入命令node -v后,按下回车。\
切换路径:cd 路径名
2.终端中的快捷键
1.PgUp(上箭头) 键,快速定位到上一次执行的命令
2.tab 键,快速补全路径
3.esc 键,快速清空当前已输入的命令
4.输入 cls 命令,可以清空终端
3.fs文件系统模块
要在JavaScript代码,使用fs模块来操作,则须:
const fs =require('fs');
语法 | 作用 | 参数 |
---|---|---|
fs.readFile(path[,options],callback) | 读取文件中的内容 | 参1:必选,文件路径 参2:可选,编码格式 参3:必选,读取后通过回调函数**拿结果(参1失败,参2成功) |
fs.writeFile(file,date[,options],callback) | 向指定文件写入内容 | 参1:必选,文件路径 参2:必选,须写入的内容 参3:可选,以何编码格式写入 参4:必选,写入后的回调函数**,写入成功则返回null,否为错误对象 |
__dirname | 表示当前文件所处目录 | 用于处理路径动态拼接 例:path.join(__dirname ,"相对路径") |
4.path路径模块
要在JavaScript代码,使用path模块来处理路径,则须:
const path =require('path');
语法 | 作用 | 参数 |
---|---|---|
path.join([...paths]) | 把多个路径片段拼接为完整的路径字符串(涉及路径均用该方法,不用+号) | 多个参数,均字符串 返回:拼接后的字符串 |
path.basename(path[,ext]) | 获取路径中的最后一部分,常用于获取路径中的中文名 | 参1:必选,路径字符串 参2:可选,文件扩展名(屏蔽作用)返回:文件名字符串 |
path.extname(path) | 获取路径中的文件扩展名 | 参1:必选,表示路径的字符串 返回:得到的扩展名字符串 |
5.http模块 (创建web服务器)
如果希望使用http模块来创建web服务器,则须:
const path =require('http');
一.服务器相关的概念
1.IP地址
格式:通常用”点分十进制“表示成(a.b.c.d)的形式,其中a、b、c、d都是用0~255之间的十进制整数表示。
注意:互联网中每台Web服务器,都有自己的IP地址。
在开发期间,自己的电脑既是一台服务器也是一个客户端,可以在自己 的浏览器中输入127.0.0.1这个IP地址,来把自己电脑当一台服务器访问。
2.域名和域名服务器
IP地址和域名是一一对应的关系,这份关系由存放在**DNS(域名服务器)**中,使用者记住所要访问的域名即可,对应的转换工作由DNS进行,所以域名服务器(DNS)就是提供IP地址和域名之间转换服务的服务器。
注意:单纯使用IP地址,互联网中的电脑也能正常工作,有了域名让互联网的 世界更加方便。
在开发测试期间,127.0.0.1对应的域名是localhost。
3.端口号
在一台电脑中可能运行着成百上千个web服务器,每个端口号对应着不同的web服务器,所以通过端口号可以准确的交给对应的web服务器进行处理。即IP地址”:“后的数字
注意:每个端口号不能同时被多个web服务器占用。
在实际应用中,URL中的80端口可以被省略。
二.创建最基本的服务器
语法 | 作用 | 参数 |
---|---|---|
http.createServer | 创建web服务器实例 | 无 |
xxx.on('request',function(req,res) {}) | 绑定事件 | req**.url**:客户端请求的URL地址 ; req**.method**:客户端请求类型; res.**end();*服务器响应内容 ; **res.setHeader('Content-Type','text/html; charset=utf-8');解决中文乱码 |
xxx.listen(参1,回调函数) | 启动服务器 | 参1:端口号 参2:启动后的回调函数 |
1.导入http模块
2.创建web服务器实例
3.为服务器绑定request事件,监听客户端的请求
4.启动服务器
二.CommonJS模块化
1.注意点:
内置模块、自定义模块(需指明路径,可省略js扩展名)、第三方模块
使用require()加载模块时会执行被加载模块
模块作用域,不污染
每个js自定义模块中都有一个module对象,里面存储和当前模块有关系的信息
module.exports对象:默认为空对象,可利用此将模块内成员共享出去,供外界使用,require()方法导入自定义模块,得到的成员就是这对象。
exports对象:为了写法更简单而出的,默认情况下等于module.exports对象,但最终共享的结果是以module.exports对象指向的为准。(注意使用误区)
加载模块优先从缓存中加载,故不会多次执行,其次内置模块优先级最高。
2.CommonJS模块化规范:
1.module变量代表当前模块。
2.module变量是一个对象,它的exports属性是对外的接口。
3.加载某个模块,其实就是加载该模块的modular.export属性。require()方法用于加载该模块。
3.包:
介绍:
Node.js中的第三方模块又称包。
由第三方开发,开源及免费。
http://ww.npmjs.com/ :搜索需要下载的包
http://registry.npmjs.org/ :从该服务器上下载需要用到的包
在终端中执行npm-v命令,来查看电脑上所安装的npm包管理工具。
安装格式化时间的moment包(@后接版本号 大版本.功能版本.Bug修复版本):
npm install 包的完整名称[@2.22.2](安装多个包用空格分开)
npm i 包的网站名称
moment:
搜就对了!
package.json配置文件:
由于包的体积过大,在多人合作开发中需要剔除掉,故因此需要在项目根目录中,创建一个package.json的配置文件,用于记录用到了哪些包,这样便可在剔除node_modules目录之后,在团队成员之间共享项目的源代码。
创建: npm init -y
注意:1.只能在英文的目录下成功运行。
2.运行npm install 命令安装包时,npm包管理工具会自动添加包的信息。
一次性安装所有的包(默认读取package.json里记录的包):
npm install
npm i
卸载包:
npm uninstall 包的完整名称
(也会修改package.json的dependdencies节点里的内容)
devDependencies节点:
如果某些包只在项目开发阶段会用到,在项目上线之后不会用到,建议把这些包记录到devDependencies节点中。 均会用到,则记录到dependdencies节点中。可以在npm网站里查询是否需要。
npm i 包的完整名称 -D
等价于 npm install 包的完整名称 --save-dev
解决下包速度慢:
淘宝NPM镜像服务器(镜像是一种文件储存形式,一个磁盘上的数据在另一个磁盘上存在完全相同的副本即为镜像)。
切换npm下包镜像源:
查看当前的下包镜像源: npm config get registry
将下包的镜像源切换为淘宝镜像源:npm config set registry=https://registry.npm.taobao.org/
安装nrm工具: npm i nrm -g
查看可用的镜像源:nrm ls
切换下包的镜像源:nrm use 镜像源名称
包的分类:
1.项目包 (安装到项目的node_modules目录中)
1.开发依赖包:(package.json的devDependencies节点)
2.核心依赖包:(package.json的dependdencies节点)
2.全局包(执行npm install -g时会把安装包安装到C:\Users\用户目录\APPDate\Roaming\npm\node_modules)
只有工具性质的才需要安装到全局,判断是否需要全局安装可以在npm网站里查看。
安装:npm i 包名 -g
卸载:npm uninstall 包名 -g
i5ting_toc:
一个可以包md文档转换为html页面的小工具
安装:npm install -g i5ting_toc
调用:i5ting_toc -f 要转换的md文件路径 -o
规范的包结构:
开发自己的包:
见第三天案例
注册npm账号,在终端里执行 npm login
命令,依次输入用户名、邮箱、密码。
将终端切换到包的根目录之后,运行 npm publish
命令后,即可发布到npm,但是需要先检测是否重名。
删除包: npm unpublish 包名 --force
1.只能删除72小时内的,72小时之后的不能删除。
2.删除后24小时内不能重复发布
3.尽量发布有意思的包
三.Express
本质:npm上的一个第三方包,提供了快速创建Web网站服务器和API接口服务器的方法。
http://www.expressjs.com.cn/
安装: npm i express [@4.17.1]
1.创建一个基本的Web服务器:
// 1.导入
const express =require('express');
// 2.创建
const app =express();
//3.调用app.listen(),启动服务器。
app.listen(80,()=>{
console.log('express server running at http://127.0.0.1');
})
监听GET请求:
app.get('请求URL',function(req,res){/*处理函数*/})
//参1:客户端请求的URL
//req:请求对象(包含了与请求相关的属性与方法)
//res:响应对象(包含了与响应相关的属性与方法)
监听POST请求:
app.post('请求URL',function(req,res){/*处理函数*/})
//参1:客户端请求的URL
//req:请求对象(包含了与请求相关的属性与方法)
//res:响应对象(包含了与响应相关的属性与方法)
响应请求:
//通过res.send响应
res.send(/*响应对象*/)
获取URL中的查询参数:
//通过req.query查询 返回一个对象,默认为空
req.query
获取URL中的动态参数:
//URL 地址中,可以通过 ':参数名' 的形式,匹配动态参数值
app.get('/user/:id/:name',(req,res){
//req.params默认为一个空对象
//里面存放着通过:动态匹配到的参数值
console.log(req.params);
})
托管静态资源:
1.express.static()
通过它,我们可以非常方便的创建一个静态资源服务器。
// 例如:将public目录下的图片、css文件、JavaScript文件对外开放
app.use(express.static('public'))
2.托管多个静态资源目录
3.挂载路径前缀
// 例如:将public目录下的图片、css文件、JavaScript文件对外开放
app.use('/public',express.static('public'))
nodemon:
1.为什么要使用nodemon:
![image-20220619220729925](C:\Users\Ming comity\AppData\Roaming\Typora\typora-user-images\image-20220619220729925.png)
2.安装nodemon:
npm i nodemon -g
3.使用nodemon
![image-20220619221147207](C:\Users\Ming comity\AppData\Roaming\Typora\typora-user-images\image-20220619221147207.png)
//使用nodemon之前的终端命令
node app.js
//使用nodemon之后的终端命令,可实现自动重启项目的效果
nodemon app.js
2.Express 路由:
1.概念:
在Express中,路由指的就是客户端的请求与服务器处理函数之间的映射关系。
2.组成:
由3部分组成:请求的类型、请求的URL地址、处理函数.
app.METHOD(PATH,HANDLER);
//请求的类型、请求的URL地址、处理函数
3.匹配过程:
4.创建路由模块:
为了方便对路由进行模块化管理,Express不建议将路由直接挂载到app上,而是建议将路由抽离为单独的模块。
//1.创建路由模块对应的.js文件
//2.调用express.Router()函数创建路由对象
var express=require('express');
var router=express.Router();
//3.向路由对象上挂载具体的路由
router.get('',()=>{});
.....
//4.使用module.express向外共享路由对象
module.exports=router;
//5.使用app.use()函数注册路由模块
5.注册路由模块/挂载访问前缀
//1.导入路由模块
const userRouter=require('路径');
//2.使用app.use()注册路由模块
app.use(userRouter)
//添加访问前缀
//app.use('/api',userRouter)
3.Express中间件
1.中间件概念:
next函数作用:
实现多个中间件连续调用的关键,它表示把流转关系转交给下一个中间件处理或路由。
2.定义一个中间件函数:
//常量mw所指向的,就是一个中间件函数
const mw =function(req,res,next) {
console.log("这是一个最简单的中间件函数");
//在当前中间件的业务处理完毕后,必须调用next()函数
//表示把流转关系转交给下一个中间件或路由
next();
}
3.全局生效的中间件
客户端发起的任何请求,到达服务器后,都会触发的中间件,叫做全局生效的中间件。
通过调用:
app.use(mw)
即可定义一个全局生效的中间件。
4.中间件的作用:
5.定义多个全局中间件:
使用app.use()连续定义多个全局中间件。客户端请求到达服务器后,会根据中间件定义的先后顺序依次进行调用。
app.use(function(req, res, next) {
console.log("调用了第一个中间件");
next();
})
app.use(function(req, res, next) {
console.log("调用了第二个中间件");
next();
})
app.get('/', (req, res) => {
res.send("Yes")
})
6.局部生效的中间件:
不使用app,use()定义的中间件,叫做局部生效的中间件。
const mw1 = function(req, res, next) {
console.log("调用了局部中间件mw1");
req.a = "局部中间件的a属性";
next();
}
app.get('/', mw1, (req, res) => {
res.send("调用了url为/的get请求" + " ---" + req.a);
})
7.定义多个局部中间件
app.get('/', mw1, mw2, (req, res) => {
res.send("调用了url为/的get请求" + " ---" + req.a);
})
app.get('/',[ mw1, mw2], (req, res) => {
res.send("调用了url为/的get请求" + " ---" + req.a);
})
8.中间件的注意事项:
9.中间件的分类:
1.应用级别:
2.路由级别:
3.错误级别:
注意:需要放到所有路由之后!!!!
4.Express内置:
//解析json格式数据
app.use(express.json())
app.post('/user', (req, res) => {
//使用req.body这个来接受客户端发来的请求体数据
//默认情况下,如果不配置解析表单数据的中间件,req.body则为undefined
console.log(req.body);
res.send("调用了url为/user的post请求");
});
//解析url-encoded格式数据
app.use(express.urlencoded({ extended: false }))
app.post('/book', (req, res) => {
//req.body可以获取json格式以及url-encoded格式数据
console.log(req.body);
res.send("调用了url为/book的post请求");
})
5.第三方:
![image-20220620113115607](C:\Users\Ming comity\AppData\Roaming\Typora\typora-user-images\image-20220620113115607.png)
10.自定义中间件:
1.实现步骤
![image-20220620151627372](C:\Users\Ming comity\AppData\Roaming\Typora\typora-user-images\image-20220620151627372.png)
2.定义中间件
app.use((req, res, next) => {
//具体逻辑:
//1.定义str储存数据
let str = '';
//2.监听req的data事件 (当数据量大时,会将数据分割后,分批发送至服务器,所以会多次触发data事件,则获取的数据也需要手动拼接)
req.on('data', (chunk) => {
str += chunk;
})
//3.监听req的end事件(请求数据体结束后触发)
req.on('end', () => {
//str是完整的请求体数据
//通过querystring处理查询字符串
const body = qs.parse(str);
//挂载为req.body的属性
req.body = body;
next();
})
})
3.封装为模块
const qs = require('querystring');
const bodyParser = ((req, res, next) => {
//具体逻辑:
//1.定义str储存数据
let str = '';
//2.监听req的data事件 (当数据量大时,会将数据分割后,分批发送至服务器,所以会多次触发data事件,则获取的数据也需要手动拼接)
req.on('data', (chunk) => {
str += chunk;
})
//3.监听req的end事件(请求数据体结束后触发)
req.on('end', () => {
//str是完整的请求体数据
//通过querystring处理查询字符串
const body = qs.parse(str);
//挂载为req.body的属性
req.body = body;
next();
})
})
module.exports = bodyParser;
4.使用Express写接口
1.搭建基本的服务器
//导入Express
const express = require('express');
//创建服务器实例
const app = express();
//----------------- 3.接口的跨域
//导入cors中间件
const cors = require('cors');
//配置中间件
app.use(cors());
//-----------------
//----------------- 2.创建API路由模块1
//解析表单数据的中间件
app.use(express.urlencoded({ extended: false }))
//导入路由模块
const router = require('./17-apiRouter');
//注册路由模块,添加前缀路径
app.use('/api', router);
//-----------------
//启动服务器
app.listen(80, () => {
console.log('http://127.0.0.1');
})
2.创建API路由模块
const express = require('express');
const apiRouter = express.Router();
//编写GET接口
apiRouter.get('/get', (req, res) => {
//拿到查询字符串
const query = req.query;
//响应处理结果
res.send({
status: 0, //0 处理成功;1 处理失败
msg: 'GET请求成功!', // 状态的描述
date: query //需要响应给客户端的数据
})
})
//编写POST接口
apiRouter.post('/post', (req, res) => {
//获取客户端通过请求体发送到服务器的 URL-encoded数据
const body = req.body;
//响应处理结果
res.send({
status: 0, //0 处理成功;1 处理失败
msg: 'POST请求成功!', // 状态的描述
data: body //需要响应给客户端的数据
})
})
module.exports = apiRouter;
3.解决跨域资源共享
1.使用CORS跨域资源共享
使用cors中间件解决跨域问题
CORS响应头部 :
1.Access-Control-Allow-Origin : 指定允许访问该资源的外域URL。
//例如:以下只允许来自 http://mingcomity.cn 的请求
res.setHeader('Access-Control-Allow-Origin','http://mingcomity.cn');
//例如:以下允许任何域的请求
res.setHeader('Access-Control-Allow-Origin','*');
2.Access-Control-Allow-Headers : 指定服务器允许的头部。
3.Access-Control-Allow-Methods : 指定允许的请求方法。默认情况之下,只允许客户端发起GET,POST,HEAD请求。
如果希望通过PUT,DELETE等方式请求服务器的资源,则需:
//只允许POST、GET、DELETE、HEAD请求方法
res.setHeader('Access-Control-Allow-Methods','POST,GET,PUT,HEAD');
//只允许所有的 HTTP 请求方法
res.setHeader('Access-Control-Allow-Methods','*');
4.Access-Control-Max-Age:缓存预检请求的秒数
简单请求:
预检请求:
简单请求与预检请求的区别:
2.使用JSONP解决跨域资源共享
注意:
如果项目中已经配置了CORS跨域资源共享,为了防止冲突,必须在配置CORS中间件之前定义JSONP接口。
实现:
//必须在配置CORS中间件之前配置JSONP请求
app.get('/api/jsonp', (req, res) => {
// 定义jsonp接口的实现
//1.获取客户端发来的回调函数的名称
const funcName = req.query.callback
//2.要发送的数据对象
const data = { name: 'zs', age: 20 }
//3.拼接一个函数的调用
const scriptStr = `${funcName}(${JSON.stringify(data)})`
//4.把拼接的字符串,返回给客户端
res.send(scriptStr);
})
四.数据库
1.常见数据库
MySQL(部分收费),Oracle(收费),SQL Server(收费),Mongodb(部分收费)
前三者属于传统型数据库(又称关系型数据库,SQL数据库):
最后一者属于新型数据库(又称非关系型数据库,NoSQL数据库):
2.MySQL的安装及配置
安装MySQL Server:专门提供数据储存和服务的软件。
安装MySQL Workbench:可视化的MySQL管理工具,通过它可以方便的操作存储在MySQL Server中的数据。
1.连接数据库
2.了解主页面的组成部分
3.创建数据库(不要有中文)
4.创建数据表
5.向表里写入数据
3.使用SQL管理数据库
1.什么是SQL
2.SQL能做什么
基于SQL做到:增删改查
3.语法
1.SELECT
用于从表中查询数据。执行的结果被储存在一个结果表中(称为结果集),语法格式如下:
-- 这是注释
-- 从 FROM 指定的【表中】,查询出【所有的】数据。 *表示【所有列】
SELECT * FROM 表名称
-- 从 FROM 指定的【表中】,查询出 【列名称(字段)】的数据
SELECT 列名称[, 列名称] FROM 表名称
注意:SQL语句中的关键字对大小写不敏感。
2.INSERT INTO
用于向表中插入新的数据行,语法格式如下:
-- 向指定的表中,插入如下几列数据,列的值通过values一一指定
-- 注意:列和值要一一对应,多个列和多个值之间,使用英文的逗号分隔
INSERT INTO 表名称 (列1,列2,...) VALUES (值1,值2,...)
3.Update
用于修改表中的数据。语法格式如下:
-- 1.用 UPADTE 指定列对应的新值
-- 2.用 SET 指定列对应的新值
-- 3.用 WHERE 指定更新的条件(特别注意)
UPDATE 表名称 SET 列名称 = 新值[, 列名称 = 新值] WHERE 列名称 = 某值
4.DELETE
用于删除表中的行。语法格式如下:
-- 从指定的表中,根据 WHERE 条件(特别注意),删除对应的数据行
DELETE FROM 表名称 WHERE 列名称 = 值
5.WHERE 子句
用于限定选择的标准。
可以在where子句中使用的运算符:
AND 表示必须同时满足多个条件,相当于 &&
OR 表示只需要满足任意一个条件即可 相当于 ||
6.ORDER BY 子句
用于根据指定的列对结果集进行排序
默认按照升序对记录进行排序
SELECT * FROM users ORDER BY status [ASC];
希望按照降序对记录排序,可以使用DESC关键字
SELECT * FROM users ORDER BY status DESC;
多重排序(用逗号分隔):
SELECT * FROM users ORDER BY status DESC,id ASC
7.COUNT(*)
用于返回查询结果的总数据条数,
SELECT COUNT(*) FROM 表名称
8.AS
用于给查询出来的列名称设置别名,可以使用AS关键字
SELECT COUNT(*) AS total FROM 表名称
SELECT username AS name, password FROM users
9.LIMIT
4.在项目中操作MySQL数据库
1.安装MySQL数据库
npm install mysql
2.配置mysql模块
使用mysql模块操作MySQL数据库之前,必须先对mysql模块进行必要的配置。
// 导入mysql模块
const mysql = require('mysql');
//建立与数据库的联系
const db = mysql.createPool({
host: '127.0.0.1', //数据库的IP
user: 'root', //登录数据库的账号
password: 'admin123', // 登录数据库的密码
database: 'my_db_01', // 指定要操作哪个数据库
})
//测试mysql模块能否正常工作 !!!没有任何实质性作用
db.query('SELECT 1', (err, results) => {
if (err) return console.log(err.message);
//只要能打印出[ RowDataPacket:{'1':1}] 就能正常工作
console.log(results);
})
3.使用mysql模块操作MySQL数据库
1.查询数据
db.query('SELECT * FROM users', (err, result) => {
//查询失败返回错误信息
if (err) return console.log(err.message);
//查询成功返回查询数据
//如果执行的是 SELECT 语句,返回的是数组
console.log(result);
})
2.插入数据
//1.要插入到users表中的数据对象
const user = { username: 'Spider-Man', password: 'pcc123' };
//2.待执行的 SQL 语句,其中英文的?表示占位符
const sqlStr = 'INSERT INTO users (username,password) VALUES (?,?)';
//3.使用数组的形式,依次为 ? 占位符指定具体的值
db.query(sqlStr, [user.username, user.password], (err, result) => {
//查询失败返回错误信息
if (err) return console.log(err.message);
//判断属性是否为1
if (result.affectedRows === 1) { console.log('插入数据成功'); }
})
3.插入数据的便捷方式
//1.要插入到users表中的数据对象
const user = { username: 'Spider-Man2', password: 'pcc4321' };
//2.待执行的 SQL 语句,其中英文的?表示占位符 便捷的sql语句
const sqlStr = 'INSERT INTO users SET ?';
//3.使用对象填充
db.query(sqlStr, user, (err, result) => {
//查询失败返回错误信息
if (err) return console.log(err.message);
//判断属性是否为1
if (result.affectedRows === 1) { console.log('插入数据成功'); }
})
4.更新数据
//1.要插更新的数据对象
const user = { id:6,username: 'aaa', password: '000' };
//2.待执行的 SQL 语句的同时,使用数组依次为占位符指定具体的值
const sqlStr = 'UPDATE users SET username=?, password=? WHERE id=?';
//3.调用db.query()执行SQL语句的同时
db.query(sqlStr,[user.username,user.password,user.id], (err, result) => {
//查询失败返回错误信息
if (err) return console.log(err.message);
//判断属性是否为1
if (result.affectedRows === 1) { console.log('更新数据成功'); }
})
5.更新数据的便捷方式
//1.要插更新的数据对象
const user = { id: 6, username: 'aaaa', password: '0000' };
//2.待执行的 SQL 语句的同时,使用数组依次为占位符指定具体的值
const sqlStr = 'UPDATE users SET ? WHERE id=?';
//3.调用db.query()执行SQL语句的同时
db.query(sqlStr, [user, user.id], (err, result) => {
//查询失败返回错误信息
if (err) return console.log(err.message);
//判断属性是否为1
if (result.affectedRows === 1) { console.log('更新数据成功'); }
})
6.删除数据
const sqlStr = 'DELETE FROM users WHERE id = ?';
db.query(sqlStr, 5, (err, result) => {
//查询失败返回错误信息
if (err) return console.log(err.message);
//判断是否删除成功
if (result.affectedRows === 1) { console.log('删除数据成功'); }
})
7.标记删除
//使用DELETE语句会真正的把数据从表中删除,保险起见,推荐使用标记删除的形式,也就是更新用户的status这样的状态字段
db.query('UPDATE users set status = ? WHERE id=?', [1, 6], (err, result) => {
//查询失败返回错误信息
if (err) return console.log(err.message);
//判断是否标记删除成功
if (result.affectedRows === 1) { console.log('标记删除数据成功'); }
})
五.前后端的身份认证
1.Session认证机制
2.在Express中使用Session认证
1.安装express-session中间件
npm install express-session
2.配置express-session中间件
//导入Session中间件
const session = require('express-session')
//配置Session中间件
app.use(
session({
secret: 'mingcomity', //任意字符串
resave: false, //固定写法
saveUninitialized: true, //固定写法
})
)
3.存数据
app.post('/api/login', (req, res) => {
// 判断用户提交的登录信息是否正确
if (req.body.username !== 'admin' || req.body.password !== '000000') {
return res.send({ status: 1, msg: '登录失败' })
}
// TODO_02:请将登录成功后的用户信息,保存到 Session 中
//将用户的信息,储存到Session
req.session.user = req.body;
//将用户的登录状态,储存到Session
req.session.islogin = true;
res.send({ status: 0, msg: '登录成功' })
})
4.读取
app.get('/api/username', (req, res) => {
// TODO_03:请从 Session 中获取用户的名称,响应给客户端
if (!req.session.islogin) {
return res.send({ status: 1, msg: 'fail' })
}
res.send({
status: 0,
msg: 'success',
username: req.session.user.username,
})
})
5.清空
// 退出登录的接口
app.post('/api/logout', (req, res) => {
// TODO_04:清空 Session 信息
req.session.destroy();
res.send({
status:0,
msg:'退出登录成功'
})
})
3.JWT认证机制
4.在Express中使用JWT
1.安装JWT相关的包
npm install jsonwebtoken express-jwt
jsonwebtoken用于生成JWT字符串
express-jwt用于将JWT字符串解析还原成JSON对象
2.导入JWT相关的包
使用require()函数,分别导入JWT相关的两个包:
const jwt =require('jsonwebtoken')
const expressJWT = require('express-jwt')
3.定义secret密钥
4.在登录成功后生成JWT字符串
// 登录接口
app.post('/api/login', function(req, res) {
// 将 req.body 请求体中的数据,转存为 userinfo 常量
const userinfo = req.body
// 登录失败
if (userinfo.username !== 'admin' || userinfo.password !== '000000') {
return res.send({
status: 400,
message: '登录失败!'
})
}
// 登录成功
// TODO_03:在登录成功之后,调用 jwt.sign() 方法生成 JWT 字符串。并通过 token 属性发送给客户端
//参1:用户的信息对象 参2:加密的密钥 参3:配置对象,可以配置的token的有效期
const tokenStr = jwt.sign({ username: userinfo.username }, secretKey, { expiresIn: '30s' })
res.send({
status: 200,
message: '登录成功!',
token: tokenStr // 要发送给客户端的 token 字符串
})
})
5.配置解析token字符串的中间件
5.将JWT字符串还原成JSON对象
// TODO_04:注册将 JWT 字符串解析还原成 JSON 对象的中间件
//expressJWT({ secret: secretKey }) 就是用来解析 Token 的中间件
//.unless({path:[/^\/api\//]}) 用来指定哪些接口不用访问权限
app.use(expressJWT({ secret: secretKey }).unless({ path: [/^\/api\//] }))
6.使用req.user获取用户信息
7.捕获解析JWT失败后产生的错误
描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本
描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本
描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本
描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本
述文本描述文本描述文本描述文本描述文本描述文本
述文本描述文本描述文本描述文本描述文本描述文本
描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本
描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本
查看全部3条回复
描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本
描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本
描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本
描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本描述文本