Node.js
Node.js
Node.js是一个基于chrome V8 引擎的JavaScript运行环境。
# 1.安装
Node.js (opens new window) LTS版本和Current版本
- LTS为长期版本,有稳定性
- Current为新特性尝鲜版
node -v # 安装后查看node版本
# 2.模块
# fs文件模块
const fs = require('fs')
- 读取文件中的内容
/**
* 参数1(必选) 文件的路径
* 参数2(可选) 编码格式 默认指定 utf-8
* 参数3(必选) 回调参数接受两个参数 (err,dataStr)
*/
fs.readFile(file[,options],callback)
2
3
4
5
6
- 向指定文件写入内容
/**
* 参数1(必选) 文件的路径
* 参数2(必选) 表示要写入的内容
* 参数3(可选) 编码格式 默认指定 utf-8
* 参数3(必选) 回调参数 err
*/
fs.writeFile(file,data[,options],callback)
2
3
4
5
6
7
# path路径模块
path模块是Node.js官方提供的,用来处理路径模块的处理
const path = require('path')
path.join([...paths]) // 将多个路径片段拼成一个完整的路径字符串
path.basename(path[,ext]) // 获取路径中的最后一部分
/**
* path 路径名(必选)
* ext 文件扩展名(可选)
*/
2
3
4
5
6
7
- html文件分离
const fs = require('fs')
const path = require('path')
const regStyle = /<style>[\s\S]*<\/style>/
const regScript = /<script>[\s\S]*<\/script>/
fs.readFile(path.join(__dirname,'/index.html'),'utf-8',(err,dataStr)=>{
if(err) return console.log('读取文件失败' + err.message);
resolveCSS(dataStr)
resolveJS(dataStr)
resolveHTML(dataStr)
})
function resolveCSS(htmlStr){
const r1 = regStyle.exec(htmlStr)
const newStr = r1[0].replace('<style>',' ').replace('</style>',' ')
fs.writeFile(path.join(__dirname,'/clock/index.css'),newStr,(err)=>{
if(err) return console.log('css写入失败' + err.message);
})
console.log('css写入成功!!!');
}
function resolveJS(htmlStr){
const r1 = regScript.exec(htmlStr)
const newStr = r1[0].replace('<script>',' ').replace('</script>',' ')
fs.writeFile(path.join(__dirname,'/clock/index.js'),newStr,(err)=>{
if(err) return console.log('js写入失败' + err.message);
})
console.log('js写入成功!!!');
}
function resolveHTML(htmlStr){
console.log(regStyle);
const newHtml = htmlStr.replace(regStyle,'<link rel="stylesheet" href="./index.css"></link>').replace(regScript,'<script src="./index.js"></script>')
console.log(newHtml);
fs.writeFile(path.join(__dirname,'/clock/index.html'),newHtml,(err)=>{
if(err) return console.log('html写入失败' + err.message);
})
console.log('html写入成功!!!');
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# http模块
// 导入http模块
const http = require('http')
const path = require('path')
const fs = require('fs')
// 创建web服务器示例
const server = http.createServer()
server.on('request',(req,res)=>{
/**
* req 请求对象
* res 响应对象
*/
const url = req.url
const fpath = path.join(__dirname + url)
fs.readFile(fpath,'utf-8',(err,dataStr)=>{
if(err) return res.end('404 not find')
// 向客户端发送指定内容,并结束这次请求
res.end(dataStr)
})
})
server.listen(8089,()=>{
console.log('server runing at http://127.0.0.1:8089');
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 模块化
模块化是指解决一个复杂问题时,自顶向下逐层把系统分成若干模块的过程。
- 内置模块(是由httNode.js提供的模块,如path、fs、http等)
- 自定义模块(用户自己写的模块)
- 第三方模块(第三方开发出来的模块)
# module对象
每一个自定义的js中都有一个module对象,里面存储了和当前模式有关的信息。
# module.exports对象
- 使用require()方法导入模块的时候,就是导入module.exports所指向的对象。
- 使用module.exports对象将莫模块内的成员共享出去,供外界使用。
module.exports = {
userName :'jack'
}
2
3
# export对象
默认情况下,exports和module.exports指向同一个对象。require()模块时,得到的永远是module.exports指向的对象。
exports.userName = 'jack'
# 模块的加载机制
# 内置模块
内置模块是由Node.js提供的,他的加载优先级最高。如果node_modules中有fs模块,require('fs')引用的还是Node.js中内置的fs模块。
# 自定义模块
加载自定义模块时,必须指定以
./
或../
开头的路径标识符
Node加载规则
- 按照确切的文件名进行加载
- 补全
.js
扩展名进行加载 - 补全
.json
扩展名进行加载 - 补全
.node
扩展名进行加载 - 加载失败,终端报错
# 第三方模块
从当前父目录开始,从
node_modules
文件中加载第三方模块,如果没有找到对应的第三方模块,则移动到上一层父目录中,进行加载,直到文件的根目录。
# 以目录作为模块
- 被加载的目录下查找
package.json
文件,寻找main属性,作为require()的入口。 - 如果目录中没有
package.json
文件或者main入口不存在或无法解析,则Node.js会加载目录下index.js文件。 - 若上面都查找失败,则终端报错
Error: Cannot find module './test'
# 3.npm
Node.js中第三方模块就是包
# 常用命令
npm -v # npm 版本
npm i 包名 -g # 安装全局依赖包
npm uninstall 包名 -g # 卸载全局依赖包
npm root -g # 查看全局安装npm包的路径
npm view 包名 versions # 查看该包所有的版本
npm i 包名@2.2.0 # 安装指定版本的包
npm install 包名 # 安装依赖
npm uninstall 包名 # 卸载依赖
npm i 包名 --save-dev # 安装依赖到开发环境 简写 npm i 包名 -D
2
3
4
5
6
7
8
9
# 包的语义化版本规范
包的版本号以"点分十进制"形式来定义的。版本号提升规则(前面的版本号增加了,后面的版本号归零)
- 第1位数字 大版本
- 第2位数字 功能版本
- 第3位数字 Bug修复版本
# 包管理配置文件
- 创建package.json
npm init -y
- 文件配置
{
"name": "init", // 名称
"version": "1.0.0", // 版本
"description": "", // 项目描述
"main": "index.js", // npm包项目的入口文件
"keywords": [], // 搜索的关键词
"scripts": { // 启动脚本
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "", // 作者
"license": "ISC", // 开源协议
"dependencies": {}, // 项目依赖(生产)
"devDependencies": {} // 项目依赖(开发)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# nrm管理npm镜像源
npm i nrm -g # 全局安装nrm
nrm ls # 查看所有的镜像源
nrm use taobao # 切换到淘宝镜像源
2
3
- 直接修改为淘宝镜像源(建议使用nrm)
# 查看当前的下包镜像源
npm config get registry
# 将npm镜像源切换到淘宝镜像源
npm config set registry https://registry.npm.taobao.org/
2
3
4
# 实用的npm包
- i5ting_toc
把md文档转换成html页面的小工具
# 构建npm包
- 包结构
- 包必须以单独的目录而存在
- 包的顶级目录必须包含
package.json
文件 package.json
中必须包含name(包名)、version(版本号)、main(入口文件)三个属性
- 包文件
- 使用npm init -y创建
package.json
文件 - 新建入口index.js,
package.json
中配置"main": "index.js"
- 新建
README.md
文件,用于包描述
- 发布包
- 注册npm账户,在终端执行
npm login
命令,输入用户名、密码、邮箱、邮箱验证吗
运行
npm login
之前,需要先把下载包的服务器切换为npm官方服务器,否则会导致发布包失败。
- 将终端切到根目录上,运行
npm publish
命令,即可将包发布到npm上。
- 删除包
npm unpublish 包名 --fore
删除npm上已发布的包
注意
npm unpublish
命令只能删除72小时以内发布的包npm unpublish
删除的包,在24小时内不允许重复发布
# 4.Express
高度包容、快速而极简的 Node.js Web 框架 Express (opens new window)
// 导入express
const express = require('express')
const app = express()
// get请求
/**
* req.params 匹配url的动态参数 /user/:id/:name 可以跟多个,但必须设置几个后面写几个
* req.query 匹配url的查询参数 /user?age=18&name=jack
* req.body post请求的主体参数
*/
app.get('/user/:id/:name', function(req, res) {
console.log(req.params);
res.send('hello world');
});
// post 请求
app.post('/submit', function(req, res) {
res.send('hello world');
});
app.listen(88,()=>{
console.log('http://127.0.0.1:88');
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 托管静态文件
app.use(express.static('public'));
app.use(express.static('files'));
// 访问位于 public 目录中的文件 (也可以托管多个静态资源)
http://localhost:3000/images/kitten.jpg
http://localhost:3000/css/style.css
http://localhost:3000/js/app.js
// 添加访问路径前缀
app.use('/public',express.static('../clock'))
2
3
4
5
6
7
8
9
# 路由
路由表示应用程序端点 (URI) 的定义以及端点响应客户机请求的方式。
- router.js
const express = require('express')
// 创建路由对象
const router = express.Router()
router.get('/user',(req,res)=>{
res.send('user')
})
module.exports = router;
2
3
4
5
6
7
- app.js
const express = require('express')
const router = require('./router')
const app = express()
// 挂载路由(添加前缀)
app.use('/api',router)
app.listen(88,()=>{
console.log('http://127.0.0.1:88');
})
2
3
4
5
6
7
8
# JSONP请求
浏览其通过
<script>
标签的src属性,请求服务器上的数据,服务器返回一个函数的调用
特点
- JSONP不是一个Ajax请求,因为他没有XMLHttpRequest这个对象。
- JSONP仅支持GET请求,不支持POST、DELETE、PUT请求。
- app.js
const express = require('express')
const cors = require('cors')
const app = express()
app.get('/api/jsonp',(req,res)=>{
// 获取客户端发送过来的回调函数名
const funName = req.query.callback
const data = {age:17,name:'jack'}
// 拼接函数调用字符串
const str = `${funName}(${JSON.stringify(data)})`
res.send(str)
})
// 如果已经配置cors了,jsonp请求必须在配置cors中间件之前声明
app.use(cors())
app.listen(8089,()=>{
console.log('server http://127.0.0.1:8089' );
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- 页面调用
$('#btnJSON').on('click',function(){
$.ajax({
type:'delete',
url:'http://127.0.0.1:8089/api/jsonp',
dataType:'jsonp',
success:(res)=>{
console.log(res);
}
})
})
2
3
4
5
6
7
8
9
10
# 中间件
中间件函数能够访问请求对象 (req)、响应对象 (res) 以及应用程序的请求/响应循环中的下一个中间件函数。下一个中间件函数通常由名为 next 的变量来表示。
注意事项
- 在路由之前注册中间件
- 客户端发送过来的请求,可以连续调用多个中间件进行处理
- 最后需要调用
next()
函数 - 调用多个中间件,多个中间件共享
req
和res
- 全局中间件(应用级别中间件)
通过
app.use()
或app.get()
或app.post()
绑定到app实例上的中间件
// 全局中间件
app.use((req, res, next)=>{
next();
})
const vm = function(req,res,next){
next()
}
app.get('/user',vm,(req,res)=>{})
// 调用多个中间件写法
app.get('/user',vm1,vm2,(req,res)=>{})
app.get('/user',[vm1,vm2],(req,res)=>{})
2
3
4
5
6
7
8
9
10
11
- 路由级别中间件
绑定到
express.Router()
实例上的中间件
const express = require('express')
const router = express.Router()
router.use(function(req,res,next){
next()
})
app.use('/api',router)
2
3
4
5
6
- 错误级别的中间件
捕获项目中发生的异常错误
警告
错误中间件必须注册在路由之后,否则不会生效
const express = require('express')
const app = express()
app.get('/user',(req,res)=>{
throw new Error('发生错误')
res.send('user')
})
app.use((err,req, res, next)=>{
// 捕获token失效
if(err.name = 'UnauthorizedError'){
return res.send('token失效')
}
res.send('错误' + err.message)
})
app.listen(88,()=>{
console.log('http://127.0.0.1:88');
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- 内置中间件
express.static()
托管静态资源中间件express.json()
4.16.0+ 解析JSON格式的请求体数据express.urlencoded()
4.16.0+ 解析URL-encdoded格式的请求体数据
const express = require('express')
const app = express()
// 如果不配置express.json() req.body则默认undefined
app.use(express.json())
// 传urlencoded参数的话,如果已配置express.json()则 req.body 为 {}
app.use(express.urlencoded({extended:false}))
2
3
4
5
6
- 第三方中间件
第三方开发出来的中间件如
body-parser
# 4.身份认证
身份认证是一种通过验证请求发送者身份的过程。它通常被用来确保只有授权用户可以访问受限资源或执行操作。
# Session认证
通过在服务器端存储用户的会话信息来记录和验证用户的身份。在 Session 认证中,用户在登录时提供用户名和密码,然后服务器会验证这些凭证,如果验证成功,就为该用户创建一个唯一的 Session ID,并将其存储到服务器端的内存或数据库中。该 Session ID 通常会在每个请求中被包含在一个称为
Cookie
的 HTTP 请求头中。
- Cookie是什么呢?
Cookie
是指浏览器端(客户端)存储在用户电脑上的小数据文件,通常是由网站服务器发送给浏览器,用于存储和检索用户的信息,如网站的登录状态、购物车信息等。下面是Cookie
的几大特征
- 自动发送
- 域名独立
- 过期时限
- 4KB大小限制
# 工作流程
- 在浏览器发送HTTP请求访问网站之前,网站服务器会在HTTP响应头中添加Set-Cookie字段,其中包含了要发送到浏览器中的Cookie内容。
- 浏览器收到响应头中的Set-Cookie字段后,将该Cookie保存到本地文件中(路径在浏览器的特定目录中),以备下次访问该网站时使用。
- 当浏览器下一次访问该网站时,将会在请求头的Cookie字段中带上保存的Cookie信息。
- 服务器收到Cookie后,会解析其中的内容并处理相关业务逻辑,然后将响应返回给浏览器。
# 缺点
Cookie是明文传输的,因此敏感信息(如用户名和密码)不应该保存在Cookie中。此外,如果一个网站的Cookie过多或者太大,将会占用用户过多的磁盘空间和内存,因此最好定期清除过期或不必要的Cookie
# 使用session
- 安装
npm i express-session
- 配置
const express = require('express')
const session = require('express-session')
const app = express()
app.use(session({
secret:'dong', // 任意字符串
resave:false, // 固定写法
saveUninitialized:true // 固定写法
}))
app.listen(8089,()=>{
console.log('server http://127.0.0.1:8089' );
})
2
3
4
5
6
7
8
9
10
11
说明
当配置成功后,可通过req.session
来访问和使用session对象。
- 注册中间件
const express = require('express')
const session = require('express-session')
const app = express()
app.use(session({
secret:'dongkj', // 任意字符串
resave:false, // 固定写法
saveUninitialized:true, // 固定写法
cookie: {
maxAge: 24 * 60 * 60 * 1000, // 24 hours
},
}))
app.listen(8089,()=>{
console.log('server http://127.0.0.1:8089' );
})
2
3
4
5
6
7
8
9
10
11
12
13
14
- 使用
// 存取数据
req.session.user = {};
// 清空session
req.session.destroy();
2
3
4
# JWT认证
JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在各方之间作为JSON对象安全地传输信息。JWT通常用于身份验证和授权,是一种无状态的认证机制。
# JWT的组成
Header
(头部):包含了 token 的元数据,例如加密算法等。Payload
(有效荷载):包含了对于身份验证和授权所需的具体信息,例如用户ID、角色等。Signature
(签名):将第一部分和第二部分组合在一起并加密得到的结果,用于校验 token 的完整性以及认证 token 是否有效。
# 工作流程
- 用户提供用户名和密码进行身份验证。
- 服务器验证成功后,生成一个 JSON Web Token 并将其返回给客户端。
- 客户端存储该 token,并在每次请求中将其添加到 Authorization Header 中进行访问。
- 服务器验证 token 的有效性并返回响应或者拒绝响应。
# 优点
使用 JWT 的好处是可以实现无状态和分布式的系统,由于 token 存储在客户端,服务器端可以无需存储用户的信息,从而降低了服务器端的存储压力。此外,由于 JWT 使用了加密技术,可以保证 token 的安全性,防止 token 被篡改或伪造
# 使用JWT
- 安装
# jsonwebtoken 用于生成JWT字符串
# express-jwt 用于将JWT字符串还原成json对象
npm i express-jwt jsonwebtoken
2
3
注意
一定要在路由之前配置解析Token中间件
配置成功后可以调用req.user
获取到用户信息,最新版本改为req.auth
- 使用
const express = require('express')
const session = require('express-session')
const cors = require('cors')
const app = express()
// 1.导入
const jwt = require('jsonwebtoken')
const expressJWT = require('express-jwt')
// 2.设置密钥
const secretKey = 'dong'
// 3.配置哪些接口不需要访问权限 案例中以/api开头的接口为不需要权限
app.use(expressJWT({secret:secretKey}).unless({path:[/^\/api\//]}))
// 解决跨域问题
app.use(cors())
app.use(express.urlencoded({extended:false}))
app.use(express.json())
// 登录
app.post('/api/login',(req,res)=>{
const {username,password} = req.body
if(username != '张三' || password != '12345'){
return res.send({
success:false,
message:'用户名或密码错误'
})
}
/**
* 4. 生成JWT字符串(3个参数)
* 参数1:用户的信息对象
* 参数2:加密的密钥
* 参数3:配置对象
*/
const token = jwt.sign({username:username},secretKey,{expiresIn:'55s'})
res.send({
success:true,
data:{
token
},
message:'登录成功'
})
})
app.post('/admin/getName',(req,res)=>{
// 5. req.user 可以获取到用户信息(token)## 最新版本改为req.auth
res.send({
success:true,
data:req.user
})
})
// 错误中间件用来捕获错误
app.use((err,req, res, next)=>{
// 捕获token失效的错误
if(err.name = 'UnauthorizedError'){
return res.send('token失效')
}
res.send('错误' + err.message)
})
app.listen(8089,()=>{
console.log('server http://127.0.0.1:8089' );
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
- 接口调用
在请求头(Headers)中添加
Authorization
:Bearer Token