nodejs基础
1. 简介
Node.js是一个开源的,跨平台的JavaScript运行环境。通俗来讲,node.js就是一款应用程序,是一款软件,它可以运行JavaScript。
1.1 Node.js的作用
1.开发服务器应用
众所周知,网页的构建是通过HTML、CSS、JavaScript来完成的,其中HTML负责控制结构、CSS负责控制样式、JS负责控制交互和效果。当在本机双击HTML文件后,页面就能够打开,但是这个网页只能在本机看到。如果想要该网页能够让每个人都能够访问,就需要使用服务器了。因为服务器能够保存我们写好的HTML、CSS、JavaScript,其他用户能够在自己的电脑上通过url来向我们的服务器发送请求,发送请求后,服务器能够将这些资源返回给用户的浏览器,然后浏览器就可以对这些资源做解析,页面就能够呈现了(所有的用户都能够通过url来访问服务器)。在这个过程中,node.js就运行在服务器端,它会对用户的请求做处理,并且把这些资源返回给浏览器。
2.开发工具类应用
目前前端开发中非常重要的三款工具Webpack、vite、Babel,它们可以提高前端项目的开发效率和质量,但都是借助于Node.js开发能力而实现的。所以可以借助node.js来创建一些属于自己的工具,来提高开发效率。
3.开发桌面端应用
对于代码编辑工具VSCode、设计工具Figma、接口测试工具Postman这三款软件都是借助electron框架,而这个框架又是借助node.js开发出来的,所以在学习了node.js后还可以去开发一些桌面端应用程序。
1.2 node.js编码注意事项
1.Node.js中不能使用BOM和DOM的API,可以使用console和定时器API。
BOM(Browser Object Model,浏览器对象模型)是与浏览器窗口进行互动的对象结构。它提供了一组可以操作浏览器窗口、历史记录、定时器等浏览器相关信息的API
DOM(Document Object Model,文档对象模型)则是一个与平台和语言无关的接口,它将网页的结构化文档表示为一个树形结构,允许程序和脚本动态地访问和更新文档的内容、结构和样式。
简而言之,BOM API 允许开发者与浏览器窗口和浏览器本身进行交互,而 DOM API 则允许开发者访问和操作网页的内容和结构。两者都是 JavaScript 编程中的重要组成部分,使得开发者能够创建动态和交互式的网页应用。
Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境,它允许 JavaScript 运行在服务器端。由于 Node.js 运行在服务器端,而不是在浏览器环境中,因此它不提供浏览器特有的 API,比如 BOM 和部分 DOM API。
2.Node.js中的顶级对象为global,也可以用globalThis访问顶级对象。
在 Node.js 环境中,全局对象被称为
global
。这意味着在 Node.js 中定义的全局变量和函数会自动成为global
对象的属性和方法。例如,如果你在 Node.js 脚本中定义了一个全局变量var myGlobalVar = 'Hello, world!';
,那么你可以通过global.myGlobalVar
来访问这个变量。globalThis
是一个相对较新的全局属性,它在所有环境中(包括浏览器和 Node.js)都指向全局对象。这意味着无论在哪个环境中,你都可以使用globalThis
来访问全局作用域下的变量和函数。在 Node.js 中,globalThis
指向的就是global
对象。
2. Buffer(缓冲器)
Buffer中文译为缓冲区,是一个类似于Array的对象,用于表示固定长度的字节序列。换句话说,Buffer就是一段固定长度的内存空间,用于处理二进制数据。
2.1 介绍
1.概念:Buffer是一个类似于数值的对象
,用于表示固定长度的字节序列。Buffer本质是一段内存空间,专门用来处理二进制数据
。
2.特点
- Buffer大小固定且无法调整
- Buffer性能较好,可以直接对计算机内存进行操作
- 每个元素的大小为1字节(byte)
2.2Buffer的使用
1.buffer的创建有三种方法,分别是alloc、allocUnsafe和from。如下程序所示:
1 | //1. alloc |
2.Buffer与字符串的转换
1 | //buffer与字符串的转换 |
3.Buffer的读写:可以直接通过[]的方式对数据进行处理
1 | let buf = Buffer.from('hello'); |
4.注意事项
如果数值超过255,则超过8位,数据会被舍弃:
1 | let buf = Buffer.from('hello'); |
中文是utf-8的中文,一个中文一般占3个字节:
1 | let buf = Buffer.from('你好'); //是6个字节 |
3. 计算机基础
3.1 计算机基本组成
1.CPU:中央处理器,是整个计算机运算和控制的中心
2.内存:是存储数据的一个介质,可以在里面存放大量的0和1这样的数据
- 特点:读写速度较快,断电丢失数据。程序在运行时都会载入到内存当中,让CPU高速的对这个数据进行执行和处理
3.硬盘:也可以存储很多0和1这样的数据,
- 特点:读写速度较慢,断电不丢失数据。平时下载的程序,如英雄联盟、浏览器、QQ等这些程序下载安装完后都是放到了硬盘里面。
4.显卡:负责处理视频信号的,当有信息需要呈现,需要在显示器中显示时,就会将信号传递给显卡,显卡处理完毕后,再将信号传递给显示器,然后显示器最终显示。
5.主板:是一块大的集成电路板,上面有很多插槽,很多元器件就是插在插槽里面,通过主板联系在一起的。
除了上面器件,再通过一些外设,如显示器、键盘、鼠标和音响等,整个电脑就算组装完毕了。
3.2 程序运行的基本流程
当所有器件都组装完毕之后,计算机也不能正常去运行。因为还缺少一个操作系统,常见的操作系统有Windows、Linux和MacOS。操作系统也是一种应用程序(010101..),用来管理和调度硬件资源。可以理解为,操作系统能够让cpu去执行哪个程序。
装系统:就是将操作系统这个程序安装在硬盘的这样一个过程。当把这个操作系统程序装到硬盘之后,电脑就可以开机运行了。
运行流程:首先会先将Windows相关的一些程序文件载入到内存里面,载入内存之后,CPU就可以运行了。CPU执行的时候,如果发现有视频信号需要在显示器上去呈现,就会交给显卡去处理,显卡处理完之后,再交由显示器去呈现。再处理过程当中,如果遇到了声音信号,这时候会交给声卡,声卡再将信号传递给外部的播放设备(如耳机、音响等)。它们一结合,就会呈现出视频和声音一起播放的效果
程序一般保存在硬盘中(还有一些是存在软盘这样一些介质里面的),软件安装的过程就是将程序写入硬盘的过程。程序在运行时会加载进入内存,然后由CPU读取并执行程序。
4. fs模块
fs模块可以实现与硬盘的交互。例如文件的创建、删除、重命名、移动,还有文件内容的写入、读取,以及文件夹的相关操作。
文件写入的应用场景:当需要持久化保存数据的时候,应该想到文件写入。
- 下载文件
- 安装软件
- 保存程序日志,如Git
- 编辑器保存文件
- 视频录制
文件读取应用场景
- 电脑开机
- 程序运行
- 编辑器打开文件
- 查看图片
- 播放视频
- 播放音乐
- Git查看日志
- 上传文件
- 查看聊天记录
4.1 读写操作
1.writeFile异步写入
语法:fs.writeFile(file, data[, options], callback)
参数说明:
- file:文件名(不存在会自动创建)
- data:待写入的数据
- options:选项设置(可选)
- callback:写入回调(当写入完成之后,会自动调用该回调函数)
测试程序:在下面程序中,代码先自上而下运行,当运行到writeFile时,会进行磁盘的写入,它会将这个磁盘写入交给另一个线程(IO线程)去完成,而主线程会继续向下执行。当IO线程写入完毕之后,会将回调函数压入到队列当中,等js的主线程给初始化的代码执行完毕之后,就会从任务队列中将这个回调函数取出来执行。如下面程序的结果是先打印12,再打印写入成功。
1 | //需求:新建一个文件夹,座右铭.txt,写入的内容:三人行,必有我师焉 |
2.writeFileSync同步写入
语法:fs.writeFileSync(file, data)
参数说明:
- file:文件名(不存在会自动创建)
- data:待写入的数据
测试程序:创建了一个data.txt文件,向里面写入了test
1 | const fs = require('fs'); |
3.appendFile异步追加
1 | const fs = require('fs'); |
4.appendFileSync同步追加
1 | const fs = require('fs'); |
5.writeFile实现追加写入
1 | fs.writeFile('./座右铭.txt', 'love love love',{flag: 'a'}, err=>{ |
6.createWriteStream流式写入
createWriteStream会根据传入的参数路径,与它建立一个通道,啥时候想写,就通过write往通道里面传入内容即可。
1 | const ws = fs.createWriteStream('./观书有感.txt'); //引入写入流对象(接收一个文件路径的参数) |
特点:程序打开一个文件是需要消耗资源的,流式写入可以减少打开关闭文件的次数。
流式写入方式适用于
大文件写入或者频繁写入的场景
,writeFile适用于写入频率较低的场景
。
7.readFile异步读取
1 | fs.readFile('./观书有感.txt',(err,data) => { |
8.readFileSync同步读取
1 | let data = fs.readFileSync('./观书有感.txt'); |
9.createReadStream流式读取
在绑定事件中,当从文件当中读取出来一块数据之后,就会执行一次回调,并把读取到的内容传递给形参chunk。这种方法相比其他读取方法,效率会更高。
1 | //创建读取流对象 |
10.文件移动和重命名
可以使用rename
或renameSync
来移动或重命名文件或文件夹
1 | //进行重命名,将座右铭.txt改为论语.txt |
11.删除文件
unlink
方法对应的同步方法是unlinkSync
;rm
方法对应的同步方法是reSync
。
1 | fs.unlink('./论语.txt', err=>{ |
4.2 文件夹操作
通过node,js可以对文件夹进行创建、读取、删除等操作
方法 | 说明 |
---|---|
mkdir/mkdirSync | 创建文件夹 |
readdir/readdirSync | 读取文件夹 |
rmdir/rmdirSync | 删除文件夹 |
1.创建文件夹mkdir
1 | //创建单个文件夹html |
2.读取文件夹readdir
1 | fs.readdir('./', (err, data) => { |
3.删除文件夹
1 | //删除单层文件夹 |
4.查看资源的状态
1 | fs.stat('./video.mp4', (err,data) => { |
5.’全局变量’__dirname
相对路径参照物是命令行的工作目录,所以有时候在代码中写相对路径时,会因为命令行的工作目录而导致编写出错。
__dirname
:保存的是所在文件的所有目录的绝对路径
1 | fs.writeFileSync(__dirname + '/index.txt', 'love'); //在当前文件下的index.txt文件中写入love |
5. path模块
path模块提供了操作路径的功能,下面是几个常用的API:
API | 说明 |
---|---|
path.resolve | 拼接规范的绝对路径 |
path.sep | 获取操作系统的路径分隔符 |
path.parse | 解析路径并返回对象 |
path.basename | 获取路径的基础名称 |
path.dirname | 获取路径的目录名 |
path.extname | 获取路径的扩展名 |
6. http模块
1.想要获取请求的数据,需要通过request对象
含义 | 语法 |
---|---|
请求方法 | request.method |
请求版本 | request.httpVersion |
请求路径 | request.url |
URL路径 | require(‘url’).parse(request.url).pathname |
URL查询字符串 | require(‘url’).parse(request.url,true).query |
请求头 | request.headers |
请求体 | request.on(‘data’,function(chunk{})) |
6.1 使用
1.用nodejs创建了一个HTTP服务端,在浏览器输入http://127.0.0.1:9000](http://127.0.0.1:9000/)后,回车,网页上会显示`Hello HTTP Server`。如果监听的端口为80的话,浏览器输入127.0.0.1即可
1 | //1.导入http模块 |
HTTP协议默认端口是80,HTTPS协议的默认端口是443,HTTP服务开发常用端口有3000、8080、8090和9000等
如果端口被其他程序占用,可以使用
资源监视器
找到占用端口的程序,然后使用任务管理器
关闭对应的程序。
2.获取请求体的方法:
1 | const http = require('http'); |
3.获取请求路径与查询字符串:
1 | const http = require('http'); |
案例:实现一个http的服务端,当url的路径是login时,访问的是登录界面;当url的路径是reg时,访问的是注册界面;当路径是其他时,访问的是Not Found界面。
1 | //1.导入http模块 |
4.设置响应报文:
1 | const http = require('http'); |
案例:创建一个HTTP服务端,返回给浏览器一个注册界面(用html写好的)
1 | //node.js部分的程序 |
1 | <!-- html部分的程序 --> |
效果:
补充:HTTP服务在哪个文件夹中寻找静态资源,哪个文件夹就是静态资源目录
,也称为网站根目录
。
5.设置资源类型(mime类型)
媒体类型是一种标准,用来表示文档、文件或字节流的性质和格式。
HTTP服务可以设置响应头Content-Type来表示响应体的MIME类型,浏览器会根据该类型决定如何处理资源。下面是常见文件对应的mime类型:
1 | html: 'text/html' |
对于未知的资源类型,可以选择
application/octet-stream
类型,浏览器在遇到该类型的响应时,会对响应体内容进行独立存储,也就是常见的下载
效果。
下面程序是设置响应体的MIME类型:
1 | const http = require('http'); |
6.2 GET和POST请求
1.使用情况
- GET请求的情况:
- 在地址栏直接输入url访问
- 点击a链接
- link标签引入css
- script标签引入css
- video与audio引入多媒体
- img标签引入图片
- form标签中的method为get(不区分大小写)
- ajax中的get请求
- POST请求的情况:
- form标签中的method为post(不区分大小写)
- ajax的post请求
2.GET和POST请求的区别
- 作用:GET主要用来获取数据,POST主要用来提交数据
- 参数位置:GET带参数请求一般是将参数缀到URL之后,POST带参数请求一般是将参数放到请求体中
- 安全性:POST请求相对GET安全一些,因为在浏览器中参数会暴露在地址栏
- GET请求大小有限制,一般为2K,而POST请求则没有大小限制
7. Node.js模块化
7.1 简介
模块化的概念:将一个复杂的程序文件依据一定规则(规范)拆分成多个文件的过程称之为模块化
。
模块的概念:对拆分出的每个文件就是一个模块,模块的内部数据是私有的,不过模块可以暴露内部数据以便其它模块使用。
模块化的好处:
- 防止命名冲突
- 高复用性
- 高维护性
7.2 操作
模块暴露数据的方式有两种:
- module.exports = value
- exports.name = value
注意:
- module.exports可以暴露
任意
数据- 不能使用
exports = value
的形式暴露数据,模块内部module与exports的隐式关系是exports = module.exports = {}
1.创建一个需要暴露的程序me.js
1 | //声明一个函数 |
引用的程序
1 | const me = require('./me.js'); |
2.导入模块
在模块中使用require传入文件路径即可引入文件:const test = require('./me.js');
require使用的一些注意事项:
- 对于自己创建的模块,导入时路径建议写
相对路径
,且不能省略./
和../
- js和json文件导入时可以不用写后缀,c/c++编写的node扩展文件也可以不用写后缀,但是一般用不到
- 如果导入其它类型的文件,会以js文件进行处理
- 如果导入的路径是个文件夹,则会首先检测该文件夹下package.json文件中的main属性对应的文件,如果存在则导入,反之如果文件不存在会报错。如果main属性不存在,或者package.json不存在,则会尝试导入文件夹下的index.js和index.json,如果还是没有找到,就会报错。
- 导入node.js内置模块时,直接require模块的名字即可,无需加
./
和../
。
8. 包管理工具
8.1 简介
包英文单词是package,代表了一组特定功能的源码集合
包管理工具:是管理包的应用软件,可以对包进行下载安装、更新、删除和上传等操作。借助包管理工具,可以快速开发项目,提升开发效率。包管理工具是一个通用的概念,很多编程语言都有包管理工具。
常用的包管理工具有:npm、yarm、cnpm
8.2 npm
npm是node.js官方内置的包管理工具,是必须要掌握的工具
初始化:创建一个空目录,然后以此目录作为工作目录启动命令行工具,执行npm init
。
npm init
命令的作用是将文件夹初始化为一个包,交互式创建package.json
文件,它是包的配置文件,每个包都必须要有package.json
。
1.属性解释
1 | { |
初始化过程中注意事项:
package name
不能使用中文、大写,默认值是文件夹的名称,所以文件夹名称也不能使用中文和大写version
要求x.x.x
的形式定义,x必须是数字,默认值是1.0.0
package.json
开源手动创建与修改- 使用
npm init -y
或者npm init --yes
极速创建package.json
2.生产环境与开发环境
开发环境是程序员专门用来写代码的环境,一般是指程序员的电脑,开发环境的项目一般只能程序员自己自己访问
生产环境是项目代码正式运行的环境,一般是指正式的服务器电脑,生产环境的项目一般每个客户都可以访问
3.生产依赖与开发依赖
可以在安装时设置选项来区分依赖的类型
,如:
类型 | 命令 | 补充 |
---|---|---|
生产依赖 | npm i -S uniq | -S等效于–save,-S是默认选项 |
开发依赖 | npm i -D less | -D等效于–save-dev |
其中生产依赖的包信息保存在package.json中dependencies
属性,开发依赖保存的包信息保存在package.json中的devDependencies
属性
生产依赖是指这个依赖即在开发阶段使用,也在最终的运行当中使用
开发依赖是指这个依赖只在开发阶段使用,过了开发阶段就没有用了
4.全局安装
上面介绍的是局部安装,即只能在打开终端的那个文件夹下使用。可以执行安装选项-g
进行全局安装,如npm i -g nodemon
,全局安装完成之后就可以在命令行的任何位置运行nodemon命令了。这个命令的作用是自动重启node应用程序
说明:
- 可以通过
npm root -g
查看全局安装包的位置 - 不是所有的包都适合全局安装,只有全局类的工具才适合,可以通过查看包的官方文档来确定安装方式。
5.安装包依赖
在项目协作中有一个常用的命令就是npm i
,通过该命令可以依据package.json
和package-lock.json
的依赖声明安装项目依赖
node_modules
文件夹大多数情况都不会存入版本库,因为当一个项目很大时,会用到很多的依赖包,而项目的依赖包都存在node_modules
文件夹中,如果将该项目上传到Git,其他人下载时就会很麻烦。所以就可以下载除了node_modules
文件夹的其它所有文件,然后通过npm i
来下载项目所需要的依赖。
6.安装指定的包和删除依赖
如果当前包的版本不匹配,或要安装指定版本的包,可以通过npm i <包名@版本号>
,如npm i jquery@1.11.2
删除依赖包可以使用:
- 局部删除:npm remove 包名或者npm r 包名
- 全局删除:npm remove -g 包名
7.配置命名别名
通过配置命名别名可以更简单的执行命令,如在package.json中的scripts属性中有如下配置:
1 | { |
配置完之后,可以使用别名执行命令,如:
1 | npm run server //执行的是node server.js |
不过start
别名比较特别,使用时可以省略,如npm start
8.3 npm配置淘宝镜像
用npm也可以使用淘宝镜像,配置的方式有两种
- 直接配置
- 工具配置
1.直接配置
执行如下命令即可完成配置
1 | npm config set registry https://registry.npmmirror.com/ |
2.工具配置(建议使用)
使用nrm配置npm的镜像地址npm registry manager
- 安装nrm:
npm i -g nrm
- 修改镜像:
nrm use taobao
- 检查是否配置成功(可选),如果是
https://registry.npmmirror.com/
,则表名成功:npm config list
补充:查看支持的镜像地址:nrm ls
然后就可以通过nrm use 要使用的镜像
来切换镜像地址了,如切换为官方地址nrm use npm
注意:淘宝镜像是只读的,只能下载,不能上传。比如说写好一个工具包,想要上传到npm去给其他人用,就需要切换为npm的官方地址才可以上传。
8.4 管理发布包
1.创建与发布
可以将自己开发的工具包发布到npm服务上,方便自己和其他开发者使用,操作步骤如下:
- 创建文件夹,并创建文件index.js,在文件中声明函数,使用module.exports暴露
- npm初始化工具包,package.json填写包的信息(包的名字是唯一的)
- 注册账号https://www.npmjs.com/signup
- 激活账号(必须)—–>填写验证码
- 修改为官方的官方镜像(命令行中运行nrm use npm)
- 命令行
npm login
填写相关用户信息 - 命令行下
npm publish
提交包
2.更新包
后续可以对自己发布的包进行更新,操作如下:
- 更新包中的代码
- 测试代码是否可用
- 修改package.json中的版本号(在原来基础上做++操作,相当于自己修改一次,也修改一次版本号)
- 发布更新
npm publish
3.删除包
执行命令npm unpublish --force
即可
删除包需要满足的条件:
- 要是包的作者
- 发布小于24小时
- 大于24小时后,没有其他包依赖,并且每周小于300下载量,并且只有一个维护者
4.nvm使用
下载地址:Releases · coreybutler/nvm-windows (github.com),选择nvm-setup.exe
下载即可
常用的命令:
命令 | 说明 |
---|---|
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 |
9. express框架
express是一个基于Node.js平台的极简、灵活的WEB应用开发框架,官方网址:https://www.expressjs.com.cn/
简单来说,express是一个封装好的工具包,本身是一个npm包,所以可以通过npm安装。express封装了很多功能,便于开发WEB应用(HTTP服务)。
9.1 操作
首先是先创建一个文件夹,在该文件夹下的终端执行`npm init’,先生成一个包,然后执行npm i express,在该目录下下载express工具包,这样在该目录下就可以使用该工具包了。如下是程序程序:
1 | //1.导入express |
1.express路由
路由确定了应用程序如何响应客户端对特定端点的请求。
一个路由的组成有请求方法
、路径
和回调函数
组成
1 | //1.get方法,路径为/home |
2.获取请求报文参数
1 | app.get('/request', (req, res) => { //req是对请求报文的一个封装对象;res是响应报文的一个封装对象 |
同时,为了更加的灵活运用,也可以通过占位符的方式来作为路径。在如下程序中,浏览器的路径只要是任意字符.html
都可以匹配成功路由。
1 | app.get('/:id.html', (req, res) => { //通过:id占位符来完成,浏览器只要是 |
3.响应设置
express框架封装了一些API来方便给客户端响应数据,并且兼容原生HTTP模块的获取方式
1 | app.get('/singer/:id.html', (req, res) => { //req是对请求报文的一个封装对象;res是响应报文的一个封装对象 |
9.2 express中间件
中间件本质是一个回调函数,中间件函数可以像路由回调一样访问请求对象(request),响应对象(response)。
中间件的作用:就是使用函数封装公共操作,简化代码
中间件的类型:
- 全局中间件:就是指一个请求发过来后,这个全局中间件就会执行,执行完毕后才去执行路由回调。(每一个请求过来,全局中间件一定会执行)
- 路由中间件:就是指一个请求发过来后,路由中间件是和路由放在一起的,只有满足了某一个路由规则,它所对应的中间件才会去执行。
1.全局中间件实现:当客户端通过浏览器访问该服务器时,会对应的记录它的url和ip,然后保保存到一个文件中。
1 | //1.导入模块 |
2.路由中间件实现:当客户端通过浏览器发送请求后,先通过url来匹配执行哪个路由,当匹配完后,要执行中间件里面的代码,即判断有无携带code=520
,如果有,就执行对应的路由回调;如果没有携带,就回应一个暗号错误。(防盗链可以用类似的方法实现)
1 | //1.导入模块 |
9.3 处理静态资源的中间件
使用注意事项:
- index.html文件为默认打开的资源
- 如果静态资源与路由规则同时都匹配,谁先匹配,谁就响应
- 路由响应动态资源,静态资源中间件响应静态资源
当添加了静态资源中间件设置,再浏览器发送请求时,如果路径声明没有写(即/),服务端这边会先找index.html页面,即到public这个静态目录里面去找,找到了就读取内容,响应给浏览器。但如果路由规则也设置了/,那么谁先匹配上,就执行谁(执行顺序从上到下)。
1 | //1.导入模块 |
比如说对应的public下有一个index.html文件,如下所示:
1 |
|
执行效果:
9.4 EJS模板引擎
模板引擎是分离用户界面和业务数据
的一种技术,是一个高效的Javascript的模板引擎。
1.ejs普通渲染
创建一个文件夹,在文件夹的终端下执行npm i ejs
,这样在该目录下就可以使用ejs工具包了。
先创建一个2-lxx.html文件,这里只是简单写了一行代码
1 | <h2>我爱你 <%= china %></h2> |
然后再创建一个js文件,来完成跨文件渲染
1 | //导入模块 |
2.ejs条件渲染
1 | //导入模块 |
9.5 Express应用程序生成器
通过应用生成器工具express-generator可以快速创建一个应用的骨架。可以通过npx命令来运行Express应用程序生成器。
全局安装这个包:npm install -g express-generator
。
创建骨架:express -e generator
,-e表示添加ejs模板引擎的支持,generator表示文件夹的名称(将代码装到哪个文件夹下)。
在生成骨架的文件夹下,安装依赖:npm i
启动服务端:npm start
9.6 案例实践-记账本
首先是创建一个文件夹,在该文件夹下创建一个应用裤架:express -e accounts
在accounts文件夹下安装依赖:npm i
需要用到一个小型的数据库lowdb:npm i lowdb@1.0.0
对每一个订单进行编号id,安装shortid:npm i shortid
10. API接口
10.1 简介
接口是前后端通信的桥梁,可以理解为一个接口就是服务中的一个路由规则,根据请求响应结果。这里所指的接口是数据接口
,与编程语言(java、go等)中的接口语法不同。
接口的作用:实现前后端通信
接口的组成:请求方法、接口地址(URL)、请求参数、响应结果
10.2 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 | 返回单个歌曲信息 |
10.3 json-server
json-server本身是一个JS编写的工具包,可以快速搭建RESTful API服务
搭建临时的接口服务操作步骤:
1.全局安装json-server
1 | npm i -g json-server |
2.创建JSON文件(db.json),编写基本结构如下:
1 | { |
3.以JSON
文件所在文件夹作为工作目录,执行如下命令:
1 | json-server --watch db.json |
完成过后,客户端(浏览器)就可以通过http://127.0.0.1:3000/
后面加上路径来访问对应的数据了。
11. 会话控制
11.1 简介
所谓会话控制就是对会话进行控制。HTTP是一种无状态的协议,它没有办法区分多次的请求是否来自于同一个客户端,无法区分用户,所以可以通过会话控制
来解决该问题。
常见的会话控制技术有三种:
- cookie
- session
- token
11.2 cookie
cookie是HTTP服务器发送到用户浏览器并保存在本地的一小块数据,简单来说:
- cookie是保存在浏览器端的一小块数据
- cookie是按照域名划分保存的
示例:
域名 | cookie |
---|---|
www.baidu.com | a=100;b=200 |
www.bilibili.com | xid=1020abce121;hm=112411213 |
jd.com | x=100;ocw=12414cce |
1.特点
浏览器向服务器发送请求时,会自动将当前域名
下可用的cookie设置在请求头中,然后传递给服务器。这个请求头的名字也叫cookie,所以将cookie理解为一个HTTP的请求头也是可以的。
2.cookie运行流程
浏览器这边会先把我们的账号密码等信息传递给服务器,服务器就会返回一个属于我们的cookie。注意的是,服务器是通过响应报文里面响应头为set-cookie传递给我们的。
当服务器返回一个我们的cookie后,浏览器就会将set-cookie后面的内容进行一个存储,于是就将cookie信息保存在我们当前这个域名的cookie下面。
当保存完毕之后,浏览器下次再向该服务器发送请求时,就会自动携带这个域名下面的cookie,然后传递给服务器。此时服务器就可以通过对这个信息的解析来得知请求的一个发送者,所以在返回结果时,就可以通过对应的发送者来响应对应的内容,这样就实现了用户的识别,实现了会话控制。
11.3 express框架使用cookie
在一个文件夹下的终端通过npm init
创建一个包的配置文件,再在该目录下通过npm i express
安装express工具包。
1.设置和删除cookie
1 | //导入模块 |
2.获取cookie
如果要在express框架中获取cookie,还需要通过npm i cookie-parser
来下载工具包cookie-parser。
1 | //导入模块 |
11.4 session
session是保存在服务器端的一块数据
,保存当前访问用户的相关消息。它的作用是实现会话控制,可以识别用户的身份,快速获取当前用户的相关消息。
1.session的运行流程
- 用户提供自己的账号和密码传递给服务器,服务器接收到后会对其进行校验,检查填写的内容是否正确、与数据库里面的填写的内容是否匹配。如果填写没有问题,服务器会为当前的访问者创建一个对象,可以理解为是session对象。在这个对象里面会保存当前用户的一些基本信息,如用户名、用户id、用户邮箱等。除此之外,服务器端还会在这个对象当中生成一个独一无二的id标识,即session_id。
- 有了这个session对象之后,一方面服务器会将该对象存到session对象池里面(每个用户有了session对象之后都会被存到服务器那边的session对象池里面),一方面会将这个id以响应cookie的形式返回给浏览器,浏览器接收到后就会将这个cookie信息保存起来。
- 有了cookie之后,以后再向服务器发送请求时,它就会带着cookie向服务器发送请求。服务器接收到该请求后,服务器就会从cookie里面把这个session_id取出来,取出来后就可以到session对象池里面去匹配寻找,即在存放session对象的容器里面去找,如果找到了跟这个session_id匹配的数据,就知道当前的用户是谁了。
11.5 express框架使用session
在一个文件夹下的终端通过npm init
创建一个包的配置文件,再在该目录下通过npm i express
安装express工具包。
然后还需要安装两个工具包,终端执行命令:npm i express-session connect-mongo
。
下面程序是session在express框架下的使用,同时结合了mongodb数据库来辅助观察存储的数据。
1 | //导入模块express |
11.6 session和cookie的区别
cookie和session的区别主要在于如下几点:
所在的位置
- cookie:浏览器端
- session:服务端
安全性
- cookie是以明文的方式存放在客户端的,安全性相对较低
- session存放于服务器中,所以安全性
相对
较好
网络传输量
- cookie设置的内容过多会增大报文体积,会影响传输效率
- session数据存储在服务器,只是通过cookie传递id,所以不影响传输效率
存储限制
- 浏览器限制单个cookie保存的数据不能超过4k,且单个域名下的存储数量也有限制
- session数据存储在服务器中,所以没有这些限制
11.7 token
token
是服务端生成并返回给HTTP客户端的一串加密字符串,token
中保存着用户信息。它的作用是实现会话控制,可以识别用户的身份,主要用于移动端APP。
1.token的工作流程
- 客户端将账号和密码通过请求发送给服务器,服务端检测了所提交的信息之后,如果没有问题,就会创建出token。创建好后,就会将该信息返回给客户端。
- 客户端收到服务器返回的token信息后,下次再发送请求时,客户端就会带着token传递给服务器,服务器就会对传来的token做校验,并且从token里面提取出用户的信息,继而识别用户的身份。
可以发现,token和cookie很像,cookie也是服务端校验用户的信息以后,将cookie返回给客户端,下次客户端发送请求时,再带着cookie给服务器。但它们之间也还是有一些区别的,cookie是自动携带的,而token是手动携带,比如说客户端给服务器发请求了,它得自己将token放在请求报文里面,然后再向服务器发请求。但cookie不是,当客户端要发送请求给服务器时,浏览器就会自动的将信息放在请求报文里面传递给服务器。
2.token的特点
服务端压力更小
- 数据存储在客户端
相对更安全
- 数据加密
- 可以避免CSRF(跨站请求伪造)
扩展性更强
- 服务间可以共享
- 增加服务节点更简单
3.JWT
JWT是目前最流行的跨域认证解决方案,可用于基于token的身份验证。JWT是token的生成于校验更规范。
使用:在文件夹的终端通过npm init
创建一个包的配置文件,再通过npm i jsonwebtoken
下载jsonwebtoken工具包。
创建出一个token:
1 | //导入模块 |
得到的结果为:
1 | eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImxpeHgiLCJpYXQiOjE3MjY0NzMzNzcsImV4cCI6MTcyNjQ3MzQzN30.mrmErdIPfWz7lLEdZRXSQdV7F9HrtYJVEuEwFSzmad0 |
验证token:
1 | let t = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImxpeHgiLCJpYXQiOjE3MjY0NzMzNzcsImV4cCI6MTcyNjQ3MzQzN30.mrmErdIPfWz7lLEdZRXSQdV7F9HrtYJVEuEwFSzmad0'; |
如果在指定的token生命周期里面执行上面的验证代码,都会出现如下信息:
1 | { username: 'lixx', iat: 1726473377, exp: 1726473437 } |