node

林浩漫
2023-12-01

1 NodeJS

nodejs是运行在服务器端的一门语言
NodeJs有三大特点:
	单线程,非阻塞IO,事件驱动
单线程:
	整个程序只有一条线程执行(同一时间只能做一件事)
	
非阻塞I/O:
	I/O:input/output 输入/输出
			input:从磁盘中输入内容到内存中	output:从内存中读取内容到磁盘中
	非阻塞I/O:当线程执行的时候,如果遇到了I/O操作,只是开启一个任务,线程不会等待,去执行下一条任务
	阻塞I/O:当线程执行的时候,如果遇到了I/O操作,开启一个任务并等待任务执行完毕之后,才去执行下一条任务

事件驱动:
	由于nodejs是非阻塞的,线程不会等待,但是后续的任务如何执行,此时会触发一个事件,由该事件将后续的任务重新放入执行队列中(nodejs循环队列)
	适用场景:
		使用nodejs搭建的服务器,特别适用异步多,高并发
		不适合计算,因为计算是同步的,会造成阻塞,也可以通过worker解决

2 运行NodeJS

node 文件名
nodemon
npm install -g nodemon
每次修改程序都会自动发布

3 模块化

nodejs也是模块化的。模块化的代表框架:
	RequireJS:遵循的是AMD规范(异步规范),一个文件就是一个模块
		引入文件通过:通过require方法引入
		暴露接口:exports, module.exports,也可以使用reurn这种方式
	Seajs:遵循的是CMD规范(异步规范),一个文件就是一个模块。
		引入文件通过require方法
		暴露接口:exports, module.exports
	Nodejs是遵循CommonJS规范(同步规范):一个文件就是一个模块
		引入文件通过require方法
		暴露接口:exports, module.exports

4 模块分类

在nodejs中模块分为三类:
	1 内置模块(核心模块)
		例如:HTTP, HTTPS, Path, FS, QueryString
		内置的模块可以直接引入并使用,通过require方法直接引入模块名称
	2 第三方模块(自定义模块)
		例如:babel-core, typescript
		第三方模块需要通过npm安装后才能使用,通过require方法直接引入模块的名称
	3 文件模块(一个文件就是一个模块)
		引入文件模块要使用相对路径(相对于当前文件所在的位置,如: ./ ../)
5 node_modules
该文件夹用于存储所有的第三方模块,当把文件存储在该文件夹中,我们就可以像引入内置模块那样来引入模块文件了。
特点:
	该文件所在的位置可以是在引入文件的同级也可以是上级目录或者是上上级目录(祖先目录,直到硬盘的根路径),当我们引入模块的时候,就可以根据就近原则,找到node_modules文件夹中对应的模块

6 内置模块–HTTP

HTTP模块用于搭建服务器的
	createServer(handle)	该方法用于搭建HTTP服务器
		handle:处理函数,函数中有两个参数:
			第一个参数:req对象:全程request请求对象,常用的属性:
				url(本次请求的路径)  method(本次请求的方式) headers(请求头对象相关信息)
			第二个参数:res对象:全称response响应对象,常用的属性:
				write(返回数据,不会断开连接,必须与end方法一起使用)
				end(返回数据,会断开连接,该方法只接受字符串类型的参数以及Buffer数据类型)
				setHeader(用于设置响应头)返回值是服务器对象
监听方法
	createServer创建的应用程序提供了listen方法,用来监听端口号
	listen(port, ip, handle)
		port:	监听的端口号,不要使用1000以内的(可能被占用,计算机内部的程序可能占用)
		ip:		指定的ip地址,可以省略
		handle:	监听成功之后执行的方法
// 引入模块
// 注意:nodejs从6.0开始支持ES6
let http = require('http');

// 搭建服务器
let app = http.createServer(function(req, res) {
    // console.log(req, res);
    // 请求相关信息
    console.log('req.url>>', req.url);
    console.log('req.method>>', req.method);
    console.log('req.header>>', req.header);
    // 响应
    // 设置响应头部
    res.setHeader('Content-Type', 'text/plain; charset=utf-8');
    res.write('第一次写入');
    res.write('第二次写入');
    // 执行end方法断开连接
    res.end('响应结束');

})

// 监听端口号
app.listen(3000, () => {
    console.log('http server listen at 3000');
})

7 内置模块–FS

FS模块全称:file System 文件系统。作用是用于操作文件夹以及文件的,使用的时候要引入fs模块。操作文件是异步的,因此fs模块为每一个操作提供了两个方法:同步方法(sync),异步方法(回调函数监听)

创建文件
	异步方法
	fs.apppendFile(fileName, content, callback)
		fileName:	创建的文件名称(合法的路径)
		content:	追加的内容
		callback:	回调函数
			参数表示错误异常
			如果创建成功,则返回null,如果创建失败,则返回一个错误对象
// 引入模块
var { appendFile, appendFileSync } = require('fs');

// 写入文件
// 同步方法
appendFileSync('demo.txt', 'hello');
// 为了保证程序正常运行,我们常常将同步的操作放在try catch语句中
try {
    var result = appendFileSync('&^*%**&%*%*%*&%&*%&*.txt', 'hello');
} catch (e) {
    console.log(222, e);        // e是捕获的错误
}
console.log(111, result);

异步方法
appendFile('style.css', `
    h1 {
        color: red;
    }
`, function(err) {
    console.log(err);
})

// 可以包含路径,但路径必须存在
appendFile('ickt/demo.txt', 'hello', (err) => {
    console.log(err);
})

// 多次执行,如果文件存在,会追加内容,不会覆盖原有文件

创建文件夹
	fs.mkdir(pathName, callback)		该方法用于创建文件夹
		pathName:	文件夹名称
		callback:	回调函数
			参数表示错误异常
			如果创建成功,则返回null
			如果创建失败,则返回一个错误对象
let { mkdir, mkdirSync } = require('fs');

// 创建目录:注意,已经存在的目录,不能再次被创建
// 同步方法
 try {
     var result = mkdirSync('ickt');
 } catch (e) {
     console.log(222, e);
 }

// console.log(111, result);

// 异步方法
// mkdir('demo', function(err) {
//     console.log(222, err);
// })
// console.log(111);

// 创建多层级目录
// 注意:创建子目录要保证父目录存在
// mkdir('demo/a', function(err) {
//     console.log(err);
// })

// 目录名包含扩展名仍然会被看成目录创建,不会创建文件
// mkdir('demo/abc.txt', function(err) {
//     console.log(err);
// })
删除文件:
	fs.unlink(fileName, callback)	该方法用于删除文件
		fileName:	要删除的文件
		callback:	回调函数
			参数表示错误异常
			如果删除成功,则返回null
			如果删除失败,则返回一个错误对象
let { unlink, unlinkSync, rmdir, rmdirSync } = require('fs');

// 删除路径
// 同步方法
// try {
//     // rmdirSync('demo');
//     // 先删除文件,再删除问价夹
//     unlinkSync('demo/abc.txt');
//     rmdirSync('demo');
//     // 只能删除空目录,还要将内部的文件及文件夹删除
// } catch (e) {
//     console.log(222, e);
// }

// 异步方法
unlink('demo/abc.txt', (err) => {
    rmdir('demo', () => {
        console.log('success');
    })
});

删除文件夹
    fs.rmdir(dirName, callback)	该方法用于删除文件夹
    	注意:该方法只能删除空文件夹
        dirName:	要删除的文件夹名称
        callback:	回调函数
            参数表示错误异常
            如果删除成功,则返回null
            如果删除失败,则返回一个错误对象
修改文件名称
	fs.rename(oldName, newName, callback)
		oldName:	旧名字
		newName:	新名字
		callback:	回调函数
            参数表示错误异常
                如果修改成功,则返回null
                如果修改失败,则返回一个错误对象
let { rename, renameSync, readFile, readFileSync } = require('fs');

// 修改文件名
// 同步方法
// try {
//     renameSync('abc.txt', 'def.txt')
// } catch(e) {
//     console.log(e);
// }
// 异步方式
// rename('def.txt', 'hello.txt', err => console.log(err));


// 读取文件
// 同步方法
// try {
//     // 默认是Buffer数据,我们可以调用toString方法转为字符串之后查看
//     // var content = readFileSync('hello.txt');
//     // console.log(content.toString());
//     // 也可以传递一个参数'utf-8'
//     var content = readFileSync('hello.txt', 'utf-8');
//     console.log(content);
// } catch(e) {
//     console.log(e);
// }

// 异步方法
// readFile('hello.txt', (err, content) => {
//     console.log(err, content.toString());
// })
readFile('hello.txt', 'utf-8', (err, content) => {
    console.log(err, content);
})

读取文件
	fs.readFile(fileName, callback)
		fileName:	要读取的文件名称
		callback:	回调函数,有两个参数
			第一个参数:错误异常
				如果创建成功	则返回null
				如果创建失败,则返回一个错误对象
			第二个参数 读取成功时候的数据
				默认是Buffer数据,我们可以调用toString方法转为字符串之后查看
判断文件的状态
	fs.stat(targetName, callback)
		targetName:	要判断的文件名称
		callback:	回调函数,有两个参数:
			第一个参数是错误异常
			第二个是状态对象
				我们可以通过状态对象调用isDirectory()
					如果为真,则表示是文件夹
					如果为假,则为文件
let { stat, statSync, readdir, readdirSync } = require('fs');

// 读取文件状态
// 同步
try {
//     // var state = statSync('a/b/c');
//     var state = statSync('a/b/c/d.txt');
//     console.log(state, state.isDirectory());

    // 读取文件夹内容
    // var arr = readdirSync('a/b')
    // var arr = readdirSync('a/b/c')
    // console.log(arr);
} catch(e) {}

// 异步
// stat('a/b', (err, state) => console.log(err, state, state.isDirectory()))
// stat('a/b/c/d.txt', (err, state) => console.log(err, state, state.isDirectory()))

// 读取文件夹内容
// readdir('a/b', (err, arr) => console.log(err, arr));
readdir('a/b/c', (err, arr) => console.log(err, arr));
读取问件夹内容
	fs.readdir(dirName, callback)
		dirName:	读取的问价夹名称
		callback:	回调函数,有两个参数
			第一个参数表示错误异常
			第二个参数是一个数组,数组中的每一项都是读取到的每一个文件
注意:在fs模块中,方法后面加上Sync就是同步的
fs.exists(filePath, (result) => resolve(result))判断文件是否存在
  • 删除非空目录同步写法
let { readdirSync, statSync, unlinkSync, rmdirSync } = require('fs');

// 删除非空目录
function del(dir) {
    // 获取目录的内容,如果该目录还有子目录或子文件,递归删除
    try {
        // 读取内容
        let arr = readdirSync(dir);
        // 遍历每一个文件
        for (var i = 0; i < arr.length; i++) {
            // 拼凑路径
            let path = dir + '/' + arr[i];
            // 逐一将每一个成员删除
            // 获取状态
            let state = statSync(path);
            // 判断是否是文件夹
           if (state.isDirectory()) {
                // 递归
               del(path);
           } else {
               unlinkSync(path);
           }
        }
        // 所有的成员都删除了,就可以删除目录了
        rmdirSync(dir)
    } catch(e) {
        console.log(e);
    }
}

del('a');
  • 删除非空目录异步写法
const { resolveSoa } = require('dns');
let { unlink, rmdir, readdir, stat } = require('fs');

// 删除非空目录
async function del(dir) {
    // 读取文件内容
    let arr = await new Promise(resolve => {
        readdir(dir, (err, arr) => {
            resolve(arr);
        })
    })
    // 如果有子目录
    if (arr) {
        // 遍历每一个成员,逐一删除
        for (let i = 0; i < arr.length; i++) {
            let path = dir + '/' + arr[i];
            let state = await new Promise(resolve => {
                stat(path, (err, state) => {
                    resolve(state);
                })
            })
            if (state.isDirectory()) {
                await del(path);
            } else {
                await new Promise(resolve => {
                    unlink(path, () => {
                        resolve();
                    });
                })
            }
        }
    }
    
    await new Promise(resolve => {
        rmdir(dir, () => {
            resolve();
        });
    })
}

del('a');

8 内置模块–URL

该模块的作用可以实现将url字符串与url对象互相转换。使用的时候需要引入url模块
	parse:该方法可以将url字符串解析成url对象
		使用方式:url.parse(url_str, bool)
			url_str:	url字符串
			bool:		是一个布尔值
				默认是false,当传递true的时候,会将url对象中的query部分变成对象
	format:该方法用于实现将url对象再次解析为url字符串
// 引入模块
// 注意:nodejs从6.0开始支持ES6
let http = require('http');
// 引入url模块
let url = require('url');

// 搭建服务器
let app = http.createServer(function(req, res) {
    // console.log(req, res);
    // 请求相关信息
    console.log('req.url>>', req.url);
    console.log(url.parse(req.url));
    console.log(url.parse(req.url, true));      // 默认是false,当传递true的时候,会将url对象中的query部分变成对象
    var result = url.parse(req.url, true);
    // 转成url
    console.log(111, url.format(result));

    // 响应
    // 设置响应头部
    res.setHeader('Content-Type', 'text/plain; charset=utf-8');
    res.write('第一次写入');
    res.write('第二次写入');
    // 执行end方法断开连接
    res.end('响应结束');

})

// 监听端口号
app.listen(3000, () => {
    console.log('http server listen at 3000');
})

9 内置模块–PATH

该模块的作用用于处理请求的路径的。使用的时候,需要引入path模块
	parse:	该方法可以实现将路径字符串转为对象
		使用方式:path.parse(path_str)
	join:该方法用于拼接路径
		使用方式:path.join(path1, path2 ...)
			path1	第一个路径
			path2	第二个路径
			可以传递多个路径,返回值就是拼接后的路径
	path.extname(filePath).slice(1);获取文件扩展名
let path = require('path');

// 解析
console.log(path.parse('/demo/index.html'));

// 路径拼接
console.log(path.join('hello', 'demo/index.html'));

10 内置模块–QueryString

该模块的作用是用于处理query字符串或者是类似query的字符串。使用的时候要引入querystring模块
	例如:
		query字符串:		'a=1&b=2'
		类似query的字符串:   'a:1$b:2'
	parse:该方法用于将query字符串解析为对象
		使用方式:qs.parse(query_str, bigSplit, smallSplit)
			query_str:		要处理的字符串(query字符串或者是类似于query字符串)
			bigSplit:		大的分隔符 默认是&
			smallSplit:		小的分隔符,默认是 =
let qs = require('querystring');

// 解析query
// var result = qs.parse('a=1&b=2');
var result = qs.parse('a$1%b$2', '%', '$');
console.log(result);

11 NPM介绍

NPM:Node Package Manager(node第三方包管理器)
	官网:https://www.npmjs.com/
在nodejs中的文件分为三类:
	1 核心模块			这类文件 可以直接引入
	2 第三方模块文件	  这类模块要安装之后才能使用
	3 文件模块			我们写的js文件就是一个模块
node_modules文件夹:
	该文件夹用于存储所有的第三方文件,当我们需要引入内部文件的时候,就可以像引入内置模块那样直接引入了

12 使用指令

npm install ModuleName
	该指令可以实现将ModuleName下载到本地
	注意:install可以简写为i
npm install ModuleName1 ModuleName2
	共同下载两个文件

如果,模块存在指令文件,需要向全局安装:npm install ModuleName -g

13 package.json

每一个项目的根目录中,都有一个package.json文件,用于定义这个项目所需要的各种模块,以及项目的配置信息
生成package.json
	我们可以通过npm init即可生成该文件
	但是创建的时候会出现一系列的询问
	一直按回车即可

14 Express

Express

Express是基于Nodejs平台的一个快速、开放、极简的web应用框架
可以帮助我们快速的搭建后台服务器,快速的处理get请求、post请求。。。
官网:http://www.expressjs.com.cn/
下载:npm install express
// 创建服务器
// 1 引入express
let express = require('express');

// 2创建应用程序
let app = express()

// 静态化针对于请求来说
// /web/ 请求 去web目录下查找文件
app.use('/web/', express.static('./web/'))

// static开头的请求,也去web下查找
// app.use('/static/', express.static('./web/'))

// 使用绝对路径
// let path = require('path')
// let root = process.cwd()
// app.use('/static/', express.static(path.join(root, './web/')))

// web目录下的资源可以直接访问
// app.use(express.static('./web/'))

// 2监听端口号
app.listen(3000)

15 搭建后台服务器

引入express
	let express = require('express')
创建应用程序
	let app = express()
监听端口号
	app.listen(3000)

当我们需要访问某一个目录的时候,此时就要对该目录进行静态化
我们可以使用Express中唯一一个中间件--static方法实现目录的静态化
	中间件:处理请求的方法,使用中间件用use方法

16 处理get请求

在Express中处理请求的方式有两种:1 通过app.get		2 通过Router路由
第一种:使用方式:app.get(path, callback)
	path:请求的路径接口
	callback:回调函数,有三个参数:
		第一个参数:req请求对象
		第二个参数:res响应对象
		第三个参数:next放行函数
获取query数据:可以通过req.query直接获取上传的数据
	req.query是Express对请求对象封装后的
// 创建服务器
// 1 引入express
let express = require('express');

// 2创建应用程序
let app = express()

app.use(express.static('./web/'))

// 配置get请求
app.get('/login', function(req, res, next) {
    console.log(req.query)
    // 返回json数据
    res.json({ color: 'red' })
    // 通过send返回,不能多次使用
    // res.send('456')
    // 返回数据,不能多次使用
    // res.end('123')
})

// 2监听端口号
app.listen(3000)

17 处理post请求

同get请求类似
app.post(path, callback)
		path:请求的路径接口
		callback:回调函数,有三个参数:
            第一个参数:req请求对象
            第二个参数:res响应对象
            第三个参数:next放行函数
            
获取post请求的数据
	如果想要获取post请求传递的数据,则需要借助中间件body-parse之后还要进行配置
		app.use(bodyParser.urlencoded({ extended: false }))
		然后就可以通过req.body来获取浏览器端提交的数据
// 创建服务器
// 1 引入express
let express = require('express');
// 引入post请求处理的中间件
let bodyPaeser = require('body-parser')

// 2创建应用程序
let app = express()

// 添加中间件
app.use(bodyPaeser.urlencoded({
    extended: false
}))

app.use(express.static('./web/'))

// 配置post请求
app.post('/login', function(req, res, next) {
    console.log(req.query)
    // 返回json数据
    res.json({ color: 'red' })

})

// 2监听端口号
app.listen(3000)

18 Router路由对象

在Express中也可以通过路由对象处理各种请求
使用方式:
	1 通过Express获取路由模块
	2 创建路由实例对象
	3 安装路由对象
放行函数
	在Express中允许对一个接口,添加多个处理函数
	在接口函数中的第三个参数就是next放行函数
	该函数不执行,后面的回调函数就不会执行

app.js

// 创建服务器
// 1 引入express
let express = require('express');
// 引入post请求处理的中间件
let bodyPaeser = require('body-parser')

// 2创建应用程序
let app = express()

app.use(express.static('./web/'))

// 引入路由
let router = require('./router')
// 安装路由
app.use(router)

// 2监听端口号
app.listen(3000)

router.js

let express = require('express');
// 获取路由
let { Router } = express;
// 创建路由对象
let router = new Router();
// 请求写在路由上
router.get('/login', (req, res) => {
    console.log(req.query);
    res.json('success')
})
// 暴露接口
module.exports = router;

19 Cookie

Cookie是HTTP协议中请求头中的一个字段
	作用:存储用户信息,如,验证是否登录等
Cookie是服务器来设置,由浏览器来保存,cookie的内存空间是受到限制的,大约4kb
登录原理
	HTTP协议是一个无状态的协议,理论上来说是无法保持登录的
	当用户填写完数据并且点击提交按钮的时候,此时浏览器会发送一个HTTP请求到服务器,服务器得到响应并经过验证之后,会在响应头中set-cookie字段中设置用户的信息,之后返回给前端,前端检测到set-cookie字段中的内容之后,会生成一个cookie文件,将用户的信息保存起来
	当用户向同一个服务器发送请求的时候,会将cookie文件中的内容携带到服务器中,经过服务器验证之后,就可以判断出用户是否登陆过
Cookie操作
设置cookie
	使用方式:res.cookie(key, value, options)
		key:		数据名称
		value:		设置的数据
		options:	配置项
获取cookie
	想要获取cookie中的内容,必须借助cookie-parser
	通过req.cookies对象获取,之后要进行设置
浏览器端获取cookie数据,通过document.cookie属性获取
// 引入express
let express = require('express');
// 引入处理请求的中间件
let bodyParser = require('body-parser');
// 引入cookie中间件
let cookieParser = require('cookie-parser');
// 创建应用
let app = express();
// 安装中间件
// 处理请求体
app.use(bodyParser.urlencoded({ extended: false }))
// 解析cookie
app.use(cookieParser());


// 静态化
app.use(express.static('./web/'))

// 登录
app.post('/login', (req, res) => {
    // console.log(req.body);
    // 存储cookie
    res.cookie('username', req.body.username)
    res.cookie('password', req.body.password)
    res.json('success')
})

// 验证登录
app.get('/checkLogin', (req, res) => {
    console.log(111, req.cookies);
    res.json(111)
})

app.listen(3000)

21 Session

Session是服务器上的一段内存空间,也是用于存储数据。但是Session依赖于Cookie的
登录原理:
	当用户通过表单或者ajax发送请求的时候,浏览器会发出一个HTTP请求到服务器,服务器得到响应并开始处理请求,之后返回一个随机字符(也叫作密钥),该字符串对于浏览器来说没什么作用,但是对于服务器来说,可以通过该随机字符串识别用户信息,之后返回数据给前端,并在响应头的set-cookie中设置用户的信息(随机字符串)。之后,浏览器检测cookie字段中的内容,会生成一个cookie文件,当再次向同一个服务器发送请求的时候,则会将cookie中的内容(随机字符串)携带到服务器,然后经过服务器处理之后,就可以判断用户是否登录过
Session是服务器上的一段内存空间,通常是保存一些重要的信息,一些不重要的信息保存在cookie中就好

22 session操作

在Express中可以通过req.session用于设置以及获取session
当想要获取session中的内容的时候,需要借助中间件express-session
我们也要进行配置:
	app.use(expressSession({
		secret:配置密钥
		resave:每一次访问session的时候,是否重置
		savaUninitialized:在初始化的时候是否设置session
	}))
// 引入express
let express = require('express');
// 引入处理请求的中间件
let bodyParser = require('body-parser');
// 引入cookie中间件
let cookieParser = require('cookie-parser');
// 引入session中间件
let expressSession = require('express-session');
const session = require('express-session');
// 创建应用
let app = express();
// 安装中间件
// 处理请求体
app.use(bodyParser.urlencoded({ extended: false }))
// 解析cookie
app.use(cookieParser());
// 解析session
app.use(session({
    secret: 'aichuanketang',
    resave: true,
    saveUninitialized: false
}))

// 静态化
app.use(express.static('./web/'))

// 登录
app.post('/login', (req, res) => {
    // console.log(req.body);
    // 存储cookie
    res.cookie('username', req.body.username)
    // res.cookie('password', req.body.password)
    // 将密码放在session中
    req.session.password = req.body.password
    res.json('success')
})

// 验证登录
app.get('/checkLogin', (req, res) => {
    // 注意:session是存储在服务器上的,一旦重启服务器,session被重置
    console.log(111, req.cookies, 222, req.session);
    // res.json(111)
    // 如果有密码,就是登陆的
    if (req.session.password) {
        res.json('登录了')
    } else {
        res.json('没有登录')
    }
})

app.listen(3000)

23 token操作

含义:凭证、令牌
生成:由后端生成
存储:存储在前端的cookie中或者本地存储中
格式:头部、数据、签名
作用:验证用户身份
流程机制:客户端使用用户名跟密码发送登录请求。服务器端收到请求,去验证用户名与密码
	验证成功后,服务器端会生成一个Token字符串,再把这个Token字符串发送给客户端,服务器端不会保留该Token字符串
	客户端收到Token字符串,可以把它存储起来,比如放在Cookie里或者Local Storage里。客户端每次向服务器端请求资源的时候需要带着服务器端签发的Token字符串
	服务器端收到请求,然后去验证客户端请求里面带着的Token,如果验证成功,响应本次请求,如果验证失败则服务器可以拒绝
token特点:
	服务器无状态:因为服务器只负责解密而不负责存储
	把所有状态信息都附加在Token上,服务器就可以不保存。但是服务器端仍然需要认证Token有效。只要服务器端能确认是自己签发的Token,而且其信息未被改动过,那就可以认为Token有效。“签名”就是做这个的
	Token是在服务器端产生的,如果前端使用用户名/密码向服务器端请求认证,服务器端认证成功,那么在服务器端会返回Token给前端
	前端可以在每次请求的时候带上Token证明自己的合法地位。如果这个Token在服务器端持久化(比如存入数据库),那它就是一个永久的身份令牌

24 token标准

JWT标准:
	标准的Token有三个部分:
		header(头部)
		payload(数据):iss:Issuer,发行者,   sub:Subject,主题,      aud:Audience,观众,		                        exp:Expiration time,过期时间,   nbf:Not before,    iat:Issued at,发                        行时间,      jti:JWT ID
		signature(签名):header,payload,secret	

25 token使用步骤

使用步骤:
	1 引入jwt(jsonwebtoken)模块
	2 定义指定加密字符串
	3 当用户登录成功之后,通过jwt提供了sign方法,可以将用户的信息以及加密字符串捆绑到一起生成token字符串
	4 将用户的信息返回给前端。前端可以将token字符串保存在本地存储中
	5 当前端再次发送请求的时候,将token字符串携带到服务器中
	6 经过jwt提供的verify方法进行解密。之后返回给前端
// 引入express
let express = require('express');
// 引入处理请求的中间件
let bodyParser = require('body-parser');
// 引入cookie中间件
let cookieParser = require('cookie-parser');
// 引入session中间件
let expressSession = require('express-session');
// 引入模块
const jwt = require('jsonwebtoken');
// 创建应用
let app = express();
// 安装中间件
// 处理请求体
app.use(bodyParser.urlencoded({ extended: false }))
// // 解析cookie
// app.use(cookieParser());
// // 解析session
// app.use(session({
//     secret: 'aichuanketang',
//     resave: true,
//     saveUninitialized: false
// }))

// 定义加密字符串
let str = 'somewords';

// 静态化
app.use(express.static('./web/'))

// 登录
app.post('/login', (req, res) => {
    // console.log(req.body);
    let token = jwt.sign(req.body, str)
    // 返回给浏览器端
    res.json({
        token
    })
})

// 验证登录
app.get('/checkLogin', (req, res) => {
    // 解密
    let data = jwt.verify(req.query.token, str);
    res.json(data)
})

app.listen(3000)

26 ejs与文件上传

EJS模板

EJS是后台服务器模板,天生可以与Express搭配使用,无需引入,但是需要下载:npm install ejs
可以通过res.render方法渲染一个模板,在该页面中提供了<%=%>插值语法
在<%=%>是真正的js环境,因此可以使用表达式。使用步骤:
	1 下载ejs
	2 创建一个views文件夹
	3 在views文件中创建以.ejs后缀名称的文件
	4 可以通过res.render(path, data)渲染一个模板
	5 在<%=%>书写要被替换的内容
	
	    <!-- 渲染标签,要将=变成- -->
    	<%-msg%>

app.js

// 引入express
let express = require('express');

// 创建应用
let app = express();

// 静态化
// app.use(express.static('./web/'))

// 配置路由
app.get('/', (req, res) => {
    // 渲染模板去views目录下寻找
    res.render('ickt.ejs', {
        title: '你好',
        intro: 'hello',
        msg: '<h2>hello</h2>'
    })
})


app.listen(3000)

test.ejs文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title><%=title%></title>
</head>
<body>
    <h1>ickt page</h1>
    <h1><%=intro%></h1>
    <!-- 渲染标签,要将=变成- -->
    <%-msg%>
</body>
</html>

27 单文件上传

需要借助formidable模块
该模块引入之后,除了可以处理普通的post请求之外的数据,还可以处理上传的图片文件等数据
	注意:此时,必须要给form表单设置enctype属性,才能够让formidable解析req对象
使用步骤:
	1 浏览器端,修改enctype类型为multipart/form-data
	2 服务器端,引入formidable模块并实例化
	3 服务器端,修改文件存储位置
	4 服务器端,解析请求对象,获取上传的文件信息
	5 服务器端,通过fs模块,存储上传的文件

html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <!-- 登录表单 -->
    <form action="/upload" method="POST" enctype="multipart/form-data">
        <input type="text" value="ickt" name="filename">
        <input type="file" name="file">
        <p><button type="submit">上传</button></p>
    </form>

</body>
</html>

app.js

// 引入express
let express = require('express');
let Formidable = require('formidable')
let path = require('path')
let fs = require('fs')

// 创建应用
let app = express();
let root = process.cwd();
// 静态化
app.use(express.static('./web/'))

// 配置路由
app.post('/upload', (req, res) => {
    // 创建文件接收器
    let fd = new Formidable()
    // 修改缓存文件地址
    fd.uploadDir = path.join(root, './upload');
    // 接收文件
    fd.parse(req, function(err, fileds, files) {
        if (err) {
            return res.json('上传失败')
        }
        // 定义文件名
        let filePath = './upload' + fileds.filename + path.extname(files.file.name);
        // 存储
        fs.rename(files.file.path, path.join(process.cwd(), filePath), function(err) {
            if (err) {
                return res.json('上传失败')
            }
            res.json({
                msg: '上传成功',
                data: filePath
            })
        })
    })
    // console.log(fd);


})



app.listen(3000)

28 多文件上传

如果想要选中多个文件,必须给input元素添加multiple属性
此时,就可以按下ctrl同时选中多张图片
但是如果还是使用单文件上传的方式接收文件就不行了
此时要服务器端监听一个事件:file事件
该事件会在每一次上传的时候都会执行一次
        <!-- multiple设置多选 accept="image/*"限制上传的文件类型,只能看到图片 -->
        <input type="file" name="file" multiple accept="image/*">

html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <form action="/upload" method="POST" enctype="multipart/form-data">
        <input type="text" value="ickt" name="filename">
        <!-- multiple设置多选 accept="image/*"限制上传的文件类型,只能看到图片 -->
        <input type="file" name="file" multiple accept="image/*">
        <p><button type="submit">上传</button></p>
    </form>
</body>
</html>

app.js

// 引入express
let express = require('express');
let Formidable = require('formidable')
let path = require('path')
let fs = require('fs')

// 创建应用
let app = express();

// 静态化
app.use(express.static('./web/'))

let root = process.cwd();
// 配置路由
app.post('/upload', (req, res) => {
    // 实例化
    let fd = new Formidable()
    // 缓存文件位置
    fd.uploadDir = path.join(root, './upload')
    // 定义数组,缓存上传的文件
    let arr = [];
    // 接收多个文件
    fd.on('file', (filed, file) => arr.push(file))
    // 解析请求
    fd.parse(req, (err, fileds, files) => {
        // 将arr数组转成包含Promise的数组
        let dealArr = arr.map(file => new Promise((resolve, reject) => {
            // 定义新的地址
            let filePath = path.join('/upload/', path.parse(file.path).name + path.extname(file.name))
            // 将文件重命名
            fs.rename(file.path, path.join(root, filePath), err => {
                // 有错误,操作失败
                if (err) {
                    reject('操作失败')
                }
                else {
                    // 操作成功
                    resolve(filePath)
                }
            })
        }))
        // 并行,串行
        Promise.all(dealArr)
            .then(
                // 成功
                data => res.json(data),
                // 失败
                err => res.json(err)
            )
    })


})

app.listen(3000)

29 MongoDB

MongoDB

数据库大致分为两类:
	SQL数据库(关系型数据库):mySql、oracle、sqlserver等
	NoSql数据库(非关系型数据库):Mongodb数据库等
SQL数据库:
	结构化查询语言:structure query language
	它的结构组成:应用程序=》数据库=》表=》数据。使用SQL数据库要学习SQL语言
NoSql数据库
	它的结构组成:应用程序=》数据库=》集合=》文档
	SQL与NOSQL的最大区别在于NoSQL的集合中的文档,它的结构可以不固定

30 Nodejs中操作MongoDB数据库

nodejs与Mongodb都是单独的应用程序,需要借助模块:mongodb

31 WebSocket

Websocket

websocket是H5新增的,与HTTP协议是同级别的,只不过它是有状态的(有持久连接)
HTTP协议:
	前端发送请求,后端得到响应并且返回数据,断开连接,之后想要再次发送新的请求,就要再次建立连接通道才能发送请求
websocket:
	前端发送请求,后端得到响应并返回数据,就保持连接,之后想要再次发送新的请求,就可以使用已经建立起来的通道再次发送请求

socket.io

socket.io是nodejs第三方模块文件,用来统一浏览器发送socket请求的方式
下载:npm install socket.io
使用socket.io
	let socket_io = require('socket.io')
	socket_io(server)
在地址栏输入:http://localhost:3000/socket.io/socket.io.js

socket服务

后台搭建
	第一步执行socket
	第二步监听connection事件
		该事件会在前端发送socket请求的时候触发
前端搭建
	第一步:通过script标签引入socket.io.js文件
	第二步:当引入socket.io.js文件之后,向全局暴露一个io变量
		要执行io方法,并且监听connect方法
当我们执行io方法的时候,就会自动发送一个socket请求。这样后台就可以接收到信息
// 引入http模块
let http = require('http');
// 引入socket
let socket = require('socket.io');

// 创建服务器
let app = http.createServer(function(req, res) {
    res.end('success')
})
// 第二种方式
// let app = http.Server(function(req, res) {
//     res.end('server success')
// })

// 处理服务器
socket(app)

// 监听端口号
app.listen(3000)
// 引入express
let express = require('express');
// 引入http
let http = require('http');
// 引入socket
let socket = require('socket.io');

// 创建服务器
let app = express();

app.get('/', (req, res) => {
    res.end('success')
})

// 通过http模块创建应用,https也是一样的
let server = http.createServer(app);
socket(server);
server.listen(3000)

// 监听端口
// app.listen(3000)

// 在express中使用socket共需三步
    // 1 创建express应用
    // 2 创建http服务
    // 3 加入socket协议

32 前后端通信

前端socket:
	on方法监听消息
		第一个参数表示消息名称		第二个参数是执行的函数
	emit方法触发消息
		第一个参数是消息名称		从第二个参数开始,是传递的数据
后端socket:
	on方法监听消息
		第一个参数表示消息名称		第二个参数是执行的函数
	emit方法触发消息
		第一个参数是消息名称		从第二个参数开始,是传递的数据

html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <h1>hello world</h1>
    <script src="/socket.io/socket.io.js"></script>
    <script>
        // 发送socket请求、
        let socket = io();
        // 监听浏览器端与服务器端连接
        socket.on('connect', function(...args) {
            // console.log(args);
            // 监听消息
            socket.on('ickt', (...args) => {
                console.log(args);
            })
        })
        // 浏览器端向服务器端发送消息
        socket.emit('demo', 100, 200, 'hello world')
    </script>
</body>
</html>

app.js

// 引入express
let express = require('express');
// 引入http
let http = require('http');
// 引入socket
let socket = require('socket.io');

// 创建服务器
let app = express();

// 静态化
app.use(express.static('./web/'))

app.get('/', (req, res) => {
    res.end('success')
})

// 通过http模块创建应用,https也是一样的
let server = http.createServer(app);

let io = socket(server);
// 监听浏览器端与服务器端及socket连接
io.on('connection', function(client) {
    // console.log(client);
    // 向连接的客户端发送数据
    client.emit('ickt', 100, 200, 'hello')
    // 监听消息
    client.on('demo', (...args) => {
        console.log(args);
    })
})

// 通过IO发送消息,可以让所有的浏览器端接收
setInterval(() => {
    io.emit('ickt', '111', 222)
}, 2000)

server.listen(3000)


 类似资料: