node入门学习

1.dependencies与devDependencies

npm i xxx默认会将包安装到生产依赖下,对应package.json就是dependencies对象下。

安装包到开发环境下:devDependencies

css 复制代码
1. npm i xxx --save-dev
2. npm i -D (简写)

2.常用命令

arduino 复制代码
npm config list 查看配置信息
npm uninstall xxx 卸载包
npm get registry 获取配置源信息
npm config set registry 设置源信息

3.发布npm包

  1. 账号密码

user:dongzeping pass:wqx666666...

  1. 注册账号

     npm adduser
    
  2. 登录账号

     npm login 
    
  3. 发布包(版本必须和上一次不一样)

     npm publish
    

4. 模块化

导入

ini 复制代码
const xx = require('./index')

导出

最终导出的结果看module.exports最后指向的内存单元。初始时exports和module.exports指向的是同一份内存单元

ini 复制代码
exports.a = 10
module.exports = 10

例子1

b.js

ini 复制代码
    module.exports = 10
    exports.a = 1

a.js

javascript 复制代码
    const a = require('./b.js')
    console.log(a) //10

例子2

b.js

ini 复制代码
   module.exports = {
      a:1
   }
   exports.a = 1

   module.exports = {
      b:1
   }

   exports.b = 1

a.js

css 复制代码
    const a = require('./b.js')
    console.log(a) //{b:1}
    
    

5. 全局变量

  1. Buffer
  2. __dirname:当前文件目录的绝对路径
  3. __filename:当前文件的绝对路径
  4. process

6.path模块

6.1:basename:文件名

arduino 复制代码
console.log(path.basename('/a/b/c.txt'))  //c.txt

6.2:dirname:文件所在目录

css 复制代码
console.log(path.basename('/a/b/c.txt'))  //a/b/c

6.3:extname:文件拓展名

arduino 复制代码
console.log(path.basename('/a/b/c.txt'))  //.txt

6.4:join:文件路径拼接

arduino 复制代码
console.log(path.join('/a','/b'))  ///a/b

6.5:resolve:文件路径拼接,返回绝对路径

arduino 复制代码
console.log(path.resolve(__dirname,'index.js'))  //c:/a/b/index.js

6.6:parse:文件字符串解析成对象

lua 复制代码
console.log(path.parse(__filename)) 
/*
{
  root: 'C:\\',
  dir: 'C:\\Users\\19394\\Desktop\\do\\not\\react测试',
  base: 'node1',
  ext: '',
  name: 'node1'
}
*/

6.7:format:对象解析成字符串

less 复制代码
console.log(path.format({
    {
      root: 'C:\\',
      dir: 'C:\\Users\\19394\\Desktop\\do\\not\\react测试',
      base: 'node1',
      ext: '',
      name: 'node1'
    }
}))  

//C:\\Users\\19394\\Desktop\\do\\not\\react测试\\node1

7. OS操作系统

1.os.cpus() cpu信息

2.os.networkInterfaces() 网络信息

3.os.platform() 系统平台

8.process进程

1. process.cwd() 当前目录

2. process.argv 输入的指令参数

  1. package.json

    json 复制代码
     "dev": "node index.js --dzp --name"
     
  2. 命令行输入 npm run dev

  3. index.js

    arduino 复制代码
     console.log( process.argv)//可以获取--dzp --name

3. process.env 系统所有配置信息

  1. 下载cross-env

    css 复制代码
     npm i cross-env
     
  2. package.json添加配置

    json 复制代码
     "scripts": {
         "dev": "cross-env NODE_ENV=dev node index.js --dzp"
      },
  3. index.js

    arduino 复制代码
     console.log(process.env.NODE_ENV)//dev

9.child_process 子进程

子进程模块可以实现多种功能,shell命令执行,脚本文件运行,进程通信等。

1. execSync(指令,[参数1,参数2])

execSync同步的执行脚本,适用于执行小脚本代码

参数1:指令字符串

参数2:数组,包含指令的额外参数

javascript 复制代码
try{
  let res1 = execSync('dir',['-A']);
  console.log(res1.toString())
}catch(e){
  console.log("error",e)
}

2. spawn(指令,[参数1,参数2])

spawn是异步的执行脚本,适用于执行一些需要长时间运行的命令。

javascript 复制代码
const ls = spawn('ls');

//成功拿到脚本执行结果
ls.stdout.on('data', (data) => {
  console.log(`stdout: ${data}`);
});

//失败回调
ls.stderr.on('data', (data) => {
  console.error(`stderr: ${data}`);
});

//关闭回调
ls.on('close', (code) => {
  console.log(`child process exited with code ${code}`);
});

3. fork

fork适用于进程之间的通信。

index.js

模拟父进程发送和接收儿子进程消息

javascript 复制代码
const child = fork("./child.js",[],{})
child.on('message',message=>{
  console.log('收到消息',message)
})
child.send({name:"dzp"})

child.js

模拟儿子进程发送和接收父亲进程消息

javascript 复制代码
process.on('message',message=>{
  console.log("儿子",message)
})

setTimeout(() => {
  process.send({age:25})
}, 3000);

10. util模块

util包含了许多内置工具函数

1. format

format类似于c语言的printf

lua 复制代码
console.log(format("%s--%d",'dzp',123))//dzp--123

11. fs 文件模块

0. 模块导入方式

常规

文件的操作都属于异步,因此参数都需要添加回调函数。

javascript 复制代码
const fs1 = require("fs")
fs1.readFile('child.js',(err,data)=>{
  if(err){
    console.log(err)
  }else {
    console.log(data.toString())
  }
})

promise格式

fs/promises模块的导入让文件的所有操作都支持promise回调。

javascript 复制代码
 const fs2 = require("fs/promises")
 
 fs2.readFile('child.js').then(data=>{
  console.log(data.toString())
}).catch(err=>{
  console.log(err)
})

1.读文件

readFile:读取小文件

javascript 复制代码
fs1.readFile('index.js',(err,data)=>{
  if(err){
    console.log(err)
  }else{
    console.log(data.toString())
  }
})

createReadStream:读取大文件

javascript 复制代码
const stream = fs1.createReadStream("./child.js");
stream.on('data',chunk=>{
  console.log(chunk.toString())
})
stream.on('end',e=>{
  console.log('close')
})

2.写文件

1. 小文件:writeFile

参数1: 文件路径

参数2:文件内容

参数3:文件配置对象,可以配置文件的权限、写入方式、编码等

参数4:回调函数

javascript 复制代码
//默认是覆盖写入
fs1.writeFile('./1.txt',"我是董泽萍",(e)=>{
  if(e) {
    console.log('error',e)
    return 
  }
  console.log('ok')
})


//追加写
fs1.writeFile('./1.txt',"我是董泽萍",{flag:'a'},(e)=>{
  if(e) {
    console.log('error',e)
    return 
  }
  console.log('ok')
})

2. 大文件:createWriteStream

和大文件读取一样,大文件写也是通过文件流的方式一段一段的写入

javascript 复制代码
 const dataList = [
  "111111\n",
  "222222\n",
  "333333\n",
]
const writeStream = fs1.createWriteStream('./1.txt',{
  flags:'a'//追加
})
dataList.forEach(item=>writeStream.write(item))
writeStream.on('close',e=>{
  if(!e){
    console.log('写入完成');
  } 
})
writeStream.end()//主动关闭

3. 追加写:appendFile

javascript 复制代码
fs1.appendFile('./1.txt',"呜呜呜呜",e=>{
  if(e){
    return 
  }
  console.log('ok')
})

3.删除文件

unlink

javascript 复制代码
fs1.unlink('./child.js',(err)=>{
  if(err) {
    console.log(err)
    return 
  }
  console.log('success')
})

4.重命名文件

rename

javascript 复制代码
fs1.rename('./index.html',"index2.html",e=>{
  if(e){
    console.log("error",e)
    return 
  }
  console.log('ok')
})

5. 创建文件夹

mkdir

javascript 复制代码
const fs = require('fs');

const folderPath = path.resolve(__dirname,"./../js/1/11/111");

fs.mkdir(folderPath, { recursive: true }, (err) => {
  if (err) {
    console.error('Error creating folder:', err);
    return;
  }
  console.log('Folder created successfully');
});

6. 删除文件夹

rmdir

javascript 复制代码
const folderPath = 'path/to/your/folder';

fs.rmdir(folderPath, { recursive: true }, (err) => {
  if (err) {
    console.error('Error deleting folder:', err);
    return;
  }
  console.log('Folder deleted successfully');
});

7. 重命名文件夹

rename

ini 复制代码
const paths = path.resolve(__dirname,"../js/1")
const newPath = path.resolve(__dirname,"../js/2")
fs1.rename(paths,newPath,e=>{
  if(e){
    return 
  }
  console.log('ok')
})

8. 软硬链接

硬链接

硬链接中两个文件指向同一块内存单元,二者相互关联,内容同步更新。

ini 复制代码
const existingFilePath = 'path/to/existing/file.txt';
const newLinkPath = 'path/to/new/link.txt';

fs.link(existingFilePath, newLinkPath, (err) => {
  if (err) {
    console.error('Error creating hard link:', err);
    return;
  }
  console.log('Hard link created successfully');
});

软链接

软链接类似电脑的快捷方式,删除一个不会影响彼此。

ini 复制代码
const fs = require('fs');

const targetPath = 'path/to/target/file.txt';
const symlinkPath = 'path/to/symlink.txt';

fs.symlink(targetPath, symlinkPath, 'file', (err) => {
  if (err) {
    console.error('Error creating symbolic link:', err);
    return;
  }
  console.log('Symbolic link created successfully');
});

12. markdown生成html案例

markdown文件转换成html标签

ini 复制代码
const marked = require('marked');
const html = marked.parse(md文件)

案例设计

md文件更新时,网页html实时改变。

思路设计

  1. 监听md文件的改变

  2. 如果md文件修改,读取md文件借助marked工具转换成html标签,然后借助ejs模版生成html文件。

  3. 借助fs文件流创建生成的html文件

  4. 借助browser-sync工具开启服务器监听html文件,如果改变自动刷新服务器(热更新)

    javascript 复制代码
     const fs1 = require("fs")
     const ejs = require('ejs')
     const marked = require('marked');
     const browserSync = require('browser-sync').create();
     const template = fs1.readFileSync('./template.ejs','utf-8')
     function renderHtml() {
       //2.读取md文件,转换成html标签
       const mdFile = fs1.readFileSync('./read.md','utf-8')
       const mdParse = marked.parse(mdFile.toString())
       //3.ejs转换html
       const htmls = ejs.render(template,{content:mdParse})
       //4.创建html文件
       fs1.writeFile("./index3.html",htmls,e=>{
         if(e){
           console.log('error',e)
           return 
         }
         console.log('生成html完成')
       })
     }
    
     //开启服务,监听指定html文件的改变
     browserSync.init({
       server: {
         baseDir: './', // 设置服务器的根目录
         index: 'index.html' // 设置默认打开的文件
       },
       files: ['./index.html'] // 监听的文件
     });
    
     /**
      * 1.监听md文件的改变
      * 2.改变后渲染htmlContent-->生成html文件-->服务刷新
      */
     fs1.watchFile('./read.md',(cuur,prev)=>{
       if(cuur.mtime!==prev.mtime) {
         renderHtml()
       }
     })

13. http模块

1. 创建http请求

javascript 复制代码
const http = require('http')
const server = http.createServer((req,res)=>{
  res.writeHead(200, {'Content-Type': 'text/plain;charset=utf-8'});
  res.end('request ok')
})
server.listen(8080,e=>{
  console.log('连接成功')
})

2.get与post请求

ini 复制代码
const http = require('http')
const server = http.createServer((req,res)=>{
  const method = req.method
  const parseUrl = url.parse(req.url);
  if(method==='GET') {
      //get
  } else if(method === 'POST') {
      //post
  }
})
server.listen(8080,e=>{
  console.log('链接成功')
})

3.设置响应头

参数1:状态码

参数2:响应头编码

css 复制代码
res.writeHead(200, {'Content-Type': 'text/plain;charset=utf-8'});

4.完整例子

javascript 复制代码
const http = require('http')
const url = require('url')
const querystring = require('querystring')
const server = http.createServer((req,res)=>{
  const method = req.method
  const parseUrl = url.parse(req.url,true);//解析请求参数
  const { pathname,query } = parseUrl
  console.log('get请求数据',query)
  if(method==='GET') {
    if(pathname==='/a/b') {
      res.writeHead(200, {'Content-Type': 'text/plain;charset=utf-8'});
      res.end('Hello, GET Request董泽!\n');
    }
  } else if(method === 'POST') {
    let body = ''
    req.on('data',chunk=>{
      body += chunk.toString()
    })
    req.on('end',()=>{
      const postData = querystring.parse(body)
      console.log('post请求数据',postData)
      res.writeHead(200, {'Content-Type': 'text/plain;charset=utf-8'});
      res.end('Hello, POST Request!\n');
    })
  }
})
server.listen(8080,e=>{
  console.log('链接成功')
})

14. 反向代理

原理

客户端访问/api请求,代理层获取请求,同时将请求发送到真正的服务端处理。 客户端不知道真实服务端地址。

用途

  1. 负载均衡,减轻服务端压力,提高整体分配和性能使用
  2. 性能优化,网页分为静态资源和动态资源,可以采用反向代理访问不同的目标地址,静态资源可以采取缓存策略提高性能等。

例子

如下示例表示访问3000服务端,而3000服务器反向代理将请求转发到了3001服务器上处理。

代理层3000代码

ini 复制代码
const http = require('http');
const httpProxy = require('http-proxy');

// 创建一个反向代理服务器实例
const proxy = httpProxy.createProxyServer({});

// 监听代理服务器的请求
const server = http.createServer((req, res) => {
  // 设置目标服务器的地址
  const target = 'http://localhost:3001'; // 目标服务器的地址

  // 执行代理转发
  proxy.web(req, res, { target });
});

// 监听端口
const PORT = 3000;
server.listen(PORT, () => {
  console.log(`Reverse proxy server running at http://localhost:${PORT}/`);
});

3001服务端代码

javascript 复制代码
const http = require("http")
const server = http.createServer((req,res)=>{
  res.writeHead(200,{'Content-Type': 'text/plain;charset=utf-8',})
  res.end('3001 响应成功')
})
server.listen(3001,()=>{
  console.log('3001 open')
})

15. 前端缓存

前端网站资源分为静态资源与动态资源,通常静态资源是长时间固定不变的,而动态资源是更新的。因此系统通常将静态资源和动态资源存储在不同的服务器上,同时对静态资源添加浏览器缓存从而提高性能。

动静资源分类技术

  1. CDN,静态资源使用CDN缓存,而动态资源请求服务器处理
  2. 反向代理,静态资源和动态资源代理到不同的服务器分别处理
  3. 静态存储服务器,专门使用服务器存储静态资源,例如阿里云,腾讯云这种。而动态资源走服务器。

静态与动态资源分离

php 复制代码
const http = require("http")
const fs = require('fs')
const mime = require('mime-types');
const path = require('path')
const urls = require('url')
const safeMime = ['text/plain','image/jpeg','audio/mpeg']
const server = http.createServer((req,res)=>{
  const { method,url } = req;
  const parseUrl = urls.parse(req.url,true);
  const {pathname} = parseUrl
  const staticUrl = path.join(__dirname,pathname)
  const mimeType = mime.lookup(pathname);
  /**处理静态资源 */
  if(method === 'GET' && url.startsWith('/static') && safeMime.includes(mimeType)) {
    try{
      let fileData = fs.readFileSync(staticUrl)
      res.writeHead(200,{
        'Content-Type': 'text/plain;charset=utf-8',
        "Cache-Control": "public, max-age=3600" 
      })
      res.end(fileData || 'dzp')
    }catch(e){
      res.writeHead(200,{
        'Content-Type': 'text/plain;charset=utf-8',
      })
      res.end('static resource error')
    }
    return 
  }
  /**处理动态资源 */
  if(method === 'GET' && url === '/api') {

  }
})
server.listen(8080,e=>{
  console.log('8080')
})

16. express

1.简单使用

javascript 复制代码
const express = require('express')
const app = express()
app.get('/',(req,res)=>{
  res.status(200).send('董泽萍')
})
app.listen(8080,()=>{
  console.log('8080')
})

2.处理get请求:req.query

javascript 复制代码
app.get('/',(req,res)=>{
  const query = req.query
  res.send('董泽萍')
})

3.处理post请求:req.body

javascript 复制代码
app.post('/',(req,res)=>{
  const query = req.body
  res.send('董泽萍')
})

4. json数据注意点

前端的请求头数据如果是json数据,express必须添加json中间件处理,否则拿不到数据

javascript 复制代码
const express = require('express')
const app = express()
app.use(express.json())//处理json数据
app.get('/',(req,res)=>{
  res.send('董泽萍')
})
app.listen(8080,()=>{
  console.log('8080')
})

5. 路由模块

express中路由按模块进行管理

index.js

javascript 复制代码
const express = require('express')
const users = require('./user/index')
const app = express()
app.use(express.json())//处理前端json数据
app.use('/use',users)//处理user路由模块

app.listen(8080,()=>{
  console.log('8080')
})

user/index.js

javascript 复制代码
const express = require('express')
const users = express.Router()
users.get('/',(req,res)=>{
  res.send('user')
})

module.exports = users;

6.中间件

express中可以自定义中间件,从而对请求头或者响应头数据进行进一步封装。

scss 复制代码
const express = require('express')
const users = require('./user/index')
const app = express()

function middleWare(req,res,next) {
    .....
    next()//继续执行
}
app.use(express.json())//处理前端json数据
app.use('/use',users)//处理user路由模块
app.use(middleWare)//自定义中间件逻辑
app.listen(8080,()=>{
  console.log('8080')
})

17. 防盗链

前端的静态资源如图片,音频,视频一般都需要添加防盗机制,从而避免其它网站窃取数据。其原理如下

前端资源请求都会在请求头携带referer字段,referer表示当前网页的地址(不是资源请求地址),因此后端根据referer判断是否是白名单网站。

scss 复制代码
const express = require('express')
const app = express()
app.use(express.json())//处理前端json数据
const whiteNetwork = ['http://127.0.0.1:8080']
function checkStatic(req,res,next) {
  const referer = req.get('referer')
  const host = referer && new URL(referer).host
  if(!referer || !whiteNetwork.includes(host)) {//无权限
    res.status(404).send('not 权限')
    return ;
  }
  next()
}
//添加防盗链中间件
app.use(checkStatic)
app.use('/statics',express.static('./static'))//设置静态资源目录
app.listen(8080,()=>{
  console.log('8080')
})

18. 跨域

通过设置响应头Access-Control-Allow-Origin字段来允许跨域

1.允许全部访问

arduino 复制代码
res.setHeader('Access-Control-Allow-Origin','*')

2.允许指定客户端访问

rust 复制代码
res.setHeader('Access-Control-Allow-Origin','http://127.0.0.1:8080')

案例

在白名单内的客户端允许跨域访问

javascript 复制代码
const express = require('express')
const app = express()
app.use(express.json())//处理前端json数据
const whiteNetwork = ['127.0.0.1:8080']

function corsCheck(req,res,next) {
  const origin = req.get('Origin')
  if(whiteNetwork.includes(origin)) {
    res.setHeader('Access-Control-Allow-Origin',origin)
  }
  next()
}
app.use(corsCheck)
app.get('/',(req,res)=>{
  const query = req.query//获取get数据
  res.send('董泽萍')
})
app.post('/api',(req,res)=>{
  const body = req.body//获取post数据
  res.send('董泽萍')
})
app.listen(8080,()=>{
  console.log('8080')
})

19. 请求头预检options

预检请求options在跨域请求并且满足如下任意条件就会触发

  1. 使用PUT、DELETE、CONNECT、OPTIONS、TRACE、PATCH非简单请求
  2. 自定义了请求头Content-Type为application/json,Authorization等

前端

前端使用了自定义请求头的Content-Type:application/json

less 复制代码
fetch('http://127.0.0.1:8080/?name=dzp',{
  headers:{
    'Content-Type': 'application/json'
  })
})

后端

后端通过options进行预检请求处理。

javascript 复制代码
const express = require('express')
const app = express()
app.use(express.json())//处理前端json数据
const whiteNetwork = ['http://127.0.0.1:5500']

function corsCheck(req,res,next) {
  const origin = req.get('Origin')
  console.log('origin',origin)
  if(whiteNetwork.includes(origin)) {
    res.setHeader('Access-Control-Allow-Origin',origin)
    res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
  }
  next()
}
app.options("*",(req,res,next)=>{
  let data = req?.query;
  if(data?.name === 'dzp') {//随机测试
    res.send('数据违规')
  }else{
    next()
  }

})
app.use(corsCheck)
app.get('/',(req,res)=>{
  const query = req.query//获取get数据
  console.log(query)
  res.send('董泽萍')
})
app.listen(8080,()=>{
  console.log('8080')
})

20. SSE(后端向前端单向通信)

sse技术是客户端和服务端建立连接后,服务端向客户端单向通信的技术。

  1. sse属于长连接机制,通常使用get请求

  2. 服务端请求头必须添加'Content-Type': 'text/event-stream'

  3. 发送数据必须以data开头,\n\n结尾,例如:

    lua 复制代码
     res.write('data: Hello\n\n');

前端

javascript 复制代码
let e = new EventSource('http://127.0.0.1:8080/sse')
e.onmessage = (data)=>{
  console.log(data)
}

服务端

javascript 复制代码
const express = require('express')
const app = express()
app.use(express.json())//处理前端json数据

app.get('/sse',(req,res)=>{
  res.writeHead(200,{
    'Content-Type': 'text/event-stream',
    'Access-Control-Allow-Origin':"*"
  })
  setInterval(()=>{
    res.write(`data: ${Date.now()}\n\n`)
  },2000)
})
app.listen(8080,()=>{
  console.log('8080')
})

21. mysql

1. 安装

  1. 官网www.mysql.com/ 下载

  2. 配置系统环境变量,mysql.exe的目录

  3. 打开cmd,输入指令

    css 复制代码
     mysql -uroot -p
     
  4. 显示welcome,表示成功。

2. vscode安装插件

  1. 插件搜索database,安装Database Client
  2. 点击左侧桶,输入数据库密码,建立连接。

3.mysql操作

1. 安装mysql工具库

css 复制代码
    npm i mysql2
    npm i js-yaml
    

2. yaml文件

yaml文件用来保存系统或者数据库配置信息,其数据格式如下。每个tab是两个空格

yaml 复制代码
database: 
  host: localhost 
  port: 3306 
  username: root 
  password: password 
  database: mydatabase

3.代码演示

javascript 复制代码
const express = require('express')
const app = express()
const fs = require('fs')
const jsYaml = require('js-yaml')
const mysql = require('mysql2/promise')
let yaml = fs.readFileSync('./db.config.yaml')
let config = jsYaml.load(yaml)
let connect;
async function connect_db() {
  try{
    connect = await mysql.createConnection({...config.database})
  }catch(e){
    throw new Error('数据库连接错误',e)
  }
}
connect_db()//连接数据库
app.get('/get/info',async(req,res)=>{
  const query = req.query;
  const {name} = query
  try{
    const [data] = await connect.query('select * from table1 where name = ',[name])
    res.send(data)
  }catch(e){
    res.send('query error')
  }
})
app.listen(8080,()=>{
  console.log('8080')
})

24.jwt

1.安装jsonwebtoken

css 复制代码
npm i jsonwebtoken

2. 首次登陆

login.html

php 复制代码
fetch('http://127.0.0.1:8080/login',
{
  headers:{'Content-type':'application/json'},
  method:'POST',
  body:JSON.stringify({name:'dzp',age:25})
})
.then(res=>res.text())
.then(data=>{
  console.log(data)
})

服务端

通过jwt.sign生成token字符串,expiresIn表示过期时间

javascript 复制代码
const express = require('express')
const app = express()
const cors = require('cors')
let jwt = require('jsonwebtoken');
const privateKey = 'adar11'
app.use(express.json())
app.use(cors());
app.post('/login',(req,res)=>{
  const info = req.body
  let token = jwt.sign(info, privateKey, { expiresIn:60*60 });
  console.log('初始token',token)
  res.send(token)
})
app.listen(8080,()=>{
  console.log('8080')
})

3. token验证

前端进行token验证时,必须在请求头添加Authorization字段。其值必须是Bearer+' '+token

前端

javascript 复制代码
const token = 'adada123213'
fetch('http://127.0.0.1:8080/info',
{
  headers:{'Authorization':`Bearer ${token}`},
  method:'GET',
}).then(res=>res.text())
.then(data=>{
  console.log(data)
})

服务端

自定义token验证中间件,添加到需要验证的请求上。

scss 复制代码
const jwtCheck = (req,res,next) => {
  const token = req.headers['authorization'];
  const newToken = token?.slice(7)
  console.log('前端传递的token',newToken)
  if (!newToken) {
    return res.status(401).json({ message: 'No token provided' });
  }
  jwt.verify(newToken,privateKey,(err,decode)=>{
    if(err) {
      return res.status(401).json({ message: err });
    }
    next()
  })
}
app.listen(8080,()=>{
  console.log('8080')
})

25.文件上传

前端

通常文件上传与其它表单信息共同传递到后端处理,因此上传的数据类型是formData类型。 如下将文件和其它数据一起传递到后端

javascript 复制代码
<form id="uploadForm" enctype="multipart/form-data">
    <input type="file" name="avatar" id="avatar">
    <button type="submit">Upload</button>
 </form>

const form = document.getElementById('uploadForm');
const messageDiv = document.getElementById('message');
form.addEventListener('submit', async function(event) {
  event.preventDefault();
  const formData = new FormData();
  formData.append('name','dzp')
  formData.append('avatar', document.getElementById('avatar').files[0]);
  try {
    const response = await fetch('http://127.0.0.1:8080/upload', {
      method: 'POST',
      body: formData,
    });
  } catch (error) {
    console.error('Error:', error);
  }
});

后端

  1. 后端通过multer处理formData的文件

  2. req.file读取保存后的文件

  3. req.body保存formData的非文件数据。

    javascript 复制代码
     const express = require('express')
     const app = express()
     const multer  = require('multer');
     // 设置 Multer 配置
     const storage = multer.diskStorage({
       destination: function (req, file, cb) {
         // 设置文件上传的目标目录
         cb(null, 'uploads/');
       },
       filename: function (req, file, cb) {
         // 设置文件名
         cb(null, Date.now()+file.originalname);
       }
     });
     const upload = multer({ storage: storage });
     //必须和前端的文件表单的name保持一致
     app.post('/upload',upload.single('avatar'),(req,res)=>{
       const data = req.body;//非文件数据
       const file = req.file;//文件
       res.send('ok')
     })

26.浏览器缓存

1.强缓存

Expires

javascript 复制代码
app.get('/info',(req,res)=>{
  const body = req.body
  const timers = new Date(Date.now()+15*1000)//15s
  res.setHeader('Expires',timers.toUTCString())
  res.send('login error')
})

前端首次请求访问服务器,响应头返回Expires字段。

第二次在有效缓存时间内访问,接口直接返回200状态码,直接从缓存中获取数据。如下图size栏显示cache

Cache-Control

Cache-Control属于强缓存的第二种策略,其优先级高于expires。 Cache-Control的取值字段如下:

  • max-age:浏览器资源缓存的时长(秒)。
  • no-cache:不走强缓存,走协商缓存
  • no-store:禁止任何缓存策略。
  • public:资源即可以被浏览器缓存也可以被代理服务器缓存(CDN)。
  • private:资源只能被客户端缓存。

前端首次请求,响应头返回字段Cache-Control。

前端第二次访问,如果在缓存有效期内,接口返回状态码200,直接取缓存资源。

2.协商缓存

Last-Modified与If-Modified-Since

scss 复制代码
function getFileModify(file) {
  return fs.statSync(file).mtime.toISOString()
}

app.get('/info',(req,res)=>{
  const body = req.body
  const timer1 = req.headers['if-modified-since']
  const timer2 = getFileModify('./index.js')
  res.setHeader('Cache-Control', 'no-cache, max-age=2592000')//走协商缓存
  res.setHeader('Last-Modified', timer2)
  if(timer1 && timer1 === timer2) {
    res.sendStatus(304).send()
  }else{
    res.sendStatus(200).send() 
  }
})

前端首次请求访问后端接口,响应头返回字段Last-Modified和Cache-control:no-cache表示走协商缓存了

第二次访问时,请求头自动携带If-Modified-Since字段,如果命中缓存则接口返回304状态码。

ETag与if-none-match

etag根据文件内容生成唯一hash值,文件内容改变hash值更新。etag的协商缓存优先级高于Last-Modified。

scss 复制代码
function getFileHash(file) {
  return crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex')
}
app.get('/info',(req,res)=>{
  const body = req.body
  res.setHeader('Cache-Control', 'no-cache, max-age=2592000')
  const etag = getFileHash('./index.js')
  const fileHash = req.headers['if-none-match']
  res.setHeader('ETag',etag)
  if(fileHash && etag === fileHash) {
    res.sendStatus(304).send()
  }else{
    res.sendStatus(200).send()
  }
})

前端首次访问,响应头返回字段Etag。

前端在后面访问时,请求头携带if-none-match字段去服务端进行验证,如果缓存命中,返回状态码304。

27. session登录

cookie

cookie信息在浏览器Application处查看

前端

前端模拟分为一个登录按钮和一个信息查看按钮。其中信息查看接口需要进行session权限验证

xml 复制代码
<body>
  <button class="login">登录</button>
  <button class="info">查看信息</button>
  <script>
    let btnLogin = document.querySelector('.login')
    let btnInfo = document.querySelector('.info')
    btnLogin.addEventListener('click',()=>{
      fetch('http://127.0.0.1:8080/login',{
        method:"POST",
        body:JSON.stringify({"name":"dzp"}),
        headers:{'Content-type':'application/json'}
      }).then(res=>res.text()).then(res=>{
        console.log(res)
      })
    })

    btnInfo.addEventListener('click',()=>{
      fetch('http://127.0.0.1:8080/info',{
        method:"GET",
      }).then(res=>res.text()).then(res=>{
        console.log(res)
      })
    })
  </script>
</body>

后端

  1. 首次登录成功后,将用户信息保存到session

  2. 将sessionID通过响应头设置到cookie,传递到前端

  3. 前端每次接口请求自动携带cookie到服务端,之后进行权限验证。

    php 复制代码
     const express = require('express')
     const app = express()
     const cors = require('cors')
     const session = require('express-session')
     const cookieParser = require('cookie-parser')
     app.use(express.json())
     app.use(cookieParser());
     app.use(cors());
     app.use(session({
       secret: 'secret', // 用于加密会话数据的密钥,可以是任意字符串
       resave: false,
       saveUninitialized: true
     }));
     app.use(express.static('./static'))
    
     app.post('/login',(req,res)=>{
       const body = req.body
       if(body.name==='dzp') {//登录成功 
         req.session.user = body//保存session
         console.log(req.session.id)
         res.cookie('session_id', req.session.id,{httpOnly:true});//响应头设置cookie
         res.send('login ok')
         return 
       } 
       res.send('login error')
     })
    
     app.get('/info',(req,res)=>{
       const sessionId = req.cookies.session_id;//获取前端传递的cookie
       if(sessionId && sessionId === req.session.id) {
         res.send('ok')
       }else{
         res.send('no 权限')
       }
     })
    
     app.listen(8080,()=>{
       console.log('8080')
     })
相关推荐
丁总学Java10 小时前
微信小程序-npm支持-如何使用npm包
前端·微信小程序·npm·node.js
看到请催我学习12 小时前
如何实现两个标签页之间的通信
javascript·css·typescript·node.js·html5
NiNg_1_23417 小时前
npm、yarn、pnpm之间的区别
前端·npm·node.js
余生H17 小时前
前端的全栈混合之路Meteor篇:关于前后端分离及与各框架的对比
前端·javascript·node.js·全栈
Ink18 小时前
从底层看 path.resolve 实现
前端·node.js
奔跑吧邓邓子20 小时前
npm包管理深度探索:从基础到进阶全面教程!
前端·npm·node.js
知否技术1 天前
为什么nodejs成为后端开发者的新宠?
前端·后端·node.js
谢尔登2 天前
【Node.js】worker_threads 多线程
node.js
osnet2 天前
showdoc二次开发
node.js·vue
泯泷2 天前
「生产必看」在企业环境中正确使用 Node.js 的九大原则
前端·后端·node.js