Bootstrap

Node.js 学习笔记

文章目录


在这里插入图片描述
文章内容

在这里插入图片描述
在这里插入图片描述
小插件Template String Converter 当输入${}时,自动为其加上 `` 反引号在这里插入图片描述

一、node入门

  1. node.js是什么
    [图片]

  2. node的作用

  3. 开发服务器应用

  4. 开发工具类应用
    [图片]

  5. 开发桌面端应用

1.命令行工具

  1. 命令的结构
    [图片]

  2. 常用命令

切换到D盘——D:
查看D盘目录——dir
切换工作目录——cd machine
输出文件夹内的所有内容——dir /s

  • cd 是change direct的缩写

[图片]

2.使用node运行js文件

js文件
[图片]

命令行操作
在这里插入图片描述
在这里插入图片描述
直接在vscode的集成终端中操作
在这里插入图片描述

3.node注意事项

在这里插入图片描述
在这里插入图片描述
顶级对象global相当于浏览器环境的window在这里插入图片描述

4.Buffer 缓冲区

概念

  • 一个类似于Array的对象,用于表示固定长度的字节序列
  • 本质是一段内存空间,专门用来处理二进制数据。
    [图片]

特点

  • 大小固定,且无法调整(与数组不同,数组大小可以改变)
  • 性能较好,可以直接对计算机内存进行操作
  • 每个元素的大小为1字节
    在这里插入图片描述

使用

let buf=Buffer.alloc(2)
console.log(buf)

let buf_2 =Buffer.allocUnsafe(11112)
//使用这种方式不一定会把旧的数据清空
//速度更快,不用进行清零操作
console.log(buf_2)

let buf_3 =Buffer.from('hello')
//把字符串的每个字符都转换成unicode
console.log(buf_3)

//终端显示

D:\web>node test.js
<Buffer 00 00>
<Buffer 80 00 df 3e 08 02 00 00 f0 fc 10 35 08 02 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ff ff ... 11062 more bytes>
<Buffer 68 65 6c 6c 6f>

D:\web>node test.js
<Buffer 00 00>
<Buffer 80 00 3b f5 be 02 00 00 80 63 34 f5 be 02 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ff ff ... 11062 more bytes>
<Buffer 68 65 6c 6c 6f>

可以发现:使用allocUnsafe每次的都不样

在这里插入图片描述

4.1 与字符串的转换

let buf_4 =Buffer.from([105,108,111,118,101,121,111,117])
console.log(buf_4)
console.log(buf_4.toString())

//
D:\web>node test.js
<Buffer 69 6c 6f 76 65 79 6f 75>
iloveyou

4.2 Buffer 的读写

Buffer 可以直接通过 [] 的方式对数据进行处理。

//读取
console.log(buf_3[1]);
//修改
buf_3[1] = 97;
//查看字符串结果
console.log(buf_3.toString())

注意:

  1. 如果修改的数值超过 255 ,则超过 8 位数据会被舍弃
  2. 一个 utf-8 的字符 一般 占 3 个字节(如:中文)

5. 计算机的基本组成

在这里插入图片描述

6. 程序运行的基本流程

在这里插入图片描述
APP、数据——载入内存——CPU读取指令——显卡or 声卡——显示器or音箱。
在这里插入图片描述

7.进程、线程

  1. 进程打开任务管理器即可查看
    在这里插入图片描述
  2. 线程是进程的组成部分。在这里插入图片描述
    在这里插入图片描述

查看QQ的线程
找到进程ID在这里插入图片描述

二、fs模块

file system 文件系统
在这里插入图片描述

1. 写入文件

文件不存在会自动创建。
在这里插入图片描述

// 导入模块
const fs1 =require('fs');
// 写入文件
fs1.writeFile('./f_write.txt','abcdefg',err=>{
    if(err){
        console.log('写入失败')
        return;
    }
})

2.同步与异步

2.1 异步写入

// 导入模块
const fs1 =require('fs');
// 写入文件
fs1.writeFile('./f_write.txt','abcdefg',err=>{
    if(err){
        console.log('写入失败')
        return;
    }
    console.log('写入成功')
})
console.log('1')

上述代码有两个线程

  1. JS主线程——执行解析JS代码。

  2. 磁盘写入——输入/输出( I / O)线程

writeFile方法是异步的。

当上述I/O线程完成后,将回调函数(err)压入队列中,等主线程执行完后续代码后,再取出这个函数,执行。

效果展示

在这里插入图片描述

2.2 同步写入

// 导入模块
const fs1 =require('fs');
// 写入文件
fs1.writeFileSync('./test1','test1');

写入效果:
在这里插入图片描述

3.文件的追加写入

3.1 appendFile

// 导入模块
const fs2 =require('fs');
// 写入文件
fs2.appendFile('./append.txt','append!!',err=>{
    //写入成功则返回错误对象,失败则返回none
    if(err){
        console.log('追加写入失败')
        return
    }
    console.log('追加写入成功')
})

在这里插入图片描述
在这里插入图片描述

3.2 appendFileSync

// 导入模块
const fs2 =require('fs');
// 写入文件
fs2.appendFileSync('./append.txt','\r\nappend!!')//进行换行操作

在这里插入图片描述

3.3 writeFile…{flag:‘a’}

在这里插入图片描述

// 导入模块
const fs1 =require('fs');
// 写入文件(第一遍直接写入的
// fs1.writeFile('./f_write.txt','ABC',err=>{
    //追加写法——添加配置项
fs1.writeFile('./f_write.txt','hhhh',{flag:'a'},err=>{
    if(err){
        console.log('写入失败')
        return;
    }
    console.log('写入成功')
})
console.log('1')

在这里插入图片描述

4.文件流式写入

// 导入模块
const fs1 =require('fs');
//创建写入对象
const ws=fs1.createWriteStream('fff.txt');
//write
ws.write('乘醉听萧鼓\r\n')
ws.write('吟赏烟霞\r\n')
ws.write('异日图将好景\r\n')
ws.write('归去凤池夸\r\n')
//关闭通道
ws.close()

在这里插入图片描述

5.文件读取

5.1 异步读取

// // 导入模块
const fs1 =require('fs');
// //创建写入对象
// const ws=fs1.createWriteStream('fff.txt');
// //write
// ws.write('乘醉听萧鼓\r\n')
// ws.write('吟赏烟霞\r\n')
// ws.write('异日图将好景\r\n')
// ws.write('归去凤池夸\r\n')
// //关闭通道
// ws.close()

//读取上面写入的文档,并进行输出
fs1.readFile('./fff.txt',(err,data)=>{
    if(err){
        console.log('读取失败')
        return
    }
    console.log(data)
    console.log(data.toString())
})

在这里插入图片描述

5.2 同步读取

// // 导入模块
const fs1 =require('fs');
//读取
let data=fs1.readFileSync('./fff.txt')
console.log(data)
console.log(data.toString())

在这里插入图片描述

5.3 流式读取

// // 导入模块
const fs1 =require('fs');
//流式读取
let rs=fs1.createReadStream('./fff.txt')
rs.on('data',data=>{
    console.log(data)
})
//读取完毕后,执行end回调
rs.on('end',()=>{
    console.log('读取完成')
})

7.文件重命名、移动

7.1 异步

// // 导入模块
const fs1 =require('fs');
//重命名
fs1.rename('./fff.txt','./poem.txt',err=>{
    if(err) throw err
    console.log('移动完成')
})

也可以用它来修改路径

在这里插入图片描述

7.2 同步

// // 导入模块
const fs1 =require('fs');
//重命名
fs1.renameSync('./poem.txt','./poemAA.txt')

8.删除

以下两个方法均有同步方法

8.1 unlink方法

// // 导入模块
const fs1 =require('fs');
//删除
fs1.unlink('./poemAA.txt',err=>{
    if(err){
        console.log('删除失败')
        return
    }
    console.log('删除成功')
})

8.2 rm方法

// // 导入模块
const fs1 =require('fs');
//删除
fs1.rm('./f_write.txt',err=>{
    if(err){
        console.log('删除失败')
        return
    }
    console.log('删除成功')
})

在这里插入图片描述

9.文件夹操作

9.1 文件夹创建mkdir

make directory

// // 导入模块
const fs1 =require('fs');
//删除
fs1.mkdir('./html',err=>{
    if(err){
        console.log('创建失败');
        return
    }
    console.log('创建成功')
})
递归创建
// // 导入模块
const fs1 =require('fs');
//删除
fs1.mkdir('./a/b/c',err=>{
    if(err){
        console.log('创建失败');
        return
    }
    console.log('创建成功')
})
//D:\web>node test.js
//创建失败    

需要添加配置项

// // 导入模块
const fs1 =require('fs');
//删除
fs1.mkdir('./a/b/c',{recursive:true},err=>{
    if(err){
        console.log('创建失败');
        return
    }
    console.log('创建成功')
})

在这里插入图片描述

9.2 文件夹读取readdir

// // 导入模块
const fs1 =require('fs');
//删除
fs1.readdir('./vue3',(err,data)=>{
    if(err){
        console.log('创建失败');
        return
    }
    console.log('创建成功',data)
})

在这里插入图片描述

9.3 删除文件夹rmdir

rm——remove

系统建议使用rm来代替rmdir

(node:968) [DEP0147] DeprecationWarning: In future versions of Node.js, fs.rmdir(path, { recursive: true }) will be removed. Use fs.rm(path, { recursive: true }) instead

// // 导入模块
const fs1 =require('fs');
//删除
fs1.rmdir('./html',err=>{
    if(err){
        console.log('删除失败')
        return
    }
    console.log('删除成功')
})

若删除之后再次运行,则显示删除失败

在这里插入图片描述

递归删除
// // 导入模块
const fs1 =require('fs');
//想删除a文件夹下的所以目录
fs1.rmdir('./a',err=>{
    if(err){
        console.log('删除失败',err)
        return
    }
    console.log('删除成功')
})

在这里插入图片描述

// 导入模块
const fs1 =require('fs');
//想删除a文件夹下的所以目录
fs1.rmdir('./a',{recursive:true},err=>{
    if(err){
        console.log('删除失败')
        return
    }
    console.log('删除成功')
})

10.查看资源状态

// 导入模块
const fs1 =require('fs');
//stat方法 status缩写
fs1.stat('./2023_1/demo/动态表格视频.mp4',(err,data)=>{
    if(err){
        console.log('操作失败')
        return
    }
    console.log('操作成功',data)
})

在这里插入图片描述

  • size 文件体积
  • birthtime 创建时间
  • mtime 最后修改时间
  • atime 最后访问时间
  • ctime 最后修改文件状态时间
  • isFile 检测是否为文件
  • isDirectory 检测是否为文件夹

11.路径补充说明

在这里插入图片描述

// // 导入模块
const fs1 =require('fs');
//使用绝对路径删除
fs1.rm('D:/web/test1',err=>{
    if(err){
        console.log('删除失败')
        return
    }
    console.log('删除成功')
})

D:/web/test1 最初的形式是D:\web\test1反斜杠,要改成 / 才可以进行操作

12.相对路径的bug解决

使用相对路径时的参照目录是命令行所在的目录,而不是当前文件的所在的所在目录。

__dirname 保存着 当前文件所在目录的绝对路径

13、path模块

在这里插入图片描述

1. path.resolve

拼接规范的逻辑路径
console.log(__dirname+'/index.html')如下图所示 斜杠不一致
在这里插入图片描述

console.log(__dirname+'/index.html')

2.其他

  1. console.log(path1.sep)
    在这里插入图片描述
  2. 文件的绝对路径console.log(__filename)
    在这里插入图片描述
  3. 解析文件
let ts='D:\\web\\node\\test.js'
console.log(path1.parse(ts))

在这里插入图片描述

  1. 获取文件名
let ts='D:\\web\\node\\test.js'
console.log(path1.basename(ts))

在这里插入图片描述

  1. 获取路径的目录名、扩展名
let ts='D:\\web\\node\\test.js'
console.log(path1.dirname(ts))
console.log(path1.extname(ts))```

在这里插入图片描述

14.URL补充

网页中的 URL 主要分为两大类:相对路径与绝对路径

绝对路径可靠性强,而且相对容易理解,在项目中运用较多

在这里插入图片描述
相对路径
如果已经在最外层,再加…/还是不变,依旧是最外层
在这里插入图片描述
使用场景
在这里插入图片描述

三、http协议

hypertext Transfer Protocol
超文本传输协议
在这里插入图片描述
请求报文,响应报文

1.http报文

在这里插入图片描述

encode 编码 decode解码

1.1 请求报文结构

在这里插入图片描述

(1) 请求行

在这里插入图片描述

  • 请求方法
    在这里插入图片描述

  • url
    在这里插入图片描述
    用于定位服务器中资源
    在这里插入图片描述

端口号有些情况下可以省略

以百度为例在这里插入图片描述> 主机名=域名?在这里插入图片描述
http版本号
在这里插入图片描述
在这里插入图片描述

(2)请求头

以键值对形式存在。
https提高交互的安全性。
在这里插入图片描述

(3)请求体

可以随意设置,与后端商量好在这里插入图片描述
json形式的请求体 在这里插入图片描述

1.2 响应报文

在这里插入图片描述

(1)响应行

在这里插入图片描述

响应状态码
在这里插入图片描述
在这里插入图片描述

响应状态描述
在这里插入图片描述

(2)响应头

记录了与服务器相关的东西
在这里插入图片描述

(3)响应体

在这里插入图片描述

2. 网络基础概念

2.1 IP

就像发件写 收件地址
IP——寻找网络设备
在这里插入图片描述
将32位二进制转为10进制在这里插入图片描述
作用
在这里插入图片描述
只要设备接入互联网,它都有自己的ip地址。

2.2 IP分类

在这里插入图片描述

(1)局域网IP

解决IP不够用的方法:共享IP在这里插入图片描述
在这里插入图片描述

(2)广域网IP

连接互联网在这里插入图片描述

局域网ip可以复用

(2)本地回环IP地址

在这里插入图片描述

2.1 端口

在这里插入图片描述
作用在这里插入图片描述
在这里插入图片描述


主战区转换为后端:服务器

四、http 模块

1. 创建HTTP服务端

// 导入模块
const http=require('http');

// 创建服务对象
// request,response是两个形参,分别是对请求,响应报文的封装
// 当服务接收到请求时,回调函数执行
const server=http.createServer((request,response)=>{
    // 设置响应体并结束响应体
    response.end('hello HTTP Server')
});

// 监听端口,启动服务器
// 当服务启动成功以后执行此回调函数
m /

在这里插入图片描述
浏览器向服务器发送请求
在这里插入图片描述

注意事项在这里插入图片描述
当响应体中有中文response.end('你好 HTTP')时,该怎么解决。在这里插入图片描述

// 导入模块
const http=require('http');

// 创建服务对象
// request,response是两个形参,分别是对请求,响应报文的封装
// 当服务接收到请求时,回调函数执行
const server=http.createServer((request,response)=>{
    // 设置响应体并结束响应体
    response.setHeader('content-type','text/html;charset=utf-8')
    response.end('你好 HTTP')
});

// 监听端口,启动服务器
// 当服务启动成功以后执行此回调函数
server.listen(9000,()=>{
    console.log('服务已经启动...')
});

在这里插入图片描述
若有端口被占用,则进入任务管理器或者资源监视器,结束任务。
在这里插入图片描述

2. 在浏览器中查看报文

请求头
在这里插入图片描述
在这里插入图片描述
提交表单后

<!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="http://127.0.0.1:9000" method="post">
        <input type="text" name="username">
        <input type="password" name="password">
        <input type="submit" name="">
    </form>
</body>
</html>

在这里插入图片描述
在这里插入图片描述

3. 提取请求报文的内容

(1)获取请求头

const http=require('http');

// 当服务接收到请求时,回调函数执行
const server=http.createServer((request,response)=>{
    // 获取请求的方法
    console.log(request.method);
    // 获取请求的url(只包含路径与查询字符串)
    console.log(request.url)
    // 获取请求头
    console.log(request.headers)
    // 设置响应体并结束响应体
    response.setHeader('content-type','text/html;charset=utf-8')
    response.end('你好 HTTP')
});

// 监听端口,启动服务器
// 当服务启动成功以后执行此回调函数
server.listen(9000,()=>{
    console.log('服务已经启动...')
});

在这里插入图片描述

(2) 获取请求体

提取路径和查询字符串

const http=require('http');

const server=http.createServer((request,response)=>{
    let url=new URL(request.url,'http://127.0.0.1')
    console.log(url)
    response.setHeader('content-type','text/html;charset=utf-8')
    response.end('请求成功')
});

server.listen(9000,()=>{
    console.log('服务已经启动...')
});


//请求体的内容
URL {
  href: 'http://127.0.0.1/search?key=123',
  origin: 'http://127.0.0.1',
  protocol: 'http:',
  username: '',
  password: '',
  host: '127.0.0.1',
  hostname: '127.0.0.1',
  port: '',
  pathname: '/search',
  search: '?key=123',
  searchParams: URLSearchParams { 'key' => '123' },
  hash: ''
}
URL {
  href: 'http://127.0.0.1/favicon.ico',
  origin: 'http://127.0.0.1',
  protocol: 'http:',
  username: '',
  password: '',
  host: '127.0.0.1',
  hostname: '127.0.0.1',
  port: '',
  pathname: '/favicon.ico',
  search: '',
  searchParams: URLSearchParams {},
  hash: ''
}

在这里插入图片描述

查询路径和字符串

    let url=new URL(request.url,'http://127.0.0.1')
    console.log('查询路径:',url.pathname)
    console.log('\n查询字符串:',url.searchParams)

在这里插入图片描述
取出字符串console.log(url.searchParams.get('key'))
在这里插入图片描述

(3)http请求练习

在这里插入图片描述

const http=require('http');

// 创建服务
const server=http.createServer((request,response)=>{
    //获取请求的方法
    // let method =request.method
    // 简写:解构赋值
    let method =request.method
    //获取请求的路径
    let pathname =new URL(request.url,'http://127.0.0.1').pathname
    //设置响应头
    response.setHeader('content-type','text/html;charset=utf-8')
    
    //判断
    if(method === 'GET' && pathname === '/login'){
        response.end('登录页面')
    }else if(method === 'GET' && pathname === '/reg'){
        response.end('注册页面')
    }else{
        response.end('404')
    }
    //每次请求只能对应一次end方法
});

server.listen(9000,()=>{
    console.log('服务已经启动...\n端口9000监听中...')
});

pathname 是一个 URL 对象的属性,它并不是一个字符串。为了正确判断请求的路径,需要使用pathname.pathname 来获取路径字符串,然后进行比较。
注意路径别写成./login
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.设置响应报文

const http=require('http')
const server=http.createServer((request,response)=>{
    // 设置响应状态码
    response.statusCode=201;
    // 响应状态的描述,一般不用
    response.statusMessage='message'
    // 设置响应头,用来表示服务端的名字
    response.setHeader('ch','text/html;charset=utf-8')
    response.setHeader('Flavia','FFFFF')
    // 响应体,write方法与end方法相比,可多次调用
    response.write('love')
    response.write('\npatience')
    //每个请求必须有end方法,且只能有一个
    response.end()
})
server.listen(9000,()=>{
    console.log('服务已经启动...')
})

在这里插入图片描述

(1)http响应练习

搭建 HTTP 服务,响应一个 4 行 3 列的表格,并且要求表格有 隔行换色效果 ,且 点击 单元格能 高亮显示


const http=require('http')
const server=http.createServer((request,response)=>{

    response.end(`
    <!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        td{
            padding:20px 40px;
        }
        table,td{
            border-collapse:collapse;
        }
    </style>
</head>
<body>
   <table border=1>
    <tr><td></td><td></td><td></td></tr>
    <tr><td></td><td></td><td></td></tr>
    <tr><td></td><td></td><td></td></tr>
    <tr><td></td><td></td><td></td></tr>
   </table>
</body>
</html>
    `)
})
server.listen(9000,()=>{
    console.log('服务已经启动...')
})

在这里插入图片描述


const http=require('http')
const server=http.createServer((request,response)=>{

    response.end(`
    <!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        td{
            padding:10px 35px;
        }
        table,td{
            border-collapse:collapse;
        }
        table tr:nth-child(odd){
            background: #eee;
        }
        table tr:nth-child(even){
            background: #fff;
        }
    </style>
</head>
<body>
   <table border=1>
    <tr><td></td><td></td><td></td></tr>
    <tr><td></td><td></td><td></td></tr>
    <tr><td></td><td></td><td></td></tr>
    <tr><td></td><td></td><td></td></tr>
   </table>
   <script>
    //获取所有td
    let tds=document.querySelectorAll('td')
    //遍历
    tds.forEach(item=>{
        item.οnmοuseοver=function(){
            this.style.background='#ccc'
        }
        item.οnmοuseοut=function(){
            this.style.background='#fff'
        }
    })
   </script>
</body>
</html>
    `)
})
server.listen(9000,()=>{
    console.log('服务已经启动...')
})

在这里插入图片描述

(2)优化

把html内容单独放一个文件


const http=require('http')
const fs=require('fs')
const server=http.createServer((request,response)=>{
    let html=fs.readFileSync(__dirname+'/index.html')
    response.end(html)
})
server.listen(9000,()=>{
    console.log('服务已经启动...')
})
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        td{
            padding:10px 35px;
        }
        table,td{
            border-collapse:collapse;
        }
        table tr:nth-child(odd){
            background: #eee;
        }
        table tr:nth-child(even){
            background: #fff;
        }
    </style>
</head>
<body>
   <table border=1>
    <tr><td></td><td></td><td></td></tr>
    <tr><td></td><td></td><td></td></tr>
    <tr><td></td><td></td><td></td></tr>
    <tr><td></td><td></td><td></td></tr>
   </table>
   <script>
    //获取所有td
    let tds=document.querySelectorAll('td')
    //遍历
    tds.forEach(item=>{
        item.onmouseover=function(){
            this.style.background='#ccc'
        }
        item.onmouseout=function(){
            this.style.background='#fff'
        }
    })
   </script>
</body>
</html>

(3)实现引入外部文件

想要独立css,js文件
根据请求路径区分请求结果,而不是每个文件都是html形式。尚硅谷第56集

在这里插入图片描述

5. 网页资源加载的基本过程

这两个请求关系不大
在这里插入图片描述
很多请求都是并行的。
在这里插入图片描述
执行着,遇到了,就去请求

6.静态资源,动态资源

静态资源:内容长时间不发生改变的资源
动态资源:内容经常更新的资源
在这里插入图片描述

7.搭建静态资源服务

需求
在这里插入图片描述


const http=require('http')
const fs=require('fs')
const server=http.createServer((request,response)=>{
    // 请求url路径
    let {pathname}=new URL(request.url,'http://127.0.0.1');
    // 拼接文件路径
    let filePath=__dirname+pathname;
    fs.readFile(filePath,(err,data)=>{
        if(err){
            response.statusCode=500;
            response.end('文件读取失败');
            return;
        }
        response.setHeader('content-type','text/html;charset=utf-8')
        response.end(data)
    })
})
server.listen(9000,()=>{
    console.log('服务已经启动...')
});

(1)静态资源目录(网站根目录)

HTTP 服务在哪个文件夹中寻找静态资源,那个文件夹就是 静态资源目录 ,也称之为 网站根目录

8.设置mime类型

媒体类型(通常称为 Multipurpose Internet Mail Extensions 或 MIME 类型 )是一种标准,用来表示文档、文件或字节流的性质和格式。

mime 类型结构: [type]/[subType]
例如: text/html text/css image/jpeg image/png application/json

HTTP 服务可以设置响应头 Content-Type 来表明响应体的 MIME 类型,浏览器会根据该类型决定如何处理资源.

//是常见文件对应的 mime 类型
html: 'text/html',
css: 'text/css',
js: 'text/javascript',
png: 'image/png',
jpg: 'image/jpeg',
gif: 'image/gif',
mp4: 'video/mp4',
mp3: 'audio/mpeg',
json: 'application/json'

对于未知的资源类型,可以选择 application/octet-stream 类型,浏览器在遇到该类型的响应时,会对响应体内容进行独立存储,也就是我们常见的 下载 效果

9.解决乱码问题

响应头的优先级更高
在这里插入图片描述
如果是html格式,则加上utf-8

10.完善错误处理

在这里插入图片描述
在这里插入图片描述

11.GET和POST请求场景

在这里插入图片描述

GET和POST请求的区别

  • GET 和 POST 是 HTTP 协议请求的两种方式。
  • GET 主要用来获取数据,POST 主要用来提交数据(上面两点两者都可以)
  • GET 带参数请求是将参数缀到 URL 之后,在地址栏中输入 url 访问网站就是 GET 请求,
  • POST 带参数请求是将参数放到请求体中

五、模块化

1.介绍

  • 将一个复杂的程序文件依据一定规则(规范)拆分成多个文件的过程称之为 模块化。
  • 其中拆分出的 每个文件就是一个模块 ,模块的内部数据是私有的,不过模块可以暴露内部数据以便其他模块使用。

好处:
  1. 防止命名冲突
  2. 高复用性
  3. 高维护性

2.暴露数据

D:\web\node\fn.js

function tie(){
    console.log('贴膜...')
}
//暴露数据
module.exports=tie
D:\web\node\index.js

// 导入模块
const tie =require('./fn.js')
//调用函数
tie()

在这里插入图片描述

3.暴露方式

注意:

  • module.exports 可以暴露 任意 数据
  • 不能使用 exports = value 的形式暴露数据,模块内部 module 与 exports 的隐式关系
    exports = module.exports = {} ,require 返回的是目标模块中 module.exports 的值

(1)module.exports = value

在这里插入图片描述

在这里插入图片描述

(2)exports.name = value

在这里插入图片描述

此处的代码改变则不生效

exports.tie=tie
exports.hi=hi

在这里插入图片描述


![在这里插入图片描述](https://img-blog.csdnimg.cn/50a1a2345eaf4fbfa7d47d227a7254aa.png)
console.log(module.exports===exports)

在这里插入图片描述

4.导入模块

使用require函数

  1. 对于自己创建的模块,导入时路径建议写 相对路径 ,且不能省略 ./../
  2. js 和 json 文件导入时可以不用写后缀,如果js和json名字一样,则先检测js,c/c++编写的 node 扩展文件也可以不写后缀,但是一般用不到
  3. 如果导入其他类型的文件,会以 js 文件进行处理
  4. 如果导入的路径是个文件夹,则会 首先 检测该文件夹下 package.json 文件中 main 属性对应
    的文件,如果存在则导入,反之如果文件不存在会报错。
    如果 main 属性不存在,或者 package.json 不存在,则会尝试导入文件夹下的 index.js 和index.json ,
    如果还是没找到,就会报错
  5. 导入 node.js 内置模块时,直接 require 模块的名字即可,无需加 ./ 和 …/

在这里插入图片描述
没有反应??

5.导入自定义模块的基本流程

  1. 将相对路径转为绝对路径,定位目标文件
  2. 缓存检测
  3. 读取目标文件代码
  4. 包裹为一个函数并执行(自执行函数)。通过 arguments.callee.toString() 查看自执行函数
  5. 缓存模块的值
  6. 返回 module.exports 的值
    在这里插入图片描述
    在这里插入图片描述
// 伪代码
function require(){
    // 转换为绝对路径
    let absolutePath=path.resolve(__dirname,file);
    // 缓存检测
    if(caches[absolutePath]){
        return caches[absolutePath]
    }
    //读取文件内容
    let code=fs.readFileSync(absolutePath).toString()
    
    // 包裹成函数,执行
    let module={}
    let exports=module.exports={}
    function (exports, require, module, __filename, __dirname) {
        const test={
            name:'尚硅谷'
        }
        module.exports=test;
        // 输出指向函数的代码体
        console.log(arguments.callee.toString())
        }(exports, require, module, __filename, __dirname)
        //缓存
        caches[absolutePath]=module.exports
    }
const m=require('./fn.js')

因为缓存,只执行一次

在这里插入图片描述

module.exports 、 exports 以及 require 这些都是 CommonJS 模块化规范中的内容。
而 Node.js 是实现了 CommonJS 模块化规范,二者关系有点像 JavaScript 与 ECMAScript

六、包管理工具

  • 『包』英文单词是 package ,代表了一组特定功能的源码集合
  • 包管理工具——管理『包』的应用软件,可以对「包」进行 下载安装 , 更新 , 删除 , 上传 等操作
  • 借助包管理工具,可以快速开发项目,提升开发效率
  • 包管理工具是一个通用的概念,很多编程语言都有包管理工具,所以 掌握好包管理工具非常重要

常用的包管理工具 npm yarn cnpm

1.npm

npm 全称 Node Package Manager ,翻译为中文意思是『Node 的包管理工具』
npm 是 node.js 官方内置的包管理工具,是 必须要掌握住的工具.

2. 初始化

创建一个空目录,然后以此目录作为工作目录 启动命令行工具 ,执行 npm init

终端显示如下

D:\web\node\files1>npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.      

See `npm help init` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
package name: (files1) npm1
version: (1.0.0)                                                                 
description:                                                                     
entry point: (index.js)                                                          
test command:                                                                    
git repository:                                                                  
keywords:                                                                        
author: F                                                                        
license: (ISC)                                                                   
About to write to D:\web\node\files1\package.json:

{
  "name": "npm1",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "F",
  "license": "ISC"
}

确认最后一步之后,自动创建一个json文件。

npm init 命令的作用是将文件夹初始化为一个『包』, 交互式创建 package.json 文件 package.json
是包的配置文件,每个包都必须要有 package.json

在这里插入图片描述
初始化过程中的注意事项:

  1. package name ( 包名 ) 不能使用中文、大写,默认值是 文件夹的名称 ,所以文件夹名称也不能使用中文和大写
  2. version ( 版本号 )要求 x.x.x 的形式定义, x 必须是数字,默认值是 1.0.0
  3. ISC 证书与 MIT 证书功能上是相同的,关于开源证书扩展阅读http://www.ruanyifeng.com/blog/2011/05/how_to_choose_free_software_licenses.html
  4. package.json 可以手动创建与修改
  5. 使用 npm init -y 或者 npm init --yes 极速创建 package.json
    在这里插入图片描述

3.npm搜索,下载,使用包

(1)搜索(search)

  1. 命令行 npm s
  2. 网站搜索网址 https://www.npmjs.com/

(2)下载(install)

  1. 通过 npm installnpm i 命令安装包
# 示例
npm install uniq
npm i uniq

安装后的目录如下
在这里插入图片描述

  • 运行之后文件夹下会增加两个资源node_modules 文件夹 存放下载的包
  • package-lock.json 包的锁文件 ,用来锁定包的版本

安装 uniq 之后, uniq 就是当前这个包的一个 依赖包 ,有时会简称为 依赖

比如我们创建一个包名字为 A,A 中安装了包名字是 B,我们就说 B 是 A 的一个依赖包 ,也会说A 依赖 B

(3)使用

// 导入uniq包
const uniq=require('uniq')
// 使用
let arr=[1,1,5,4,2,2,2,2,2,3,1]
const result =uniq(arr)
console.log(arr)
console.log(result)

在这里插入图片描述
由结果可见,此方法会改变原数组,并且结果经排序后输出。

4. require导入包

在这里插入图片描述

以下代码等价

但是第一种写法const uniq=require('uniq')更灵活就,因为:如果node_modules文件夹安装在了外层目录,根据如下规则:

  1. 在当前文件夹下 node_modules 中寻找同名的文件夹
  2. 在上级目录中下的 node_modules 中寻找同名的文件夹,直至找到磁盘根目录
    还是能成功导入模块

在这里插入图片描述

5.开发与生产依赖

举个例子方便大家理解,比如说做蛋炒饭需要 大米 , 油 , 葱 , 鸡蛋 , 锅 , 煤气 , 铲子 等
其中 锅 , 煤气 , 铲子 属于开发依赖,只在制作阶段使用
大米 , 油 , 葱 , 鸡蛋 属于生产依赖,在制作与最终食用都会用到
所以 开发依赖 是只在开发阶段使用的依赖包,而 生产依赖 是开发阶段和最终上线运行阶段都用到的依赖包

(1) 生产环境与开发环境

  • 开发环境是程序员 专门用来写代码 的环境,一般是指程序员的电脑,开发环境的项目一般 只能程序员自己访问
  • 生产环境是项目 代码正式运行 的环境,一般是指正式的服务器电脑,生产环境的项目一般 每个客户都可以访问

(2)生产依赖

运行时也使用它

生产依赖 npm i -S uniq或者npm i --save uniq
-S 等效于 --save, -S 是默认选项
包信息保存在 package.json 中 dependencies 属性

(3)开发依赖

只在开发阶段使用
npm i -D less
npm i --save-dev less
-D 等效于 --save-dev
包信息保存在 package.json 中 devDependencies 属性

npm i --save uniq
npm i -D less

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

6.全局安装 -g

执行安装选项 -g 进行全局安装
全局安装完成之后就可以在命令行的任何位置运行 nodemon 命令
该命令的作用是 自动重启 node 应用程序

npm i -g nodemon

在这里插入图片描述

// 导入模块
const http=require('http');

// 创建服务对象
const server=http.createServer((request,response)=>{
    response.setHeader('content-type','text/html;charset=utf-8')
    response.end('你好 nodemon')
});

// 监听端口,启动服务器
// 当服务启动成功以后执行此回调函数
server.listen(9000,()=>{
    console.log('服务已经启动...')
});

当返回的内容改变时,不需要手动重启服务器,使用nodemon会自动重启,只需要在浏览器界面刷新即可
在这里插入图片描述

在这里插入图片描述

实时根据内容的改变,自动重启服务器
在这里插入图片描述
在这里插入图片描述

说明:

  • 全局安装的命令不受工作目录位置影响
  • 可以通过 npm root -g 可以查看全局安装包的位置
  • 不是所有的包都适合全局安装 , 只有全局类的工具才适合,可以通过 查看包的官方文档来确定安装方式 ,这里先不必太纠结

在这里插入图片描述

7.修改window执行策略

目的:为解决 全局包无法使用的问题

如果可以使用nodemon命令,则不用管这些

  • 方法一
    在这里插入图片描述
  • 方法二
    修改vscode的终端窗口
    在这里插入图片描述

8.环境变量Path

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
当输入QQ命令时,会在当前目录查找以exe或者cmd结尾的文件,找不到则无法运行。

目的:不论在哪个目录都可以通过命令行找到QQ

步骤

  1. 把QQ所在目录复制D:\Tencent\QQ\Bin
  2. 打开高级设置——环境变量在这里插入图片描述
  3. 打开用户变量中的Path在这里插入图片描述
    4.重新打开一个命令行
    在这里插入图片描述

Path 是操作系统的一个环境变量,可以设置一些文件夹的路径,在当前工作目录下找不到可执行文件时,就会在环境变量 Path 的目录中挨个的查找,如果找到则执行,如果没有找到就会报错

9.安装包的所有依赖

在项目协作中有一个常用的命令就是 npm i通过该命令可以依据 package.jsonpackag-lock.json 的依赖声明安装项目依赖
node_modules 文件夹
。大多数情况都不会存入版本库

作用:下载 这个包(项目)安装的所有依赖。一般用于拉取远程仓库的代码,之后执行的。或者把文件发给别人,不用打包node_modules文件夹,人家npm i即可。

10.安装指定版本、删除包

项目中可能会遇到版本不匹配的情况,有时就需要安装指定版本的包

  1. 安装指定版本包
## 格式
npm i <包名@版本号>
## 示例
npm i jquery@1.11.2

在这里插入图片描述
在这里插入图片描述
2. 删除包
在这里插入图片描述
在这里插入图片描述

11.配置命令别名

  1. 点开package.json
  2. 找到script属性在这里插入图片描述
  3. 配置别名在这里插入图片描述
  4. 执行
//index.中的内容
console.log('hi')

在这里插入图片描述

start 别名比较特别,使用时可以省略 run

"scripts": {
"start": "node index.js"
},

npm start

npm start 是项目中常用的一个命令,一般用来启动项目 npm run 有自动向上级目录查找的特性,跟 require 函数也一样
对于陌生的项目,我们可以通过查看 scripts 属性来参考项目的一些操作

12.cnpm

  • cnpm 是一个淘宝构建的 npmjs.com 的完整镜像,也称为『淘宝镜像』,网址https://npmmirror.com/
  • cnpm 服务部署在国内 阿里云服务器上 , 可以提高包的下载速度
  • 官方也提供了一个全局工具包 cnpm ,操作命令与 npm 大体相同

(1)安装

npm install -g cnpm --registry=https://registry.npmmirror.com

在这里插入图片描述

(2)操作命令

初始化在这里插入图片描述

功能命令
初始化cnpm init / cnpm init
安装包cnpm i uniq
安装包cnpm i -S uniq
安装包cnpm i -D uniq
安装包cnpm i -g nodemon
安装项目依赖cnpm i
删除cnpm r uniq

在这里插入图片描述

(3)配置淘宝镜像

用 npm 也可以使用淘宝镜像,配置的方式有两种

  • 直接配置
npm config set registry https://registry.npmmirror.com/
  • 工具配置
    使用 nrm 配置 npm 的镜像地址 npm registry manager(nrm作用:决定npm到哪去下载包)
    在这里插入图片描述
    1. 安装 nrm npm i -g nrm
    2. 修改镜像 nrm use taobao
    3. 检查是否配置成功(选做) npm config list
    4. 检查 registry 地址是否为https://registry.npmmirror.com/ , 如果 是 则表明成功在这里插入图片描述

补充说明:
建议使用第二种方式 进行镜像配置,因为后续修改起来会比较方便
还可以使用nrm use npm切换回原来的

在这里插入图片描述

13.yarn

yarn 是由 Facebook 在 2016 年推出的新的 Javascript 包管理工具,官方网址:https://yarnpkg.com/

yarn 官方宣称的一些特点

  • 速度超快:yarn 缓存了每个下载过的包,所以再次使用时无需重复下载。 同时利用并行下载以最大
  • 化资源利用率,因此安装速度更快
  • 超级安全:在执行代码之前,yarn 会通过算法校验每个安装包的完整性
  • 超级可靠:使用详细、简洁的锁文件格式和明确的安装算法,yarn 能够保证在不同系统上无差异的
    工作

(1)安装

使用 npm 安装 yarn

npm i -g yarn

在这里插入图片描述

(2)命令操作

功能命令
初始化yarn init 或 yarn init -y
安装包yarn add uniq (生产依赖)
安装包yarn add less --dev (开发依赖)
安装包yarn global add nodemon (全局安装)
删除包yarn remove uniq (删除项目依赖包)
删除包yarn global remove nodemon (全局删除包)
安装项目依赖yarn
运行命令别名yarn <别名> (不需要添加 run)

在这里插入图片描述

{
  "dependencies": {
    "uniq": "^1.0.1"
  },
  "name": "yarnf",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "scripts": {
    "start": "node index.js"
  }
}

在这里插入图片描述

(3)全局安装中的注意点

因为没有配置环境变量,所以,通过yarn安装的-g等命令,没有作用,必须要配置环境变量。

  1. 找到yarn的位置
    在这里插入图片描述
    D:\nodejs\node_global\bin
  2. 手动配置环境变量

(4)配置淘宝镜像

可以通过如下命令配置淘宝镜像yarn config set registry https://registry.npmmirror.com/

在这里插入图片描述

可以通过 yarn config list 查看 yarn 的配置项

14. npm 和 yarn 选择

根据不同的场景进行选择

  1. 个人项目
    如果是个人项目, 哪个工具都可以 ,可以根据自己的喜好来选择
  2. 公司项目
    如果是公司要根据项目代码来选择,可以 通过锁文件判断 项目的包管理工具
    npm 的锁文件为 package-lock.json
    yarn 的锁文件为 yarn.lock

包管理工具 不要混着用,切记,切记,切记

15.发布包

(1) 创建与发布

我们可以将自己开发的工具包发布到 npm 服务上,方便自己和其他开发者使用,操作步骤如下:

  1. 创建文件夹,并创建文件 index.js, 在文件中声明函数,使用 module.exports 暴露

  2. npm 初始化工具包,package.json 填写包的信息 (包的名字是唯一的)

  3. 注册账号 https://www.npmjs.com/signup

  4. 激活账号 ( 一定要激活账号 )

  5. 修改为官方的官方镜像 (命令行中运行 nrm use npm )在这里插入图片描述

  6. 命令行下 npm login 填写相关用户信息

  7. 命令行下 npm publish 提交包 在这里插入图片描述

+表示发布成功

可以进行安装和使用了
在这里插入图片描述

(2) 更新包

后续可以对自己发布的包进行更新,操作步骤如下

  1. 更新包中的代码
    在这里插入图片描述

  2. 测试代码是否可用在这里插入图片描述

  3. 修改 package.json 中的版本号在这里插入图片描述

  4. 运行npm publish 发布更新的版本

(3)删除包

执行如下命令删除包npm unpublish --force

**加粗样式**

减号表示删除成功

删除包需要满足一定的条件,
https://docs.npmjs.com/policies/unpublish

  • 你是包的作者
  • 发布小于 24 小时
  • 大于 24 小时后,没有其他包依赖,并且每周小于 300 下载量,并且只有一个维护者

16.包管理工具扩展介绍

在很多语言中都有包管理工具,比如:

语言包管理工具
PHPcomposer
Pythonpip
Javamaven
Gogo mod
JavaScriptnpm/yarn/cnpm/other
RubyrubyGems

除了编程语言领域有包管理工具之外,操作系统层面也存在包管理工具,不过这个包指的是『 软件包 』

操作系统包管理工具网址
Centosyumhttps://packages.debian.org/stable/
Ubuntuapthttps://packages.ubuntu.com/
MacOShomebrewhttps://brew.sh/
Windowschocolateyhttps://chocolatey.org/

17.nvm的介绍与使用

全称:Node Version Manager ——用来管理node版本的工具,方便切换不同版本的node.js。

(1)下载安装

首先先下载 nvm,下载地址 https://github.com/coreybutler/nvm-windows/releases
选择 nvm-setup.exe 下载即可(网络异常的小朋友可以在资料文件夹中获取)。

(2)常用命令

命令说明
nvm list available显示所有可以下载的 Node.js 版本
nvm list显示已安装的版本
nvm install 18.12.1安装 18.12.1 版本的 Node.js
nvm install latest安装最新版的 Node.js
nvm uninstall 18.12.1删除某个版本的 Node.js
nvm use 18.12.1切换 18.12.1 的 Node.js

在这里插入图片描述

七、express框架

express 是一个基于 Node.js 平台的极简、灵活的 WEB 应用开发框架,官方网址:https://www.expressjs.com.cn/
简单来说,express 是一个封装好的工具包,封装了很多功能,便于我们开发 WEB 应用(HTTP 服务)

1.初体验

(1)express下载

express 本身是一个 npm 包,所以可以通过 npm 安装

npm init
npm i express

在这里插入图片描述

(2)创建代码

创建 JS 文件,键入如下代码

// 导入express 
const express=require('express');
// 创建应用对象
const app=express();
// 创建路由
// 当浏览器发送请求的方法是get,且请求的路径是/home的话
// 就会执行后面的回调函数,为浏览器响应结果
app.get('/home',(req,res)=>{;
    // req是请求报文的封装对象,res是响应报文的封装对象。
    res.end('hi express');
})
// 监听端口,启动服务
app.listen(3000,()=>{
    console.log('服务已经启动,端口3000正在监听中...')
})

在这里插入图片描述
使用nodemon开启服务器。
在这里插入图片描述
当正确输入路径后
在这里插入图片描述

2.路由的使用

官方定义:路由确定了应用程序如何响应客户端对特定端点的请求

(1)路由的组成

一个路由的组成有 请求方法 , 路径 和 回调函数 组成
express 中提供了一系列方法,可以很方便的使用路由,使用格式:app.<method>(path,callback)

app.get('/',(req,res)=>{;
    // req是请求报文的封装对象,res是响应报文的封装对象。
    res.end('home');
})

在这里插入图片描述

(2)post请求

在地址栏回车键入的是get请求。所以无法获取login页面。

app.post('/login',(req,res)=>{
    res.end('login_post')
})

在这里插入图片描述
解决方法

<!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 method="post" action="http://127.0.0.1:3000/login">
    <button>发送</button>
   </form>
</body>
</html>

点击“发送”按钮,即可跳转到login页面

在这里插入图片描述

(3)允许所有的请求方法

//js文件
const express=require('express');
const app=express();
app.all('/test',(req,res)=>{
    res.end('test_all')
})
app.listen(3000,()=>{
    console.log('服务已经启动,端口3000正在监听中...')
})
//html文件
<body>
   <form method="post" action="http://127.0.0.1:3000/test">
    <button>发送</button>
   </form>
</body>

使用app.all( )这样不论是html文件的表单发送的get请求,还是通过网址栏发送的get请求,都能得到服务器返回的页面。

const express=require('express');
const app=express();
// 匹配所有方法
app.all('/test',(req,res)=>{
    res.end('test_all')
})
// 匹配404
app.all('*',(req,res)=>{
    // *表示匹配所有的路径
    res.end('404 Not Found')
})
app.listen(3000,()=>{
    console.log('服务已经启动,端口3000正在监听中...')
})


在这里插入图片描述

3.提取请求数据

(1)req属性

const express=require('express');
const app=express();
app.get('/request',(req,res)=>{
    // 原生操作
   console.log(req.method)
   console.log(req.url)
   console.log(req.httpVersion)
   console.log(req.headers)
   console.log('______________________________________')
   //express 操作
    console.log(req.path)
    console.log(req.query)
    console.log(req.ip)
   res.end('hello expression')
})

app.listen(3000,()=>{
    console.log('服务已经启动,端口3000正在监听中...')
})

输出结果

GET
/request?a=100&b=200
1.1
{
  host: '127.0.0.1:3000',
  connection: 'keep-alive',
  'cache-control': 'max-age=0',
  'sec-ch-ua': '"Microsoft Edge";v="117", "Not;A=Brand";v="8", "Chromium";v="117"',
  'sec-ch-ua-mobile': '?0',
  'sec-ch-ua-platform': '"Windows"',
  'upgrade-insecure-requests': '1',
  'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36 Edg/117.0.2045.36',
  accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
  'sec-fetch-site': 'none',
  'sec-fetch-mode': 'navigate',
  'sec-fetch-user': '?1',
  'sec-fetch-dest': 'document',
  'accept-encoding': 'gzip, deflate, br',
  'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
  cookie: '_ga=GA1.1.1922259483.1687846523; _ga_R1FN4KJKJH=GS1.1.1688639930.11.1.1688640103.0.0.0'
}
______________________________________
/request
{ a: '100', b: '200' }
::ffff:127.0.0.1

(2)req方法

const express=require('express');
const app=express();
app.get('/request',(req,res)=>{
    //express 方法
    // 获取请求头
    // console.log(req.get())
    // 只获取host请求头
    console.log(req.get('host'))
   res.end('hello expression')
})

app.listen(3000,()=>{
    console.log('服务已经启动,端口3000正在监听中...')
})

输出结果
//      127.0.0.1:3000

4. 获取路由参数

路由参数指的是 URL 路径中的参数(数据

(1)占位符 :f

const express=require('express');
const app=express();
app.get('/:idf.html',(req,res)=>{
    res.setHeader('content-type','text/html;charset=utf-8')
    res.end('商品详情')
})

app.listen(3000,()=>{
    console.log('服务已经启动,端口3000正在监听中...')
})

使用:id占位符。

弹幕:商品页面就是固定的,然后去根据id请求数据库中的内容,渲染到当前页面

(2)获取路由参数req.params.f

通过req.params.idf得到请求数据时的路由参数。

const express=require('express');
const app=express();
app.get('/:idf.html',(req,res)=>{
    res.setHeader('content-type','text/html;charset=utf-8')
    console.log(req.params.idf)
    res.end('商品详情')
})

app.listen(3000,()=>{
    console.log('服务已经启动,端口3000正在监听中...')
})


在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

(3)练习

为什么singers不能换成别的值
在这里插入图片描述

a.获取数据

下面两块代码只有占位符不同

const express=require('express');
//导入json文件
// 因为这个json文件里面有两层对象,所以使用解构赋值
const {singers}=require('./singers.json')
console.log(singers)
const app=express();
app.get('/single/:f.html',(req,res)=>{
    res.setHeader('content-type','text/html;charset=utf-8')
    let{f}=req.params;
    res.end('商品详情')
})

app.listen(3000,()=>{
    console.log('服务已经启动,端口3000正在监听中...')
})


最后结果变成数组。在这里插入图片描述

const express=require('express');
//导入json文件
// 因为这个json文件里面有两层对象,所以使用解构赋值
const {singers}=require('./singers.json')
// console.log(singers)
const app=express();
app.get('/singer/:id.html',(req,res)=>{
    res.setHeader('content-type','text/html;charset=utf-8')
    let{id}=req.params;
    // 在数组中寻找对应id的数据
    let result=singers.find(item=>{
        if(item.id===Number(id)){
            return true;
        }
    });
    console.log(result)
    res.end('result')
})

app.listen(3000,()=>{
    console.log('服务已经启动,端口3000正在监听中...')
})
const express=require('express');
//导入json文件
// 因为这个json文件里面有两层对象,所以使用解构赋值
const {singers}=require('./singers.json')
// console.log(singers)
const app=express();
app.get('/singer/:f.html',(req,res)=>{
    res.setHeader('content-type','text/html;charset=utf-8')
    let{f}=req.params;
    // 在数组中寻找对应id的数据
    let result=singers.find(item=>{
        if(item.id===Number(f)){
            return true;
        }
    });
    console.log(result)
    res.end('result')
})

app.listen(3000,()=>{
    console.log('服务已经启动,端口3000正在监听中...')
})


item就是数组中的某个对象
在这里插入图片描述

b.将数据展示到页面
const express=require('express');
//导入json文件
// 因为这个json文件里面有两层对象,所以使用解构赋值
const {singers}=require('./singers.json')
// console.log(singers)
const app=express();
app.get('/singer/:f.html',(req,res)=>{
    res.setHeader('content-type','text/html;charset=utf-8')
    let{f}=req.params;
    // 在数组中寻找对应id的数据
    let result=singers.find(item=>{
        if(item.id===Number(f)){
            return true;
        }
    });
    // console.log(result)
    //当请求的内容不在json数据里,就返回404
    if(!result){
        res.statusCode=404;
        res.end(`<h1>404 Not Found</h1>`)
        return;
    }
    res.end(`<!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>
       <h2>${result.singer_name}</h2>
       <img src='${result.singer_pic}' alt="">
    </body>
    </html>`)
})

app.listen(3000,()=>{
    console.log('服务已经启动,端口3000正在监听中...')
})


在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5.响应设置

(1)原生响应

const express=require('express');
const app=express();
app.get('/response',(req,res)=>{
    // 原生响应
    res.statusCode=404
    res.statusMessage='love ping'
    res.setHeader('xxx','yyy')
    res.write('hello')

    res.end('response')
})
app.listen(3000,()=>{
    console.log('服务已经启动,端口3000正在监听中...')
})

在这里插入图片描述

(2)内置响应

分开写

const express=require('express');
const app=express();
app.get('/response',(req,res)=>{
    res.status(500)
    res.set('chang','ping')
    res.send('你好')
    res.end('response')
})
app.listen(3000,()=>{
    console.log('服务已经启动,端口3000正在监听中...')
})

连着写

const express=require('express');
const app=express();
app.get('/response',(req,res)=>{
    res.status(500).set('haiying','changping').send('这很OK')
})
app.listen(3000,()=>{
    console.log('服务已经启动,端口3000正在监听中...')
})

在这里插入图片描述

res.send('你好')方法会自动在响应头添加,防止中文展示乱码。在这里插入图片描述

(3)其他响应

a.重定向
const express=require('express');
const app=express();
app.get('/other',(req,res)=>{
    //跳转响应
    res.redirect('https://mp.csdn.net/mp_blog/manage/article?spm=1000.2115.3001.5448')
})
app.listen(3000,()=>{
    console.log('服务已经启动,端口3000正在监听中...')
})

在这里插入图片描述

b.下载

在网址输入路径后,自动下载软件

const express=require('express');
const app=express();
app.get('/other',(req,res)=>{
    //下载响应
    res.download(__dirname+'/package.json')
})
app.listen(3000,()=>{
    console.log('服务已经启动,端口3000正在监听中...')
})

在这里插入图片描述

c.JSON响应
const express=require('express');
const app=express();
app.get('/other',(req,res)=>{
    //JSON响应
    res.json({
        'fang':'haiying',
        'age':'19'
    })
})
app.listen(3000,()=>{
    console.log('服务已经启动,端口3000正在监听中...')
})

在这里插入图片描述

d.响应文件内容
const express=require('express');
const app=express();
app.get('/other',(req,res)=>{
    // 响应文件内容
    res.sendFile(__dirname+'/index.html')
})
app.listen(3000,()=>{
    console.log('服务已经启动,端口3000正在监听中...')
})
<!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>
  <div style="color: pink;">你好呀</div>
</body>
</html>

在这里插入图片描述

6.中间件

  1. 什么是中间件
    • 中间件(Middleware)本质是一个回调函数
    • 中间件函数 可以像路由回调一样访问 请求对象(request) , 响应对象(response)
  2. 中间件的作用
    中间件的作用 就是 使用函数封装公共操作,简化代码
  3. 中间件的类型
    全局中间件
    路由中间件

在这里插入图片描述

(1)全局中间件

需求:记录每个请求的url和IP地址

a. 原始方法
const express=require('express');
const fs=require('fs')
const path=require('path')
const app=express();
app.get('/home',(req,res)=>{ 
    // 获取url和ip
     let {url,ip}=req;
     console.log(url+'   '+ip)
     //将信息保存在文件中
    fs.appendFileSync(path.resolve(__dirname,'./fhy.log'),`${url}${ip}\r\n`)
    res.send('主页')
})
app.get('/admin',(req,res)=>{
    res.send('后台')
})
app.all('*',(req,res)=>{
    res.send(`404 Not Found`)
})
app.listen(3000,()=>{
    console.log('服务已经启动,端口3000正在监听中...')
})

在这里插入图片描述
但是目前只对主页一个路径起作用

b.中间件方法

整个效果就是先执行中间件函数,然后通过next执行路由函数。

const express = require('express');
const fs = require('fs')
const path = require('path')
const app = express();

// 声明中间件函数
//next是内部函数,后续会指向路由或者中间件
function recordMiddleware(req, res, next) {
    // 获取url和ip
    let { url, ip } = req;
    console.log(url + '   ' + ip)
    //将信息保存在文件中
    fs.appendFileSync(path.resolve(__dirname, './fhy.log'), `${url}${ip}\r\n`)
    // 调用next函数
    next()
}

//使用中间件函数
app.use(recordMiddleware)

app.get('/home', (req, res) => {
    res.send('主页')
})
app.get('/admin', (req, res) => {
    res.send('后台')
})
app.all('*', (req, res) => {
    res.send(`404 Not Found`)
})
app.listen(3000, () => {
    console.log('服务已经启动,端口3000正在监听中...')
})

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
文件内容
在这里插入图片描述

(2)路由中间件——不是很懂

如果 只需要对某一些路由进行功能封装 ,则就需要路由中间件。

需求:在这里插入图片描述

const express = require('express');
const fs = require('fs')
const path = require('path')
const app = express();

// 声明中间件函数
//next是内部函数,后续会指向路由或者中间件
let checkCodeMid=(req, res, next)=> {
//    判断url的code参数是否等于521
    if(req.query.code==='521'){
        next()
    }else{
        res.send('暗号错误')
    }
    // 调用next函数
    next()
}

//使用中间件函数
app.use(checkCodeMid)

// 以下都是路由回调
app.get('/home', (req, res) => {
    res.send('主页')
})

//把中间件函数放到受约束的函数中
app.get('/admin', checkCodeMid,(req, res) => {
    res.send('后台')
})
app.get('/set',checkCodeMid, (req, res) => {
    res.send('设置页面')
})
app.all('*', (req, res) => {
    res.send(`404 Not Found`)
})
app.listen(3000, () => {
    console.log('服务已经启动,端口3000正在监听中...')
})

(3)静态资源中间件

静态资源目录(网站根目录)
浏览器发送给服务端请求后,服务端到哪个目录下去找对应的文化,则那个文件的文件夹就是静态资源目录。

const express = require('express');
const app = express();
//静态资源中间件的设置,将当前文件夹下的public目录作为网站的根目录
//express.static的返回结果是中间件函数
app.use(express.static(__dirname + '/public')); // 这个参数就是静态资源文件夹的路径

app.get('/home',(req,res)=>{
res.send('首页');
});

app.listen(3000,()=>{
console.log('3000 端口启动....');
});

public要放在init文件夹下面
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

a.注意事项
  1. index.html 文件为默认打开的资源(即不输入/index.html,界面默认显示此文件)在这里插入图片描述
    在这里插入图片描述

  2. 如果静态资源与路由规则同时匹配,谁先匹配谁就响应。

  3. 路由一般响应动态资源,静态资源中间件响应静态资源

b.练习

目的:使手机也可以通过局域网看到静态资源

在这里插入图片描述
接下来是手机显示
请添加图片描述
请添加图片描述

7.获取请求体数据

使用 body-parser 包处理请求体
在这里插入图片描述

需求如下:
在这里插入图片描述
通过按钮发送post请求数据,获取请求体
在这里插入图片描述
在这里插入图片描述

在npm搜索包,并选择路由中间件的使用方式

// create application/json parser
var jsonParser = bodyParser.json()

// create application/x-www-form-urlencoded parser
var urlencodedParser = bodyParser.urlencoded({ extended: false })

当中间件函数执行完毕后,会向req对象上,添加一个body属性。
在这里插入图片描述

const express =require('express');
const app=express();
//导入解析请求体的包
const bodyParser=require('body-parser')
//解析queryString格式 请求体的中间件
const urlEncodeParser=bodyParser.urlencoded({extended:false})
//创建路由规则
app.get('/login',(req,res)=>{
    // 响应文件内容
    res.sendFile(__dirname+'/form.html')
})
app.post('/login',urlEncodeParser,(req,res)=>{
    if(req.body){
        res.send(req.body)
    }else{
        res.send('数据获取失败')
    }
})

app.listen(3000,()=>{
    console.log('服务已经启动...')
})
<body>
    <!-- <form action="http://127.0.0.1:3000/login" method="post"> -->
    <!-- 代码可以简写,只写路径 -->
        <form action="/login" method="post">
        用户名:<input type="text" name="username">
        <br>
        密码:<input type="password" name="password">
        <br>
        <button>登录</button>
    </form>
</body>

在这里插入图片描述

8.防盗链

(1)介绍

防止外部网站盗用网站资源。
禁止该域名之外的其他网站访问这个资源在这里插入图片描述

实现防盗链效果

在请求头中有一个 referer包含了发送请求的网站的协议、域名、端口。
在这里插入图片描述
在这里插入图片描述
如果referer不对,则返回404结果。

(2)练习

正常展示静态资源,使用localhost127.0.0.1都可以访问

const express =require('express');
const app=express();
app.use(express.static(__dirname+'/public'))

app.listen(3000,()=>{
    console.log('服务已经启动...')
})

在这里插入图片描述
在这里插入图片描述

添加防盗链效果:只有127.0.0.1域名可以访问。

const express = require('express');
const app = express();
//添加中间件,实现效果
app.use((req, res, next) => {
    // 获取referer
    let referer = req.get('referer')
    // 判断一下是否有referer
    if (referer) {
        // 实例化
        let url = new URL(referer)
        // 获取hostname域名
        let hostname=url.hostname
        console.log(hostname)
        if(hostname!=='127.0.0.1'){
            res.end('404')
            return
        }
    }
    next()
})
app.use(express.static(__dirname + '/public'))

app.listen(3000, () => {
    console.log('服务已经启动...')
})

在这里插入图片描述
在这里插入图片描述

9.路由模块化

  • express 中的 Router 是一个完整的中间件和路由系统,可以看做是一个小型的 app 对象。
  • 对路由进行模块化,更好的管理路由
  • router就相当于小型的app对象,就是把原来的app.get( )、app.post( )变成router.get( ) 、router.post()等。

(1)未导入时

没有路由组件时

//main.js文件
const express = require('express');
const app = express();
//引入子路由文件
// const homeRouter=require('./routers/homeRouter.js')
//设置和使用中间件
// app.use(homeRouter)

app.all('*',(req,res)=>{
    res.send('<h1>404</h1>')
})
app.listen(3000,()=>{
console.log('3000 端口启动....');
})

在这里插入图片描述
在这里插入图片描述

(2)导入一个后

创建的路由文件的路径D:\web\node\init\routers\homeRouter.js

homeRouter.js
//1. 导入 express
const express = require('express');
//2. 创建路由器对象
const router = express.Router();
//3. 在 router 对象身上添加路由
router.get('/', (req, res) => {
res.send('首页');
})
router.get('/search', (req, res) => {
res.send('内容搜索');
});
//4. 暴露
module.exports = router;

在这里插入图片描述
在这里插入图片描述

(3) 两个都导入后

//1. 导入 express
const express = require('express');
//2. 创建路由器对象
const router = express.Router();
//3. 在 router 对象身上添加路由
router.get('/admin', (req, res) => {
res.send('管理页面');
})

//4. 暴露
module.exports = router;

在这里插入图片描述

(4)设置路由前缀

在这里插入图片描述

在这里插入图片描述

修改后的user.js路由模块。

var express = require('express');
var router = express.Router();

/* GET users listing. */
router.get('/', function(req, res, next) {
  res.send('respond with a resource');
});

router.get('/test', function(req, res, next) {
  res.send('Flavia测试');
});

module.exports = router;

在这里插入图片描述
因为在app.js中设置了路由前缀app.use('/users', usersRouter);,所以得输入 /users/test
在这里插入图片描述

成功显示!

10.模版引擎

在这里插入图片描述

(1)理解

也就是分离服务器端的js和html
如下面的代码

const express=require('express');
//导入json文件
// 因为这个json文件里面有两层对象,所以使用解构赋值
const {singers}=require('./singers.json')
// console.log(singers)
const app=express();
app.get('/singer/:f.html',(req,res)=>{
    res.setHeader('content-type','text/html;charset=utf-8')
    let{f}=req.params;
    // 在数组中寻找对应id的数据
    let result=singers.find(item=>{
        if(item.id===Number(f)){
            return true;
        }
    });
    // console.log(result)
    //当请求的内容不在json数据里,就返回404
    if(!result){
        res.statusCode=404;
        res.end(`<h1>404 Not Found</h1>`)
        return;
    }
    res.end(`<!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>
       <h2>${result.singer_name}</h2>
       <img src='${result.singer_pic}' alt="">
    </body>
    </html>`)
})

app.listen(3000,()=>{
    console.log('服务已经启动,端口3000正在监听中...')
})

我们要做的就是分离这部分在这里插入图片描述

(2)初体验

注意 nrm use taobao 使用淘宝镜像。
下载安装EJS npm i ejs
在这里插入图片描述
在ejs文件夹下装的,但是node_modules文件夹在ejs文件夹外部。
在这里插入图片描述

目的:我们要达到拼接效果,像下面代码所示,但是不想让str2变量和其他混在一起,这样不利于后续处理

const ejs=require('ejs')
let str1 ='你好'
let str2=`ejs${str1}`
console.log(str2)

在这里插入图片描述

<%= %> 是ejs解析内容的标记,作用是输出当前表达式的执行结果。
ejs会从后面传入的数据中找<%= %> 内的内容,并利用对应属性的值替换掉<%= %> 内的内容。

下面不同的写法,效果相同

const ejs=require('ejs')
let result=ejs.render('你好<%= str %>',{str:'ejs'})
console.log(result)
const ejs=require('ejs')
h='你好<%= str %>'
str='ejs'

let result=ejs.render(h,{str:str})
console.log(result)

const fs=require('fs')
const ejs=require('ejs')
hi=fs.readFileSync('./str.html').toString()
let result=ejs.render(hi,{str:'ejs'})
console.log(result)

str.html的内容
在这里插入图片描述

在这里插入图片描述

有点晕了

const fs=require('fs')
const ejs=require('ejs')

let ejsHtml=fs.readFileSync('./ejs.html').toString()
let str='ejs'
weather='今天天气不错'
let result=ejs.render(ejsHtml,{str,weather,ejsHtml})
console.log(result)


<!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>
    <h2>你好<%= str %></h2>
    <%= weather %>
</body>
</html>

在这里插入图片描述

(3)列表渲染

原生js实现

const palyer=['喜羊羊','美羊羊','懒羊羊','灰太狼']
let str='<ul>'
palyer.forEach(item=>{
    str+=`<li>${item}</li>`
})
str+='</ul>'
console.log(str)

在这里插入图片描述

使用ejs

(4)条件渲染

11.express-generator

安装命令npm install -g express-generator
在这里插入图片描述
创建命令express -e 名字

在这里插入图片描述
所创建的文件夹目录
在这里插入图片描述

  1. 安装依赖在这里插入图片描述

  2. 装好后的目录
    在这里插入图片描述

  3. 通过 npm start 运行文件在这里插入图片描述

  4. 进入
    在这里插入图片描述

错误处理的两种方式
在这里插入图片描述

12.文件上传

(1)上传效果实现

D:\web\node\init\ejs\generator\routes\index.js下的内容

var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { title: 'Express' });
});
//显示网页的表单
router.get('/upload',(req,res)=>{
  res.render('upload')
})
//处理文件上传
router.post('/upload',(req,res)=>{
  res.send('成功')
})

module.exports = router;

D:\web\node\init\ejs\generator\views\upload.ejs下的内容

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>文件上传</title>
</head>

<body>
    <h2>文件上传</h2>
    <hr>
    <!-- 文件上传时,必不可少的设置enctype -->
    <form action="/upload" method="post" enctype="multipart/form-data">
        用户名:<input type="text" name="username">
        <br>
        头像:<input type="file" name="upload">
        <br>
        <button>点击提交</button>
    </form>

</body>

</html>

点击提交后的内容

在这里插入图片描述
在这里插入图片描述

(2)对上传文件进行处理

下载formidable

npm i formidable

在这里插入图片描述

有bug
在这里插入图片描述
在这里插入图片描述

已经解决!!!!!!!!!!
看到弹幕说换个版本
然后试了一下npm i formidable@2没问题了


var express = require('express');
// const formidable =require('formidable')

var router = express.Router();
const formidable = require('formidable');
/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { title: 'Express' });
});
//显示网页的表单
router.get('/upload',(req,res)=>{
  res.render('upload')
})
//处理文件上传
router.post('/upload',(req,res)=>{
  // 创建form表单
  const form = formidable({multiples:true});
// 解析请求报文
  form.parse(req, (err, fields, files) => {
    if (err) {
      next(err);
      return;
      
    }
    console.log('00')
    console.log(fields)
    console.log(files)
    res.send('ok')
  });
})

module.exports = router;

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>文件上传</title>
</head>

<body>
    <h2>文件上传</h2>
    <hr>
    <!-- 文件上传时,必不可少的设置enctype -->
    <form action="/upload" method="post" enctype="multipart/form-data">
        用户名:<input type="text" name="username">
        <br>
        头像:<input type="file" name="upload">
        <br>
        <button>点击提交</button>
    </form>

</body>

</html>

在这里插入图片描述
在这里插入图片描述

(3)保存文件

router.post('/upload',(req,res)=>{
  // 创建form表单
  const form = formidable({
    multiples:true,
    //设置文件的保存路径
    uploadDir:__dirname+'/../public/images',
    // 保存文件后缀名
    keepExtensions:true
  });
// 解析请求报文
  form.parse(req, (err, fields, files) => {
    if (err) {
      next(err);
      return;
    }
    console.log(fields)
    console.log(files)
    res.send('ok')
  });
})

在这里插入图片描述

让用户获得图像

let url='/images/'+files.upload.newFilename
    // 学完数据库,将此数据保存在数据库中
    res.send(url)

注意大小写newFilename是对的
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

八、接口

1.简介

  • 接口是 前后端通信的桥梁
  • 简单理解:一个接口就是 服务中的一个路由规则 ,根据请求 响应结果。(可以接收客户端发来的请求,并且给客户端响应结果,一般是json格式
  • 接口的英文单词是 API (Application Program Interface),所以有时也称之为 API 接口
  • 这里的接口指的是『数据接口』, 与编程语言(Java,Go 等)中的接口语法不同。

2.开发与调用

  • 大多数接口都是由 后端工程师 开发的, 开发语言不限
  • 一般情况下接口都是由 前端工程师 调用的,但有时 后端工程师也会调用接口 ,比如短信接口,支付接口等。

3.接口的组成

请求方法
接口地址(URL)
请求参数
响应结果
在这里插入图片描述

4. RESTful API

RESTful API 是一种特殊风格的接口,主要特点有如下几个:

  • URL 中的路径表示 资源 ,路径中不能有 动词 ,例如 create , delete , update 等这些都不能有
  • 操作资源要与 HTTP 请求方法 对应
  • 操作结果要与 HTTP 响应状态码 对应

规则示例:

操作请求类型URL返回
新增歌曲POST/song返回新生成的歌曲信息
删除歌曲DELETE/song/10返回一个空文档
修改歌曲PUT/song/10返回更新后的歌曲信息
修改歌曲PATCH/song/10返回更新后的歌曲信息
获取所有歌曲GET/song返回歌曲列表数组
获取单个歌曲GET/song/10返回单个歌曲信息

扩展阅读:https://www.ruanyifeng.com/blog/2014/05/restful_api.html

5.json-server

json-server 本身是一个 JS 编写的工具包,可以快速搭建 RESTful API 服务

官方地址: https://github.com/typicode/json-server

  1. 全局安装 json-server
npm i -g json-server

在这里插入图片描述

  1. 创建 JSON 文件(db.json),编写基本结构
{
"song": [
{ "id": 1, "name": "干杯", "singer": "五月天" },
{ "id": 2, "name": "当", "singer": "动力火车" },
{ "id": 3, "name": "不能说的秘密", "singer": "周杰伦" }
]
}
  1. JSON 文件所在文件夹作为工作目录 ,执行如下命令
json-server --watch db.json

在这里插入图片描述

默认监听端口为 3000
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6.接口测试工具

7.apiPost

(1)前缀

  • 设置前缀在这里插入图片描述

在这里插入图片描述

(2)新增歌曲

在这里插入图片描述
在这里插入图片描述

会自动添加id,我们不用写

(3)删除歌曲

需要传入对应id
在这里插入图片描述

(4)更新数据

在这里插入图片描述

(5)传入公共参数

新建一个目录,把相关接口都放进去。

在这里插入图片描述

8.postman

在这里插入图片描述

九、会话控制

  • HTTP 是一种无状态的协议,它没有办法区分多次的请求是否来自于同一个客户端, 无法区分用户。
  • 而产品中又大量存在的这样的需求,所以我们需要通过 会话控制 来解决该问题
  • 常见的会话控制技术有三种:
    • cookie
    • session
    • token

1.cookie

  • cookie 是 HTTP 服务器发 送到用户浏览器 并保存在本地的一小块数据
  • cookie 是按照域名划分保存的

简单示例:

域名cookie
www.baidu.coma=100; b=200
www.bilibili.comxid=1020abce121; hm=112411213
jd.comx=100; ocw=12414cce

(1)特点

  • 浏览器向服务器发送请求时,会自动将 当前域名下 可用的 cookie 设置在请求头中,然后传递给服务器。
  • 请求头的名字也叫 cookie ,所以将 cookie 理解为一个 HTTP 的请求头也是可以的

(2)运行流程

填写账号和密码校验身份,校验通过后下发 cookie
在这里插入图片描述
有了 cookie 之后,后续向服务器发送请求时,就会自动携带 cookie
在这里插入图片描述

(3)浏览器操作 cookie

浏览器操作 cookie 的操作,使用相对较少,大家了解即可

  1. 禁用所有 cookie

  2. 删除 cookie在这里插入图片描述

  3. 查看 cookie
    在这里插入图片描述

(4)cookie 的代码操作

在这里插入图片描述

express 中可以使用 cookie-parser 进行处理

const express =require('express');
//1. 安装 cookie-parser npm i cookie-parser
//2. 引入 cookieParser 包
const cookieParser = require('cookie-parser');
const app = express();
//3. 设置 cookieParser 中间件
app.use(cookieParser());

//4-1 设置 cookie
app.get('/set-cookie', (request, response) => {
// 不带时效性
response.cookie('username','wangwu');
// 带时效性
//maxAge是生命周期
//只要生命周期没到时间,就算浏览器关闭,也依旧存在
response.cookie('email','[email protected]', {maxAge: 5*60*1000 });
//响应
response.send('Cookie的设置');
});

//4-2 读取 cookie
app.get('/get-cookie', (request, response) => {
//读取 cookie
console.log(request.cookies);
//响应体
response.send('Cookie的读取');
});

//4-3 删除cookie
app.get('/delete-cookie', (request, response) => {
//删除
response.clearCookie('username');
//响应
response.send('cookie 的清除');
});
//4. 启动服务
app.listen(3000, () => {
console.log('服务已经启动....');
});

不同浏览器中的 cookie 是相互独立的,不共享

2. session

在这里插入图片描述

代码操作

const express = require('express');
//1. 安装包 npm i express-session connect-mongo
//2. 引入 express-session connect-mongo
const session = require("express-session");
const MongoStore = require('connect-mongo');
const app = express();
//3. 设置 session 的中间件
app.use(session({
name: 'sid', //设置cookie的name,默认值是:connect.sid
secret: 'atguigu', //参与加密的字符串(又称签名)
saveUninitialized: false, //是否为每次请求都设置一个cookie用来存储session的id
resave: true, //是否在每次请求时重新保存session
store: MongoStore.create({
mongoUrl: 'mongodb://127.0.0.1:27017/project' //数据库的连接配置
}),
cookie: {
httpOnly: true, // 开启后前端无法通过 JS 操作
maxAge: 1000 * 300 // 这一条 是控制 sessionID 的过期时间的!!!
},
}))
//创建 session
app.get('/login', (req, res) => {
//设置session
req.session.username = 'zhangsan';
req.session.email = '[email protected]'
res.send('登录成功');
})
//获取 session
app.get('/home', (req, res) => {
console.log('session的信息');
console.log(req.session.username);
if (req.session.username) {
res.send(`你好 ${req.session.username}`);
}else{
res.send('登录 注册');
}
})
//销毁 session
app.get('/logout', (req, res) => {
//销毁session
// res.send('设置session');
req.session.destroy(() => {
res.send('成功退出');
});
});
app.listen(3000, () => {
console.log('服务已经启动, 端口 ' + 3000 + ' 监听中...');
});

在这里插入图片描述
有这个标识后,前端js便不能对cookie进行操作。否则,不安全,如使用document.cookie

在这里插入图片描述

session 和 cookie 的区别

  1. 存在的位置 :cookie——浏览器端 session——服务端
  2. 安全性: cookie 是以明文的方式存放在客户端的,安全性相对较低 session 存放于服务器中,所以安全性 相对 较好
  3. 网络传输量 :cookie 设置内容过多会增大报文体积, 会影响传输效率 。session 数据存储在服务器,只是通过 cookie 传递 id,所以不影响传输效率
  4. 存储限制 浏览器限制单个 cookie 保存的数据不能超过 4K ,且单个域名下的存储数量也有限制session 数据存储在服务器中,所以没有这些限制.

3. token

  • token 是服务端生成并返回给 HTTP 客户端的一串加密字符串, token 中保存着 用户信息
  • 作用:实现会话控制,可以识别用户的身份,主要用于移动端 APP。
  • token 的工作流程
  • 填写账号和密码校验身份,校验通过后响应 token,token 一般是在响应体中返回给客户端的
    在这里插入图片描述

特点

  • 服务端压力更小
    数据存储在客户端
  • 相对更安全
    数据加密
    可以避免 CSRF(跨站请求伪造)
  • 扩展性更强
    服务间可以共享
    增加服务节点更简单

JWT(JSON Web Token )是目前最流行的跨域认证解决方案,可用于基于 token 的身份验证
JWT 使 token 的生成与校验更规范
我们可以使用 jsonwebtoken 包 来操作 token

//导入 jsonwebtokan
const jwt = require('jsonwebtoken');
//创建 token
// jwt.sign(数据, 加密字符串, 配置对象)
let token = jwt.sign({
username: 'zhangsan'
}, 'atguigu', {
expiresIn: 60 //单位是 秒
})
//解析 token
jwt.verify(token, 'atguigu', (err, data) => {
if(err){
console.log('校验失败~~');
return
}
console.log(data);
})

4. 本地域名

所谓本地域名就是 只能在本机使用的域名 ,一般在开发阶段使用

十、项目上线

创建.gitignore,并写入
在这里插入图片描述

HTTPS

尚硅谷

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;