目录
一、基础
1.1 Node.js简介
Node.js 是一个基于 Chrome V8 引擎的 JavaScript 的后端运行环境,是单进程单线程应用程序;
Node.js 中无法调用 DOM 和 BOM 等浏览器内置 API。
官网:Node.js
基本使用方法:
查看是否安装成功:终端输入命令 node –v 查看 Node.js 版本号。
执行js文件:切换到js文件目录 → 终端输入 node 文件路径 。
Tips:终端中的快捷键:
↑ 键 ,快速定位上一次执行的命令
tab 键,快速补全路径
esc 键,快速清空当前已输入的命令
cls ,清空终端
ctrl+c 键,终止服务器
1.2 nvm
nvm:nodejs的版本管理工具。
nvm安装包下载地址:Releases · coreybutler/nvm-windows · GitHub。
注意:新建nvm和nodejs文件夹,路径尽量不要出现中文,双击安装包安装。
nvm的基本使用:
nvm off // 禁用node.js版本管理(不卸载任何东西)
nvm on // 启用node.js版本管理
nvm install <version> // 安装node.js的命名 version是版本号 例如:nvm install 8.12.0
nvm uninstall <version> // 卸载node.js是的命令,卸载指定版本的nodejs,当安装失败时卸载使用
nvm ls // 显示所有安装的node.js版本
nvm list available // 显示可以安装的所有node.js的版本
nvm use <version> // 切换到使用指定的nodejs版本
nvm v // 显示nvm版本
nvm install stable // 安装最新稳定版
1.3 npm与包
包:Node.js 中的第三方模块又叫做包。
npm:Node Package Manager 简称 npm 包管理工具 ,安装node自带npm,npm社区官网:npm | Home。
npm基本使用:
① 初始化清单文件 :npm init -y(得到 package.json 文件,有则略过此命令)
② 下载包 :
下载项目依赖包: npm i 软件包名称
下载全局包: npm i 软件包名称 -g
下载指定版本包:npm i 软件包名称@版本号
Tips:npm install 可简写 npm i
解决下载包慢的问题:配置 npm 淘宝镜像
npm config set registry http://registry.npm.taobao.org
③ 常用命令:
npm -v // 查看 npm 版本号
npm i 包的完整名称 // 在项目中安装最新版本的包
npm i 包的完整名称@版本号 // 在项目中安装指定版本的包,会覆盖之前下载的包
npm uni 包名 // 卸载指定包
npm i 包名 -D // 安装指定包,并记录到devDependdencies节点中
npm i 软件包名 -g // 下载全局软件包
npm run 自定义命令 // 运行自定义命令
包的分类:
① 项目包:项目执行所依赖;
② 全局包:在执行 npm install 命令时,如果提供了 -g 参数,则会把包安装为全局包,只有工具性质的包,才有全局安装的必要性。因为它们提供了好用的终端命令。
全局包 默认安装位置: C:\Users\xxx\AppData\Roaming\npm\node_modules,如果使用nvm方式安装的nodejs,默认安装位置为:xxx/nodejs/node_modules,此nodejs文件夹与nvm处于同一个文件夹下。
文件目录介绍:
node_modules 文件夹:存放所有已安装到项目中的包。
package-lock.json 配置文件:记录 node_modules 下包的下载信息。
package.json :包管理配置文件。
Tips:关于 package.json
package.json 中必须包含 name,version,main 这三个属性,分别代表包的名字、版本号、包的入口;
dependencies(生产环境) :用来记录使用 npm install 命令安装了哪些包;
devDependencies (开发环境):如果某些包只在项目开发阶段会用到,在项目上线之后不会用到,则建议把这些包记录到 devDependencies 节点中。
解决下包慢的问题:添加淘宝镜像,具体操作不赘述。
Tips:npm i 中 -S, -D,-g 的区别
-S 等同于--save,保存在package.json文件中,是在dependencies 下,生产环境,默认可省略;
-D 等同于--save-dev,也保存在package.json文件中,是在devDependencies下,本地开发环境;
-g 等同于--global的简写,对模块进行全局安装,作用于全局环境下。
1.4 npx
npx是一个由Node.js官方提供的用于快速执行npm包中的可执行文件的工具。它可以帮助我们在不全局安装某些包的情况下,直接运行该包提供的命令行工具。npx会在执行时,检查本地项目中是否安装了对应的依赖,如果没有安装则会自动下载安装,并执行命令。如果本地已经存在该依赖,则直接执行命令。
npx 不会像 npm 或 yarn 一样将包下载到本地的 node_modules 目录中。
eg: npx nodemon xxx
1.5 其他包管理器
1.5.1 yarn
yarn 是一个新的包管理工具,yarn 弥补了 npm 的一些缺陷,执行速度更快。yarn 默认有一个 yarn.lock 文件锁定版本,保证环境统一。
安装 yarn:npm install -g yarn
yarn 的常用命令:
yarn -v // 查看版本
yarn add 包名@版本 // 安装指定版本的包
yarn global add 包名 // 安装全局包
yarn upgrade 包名 // 升级指定包
yarn remove 包名 // 移除指定包
yarn 自定义命令 // 执行自定义命令
1.5.2 pnpm
一优势:比同类工具快2倍左右、节省磁盘空间
安装:npm install -g pnpm
用法与yarn类似
二、模块化
2.1 概念
Node.js 遵循了 CommonJS 模块化规范,CommonJS 规定了模块的特性和各模块之间如何相互依赖。原生JavaScript遵循 ECMAScript 模块化规范。
Tips:Node.js 默认支持 CommonJS 标准语法如需使用 ECMAScript 标准语法,在运行模块所在文件夹新建 package.json 文件,并设置 { "type" : "module" }
2.2 加载模块
// 1.加载内置模块
const fs = require("fs");
// 2.加载自定义模块
const custom = require("./custom.js");
// 3.加载第三方包
const express = require("express");
模块加载机制:
2.3 向外暴露成员
语法:
module.exports.变量 = { }
module.exports = { }
exports.变量
注意:如果要对外暴露属性或方法,就用 exports 就行,要暴露对象(类似class,包含了很多属性和方法),就用 module.exports。
三、内置模块
3.1 fs 模块
概念:fs 模块是 Node.js 官方提供的、用来操作文件的模块。
语法:
① const fs = require('fs'):加载 fs 模块对象 。
② fs.writeFile(file,data[,options],callback) :向指定的文件写入内容。
参数1:必选参数,需要指定一个文件路径的字符串,表示文件的存放路径。
参数2:必选参数,表示要写入的内容。
参数3:可选参数,表示以什么格式写入文件内容,默认值是 utf8。
参数4:必选参数,文件写入完成后的回调函数。
③ fs.readFile(path,[,options],callback) :读取指定文件中的内容,异步读取;
fs.readFileSync() 同步读取。
参数1:必选参数,字符串,表示文件的路径。
参数2:可选参数,表示以什么编码格式来读取文件。
参数3:必选参数,文件读取完成后,通过回调函数拿到读取的结果。
// 1.加载 fs 模块
const fs = require("fs");
// 2.写入文件内容
fs.writeFile('./files/1.txt', 'hello node', function(err) {
if (err) {
return console.log('文件写入失败!' + err.message)
}
console.log('文件写入成功!')
})
// 3.读取文件内容
fs.readFile('./files/1.txt', 'utf8', function(err, dataStr) {
if (err) {
return console.log('读取文件失败!' + err.message)
}
console.log('读取文件成功!' + dataStr)
})
3.2 path 模块
概念:path 模块是 Node.js 官方提供的、用来处理路径的模块。
__dirname:内置变量,表示当前文件所处的目录。
语法:
① const path = require('path'): 加载path模块。
② path.join(__dirname,'相对路径'):拼接路径。
③ path.basename(path[,ext ]) :获取路径中的文件名。
path <string> 必选参数,表示一个路径的字符串;
ext <string> 可选参数,表示文件扩展名;
返回: <string> 表示路径中的最后一部分。
④ path.extname(path) :获取路径中的扩展名。
const fs = require("fs");
// 1.加载 path 模块
const path = require("path");
// 2.调用 path.join() 配合 __dirname 组成文件的绝对路径
fs.readFile(path.join(__dirname, './files/1.txt'), 'utf8', function(err, dataStr) {
if (err) {
return console.log(err.message)
}
console.log(dataStr)
})
3.3 http 模块
概念:http 模块是 Node.js 官方提供的、用来创建 web 服务器的模块。
使用 http 模块 创建一个基本的服务器:
① 导入http模块:const http = require('http');
② 创建 web 服务器实例:调用 http.createServer() 方法;
③ 为服务器实例绑定 request 事件,监听客户端的请求;
④ 启动服务器;
⑤ 运行 node命令执行文件。
// 1. 导入 http 模块
const http = require('http')
// 2. 创建 web 服务器实例
const server = http.createServer()
// 3. 为服务器实例绑定 request 事件,监听客户端的请求
server.on('request', function (req, res) {
console.log('Someone visit our web server.')
})
// 4. 启动服务器
server.listen(8080, function () {
console.log('server running at http://127.0.0.1:8080')
})
//tops:8080端口号,http://127.0.0.1:本地ip地址
Tips:
IP地址和域名是一 一对应的关系;
127.0.0.1 对应的域名是 localhost,都代表本电脑;
也可以指定局域网的IPv4地址进行访问
端口号:标记服务器里不同功能的服务程序;
端口号范围:0-65535 之间的任意整数,http 协议默认访问 80 端口(80可省略)。
跨域:协议名与域名、端口号不同;
跨站:顶级域名与二级域名不同。
四、Express
4.1 基础
概念:Express 是基于 Node.js 平台,快速、开放、极简的 Web 开发框架。本质是一个 npm 上的第三方包,提供了快速创建 Web 服务器的便捷方法,官网:Express - 基于 Node.js 平台的 web 应用开发框架 - Express中文文档 | Express中文网
安装:npm i express@4.17.1 (这里安装4.17.1版本)
nodemon:
作用:监听项目文件的变动,当代码被修改后,nodemon 会自动帮我们重启项目
安装:npm i -g nodemon
使用:nodemon 文件路径 或 npx nodemon 文件路径
4.2 使用方法
// 1. 导入 express
const express = require('express')
// 2. 创建 web 服务器
const app = express()
const port = 8080 //端口号
const ip = "127.0.0.1" //本地主机IP地址,也可以使用局域网的IPv4地址
// 4. 监听客户端的 GET 和 POST 请求,并向客户端响应具体的内容
app.get('/user', (req, res) => {
// 调用 express 提供的 res.send() 方法,向客户端响应一个 JSON 对象
res.send({ name: 'zs', age: 20, gender: '男' })
})
app.post('/user', (req, res) => {
// 调用 express 提供的 res.send() 方法,向客户端响应一个 文本字符串
res.send('请求成功')
})
app.get('/', (req, res) => {
// 通过 req.query 可以获取到客户端发送过来的 查询参数
// 注意:默认情况下,req.query 是一个空对象
console.log(req.query)
res.send(req.query)
})
// 注意:这里的 :id 是一个动态的参数
app.get('/user/:ids/:username', (req, res) => {
// req.params 是动态匹配到的 URL 参数,默认也是一个空对象
console.log(req.params)
res.send(req.params)
})
// 3. 启动 web 服务器
app.listen(port,ip, () => {
console.log('express server running at http://127.0.0.1')
})
4.3 req 与 res
Tips:
// req 常用
req.query; //获取客户端通过查询字符串(拼接到url的数据)发送到服务器的数据(一般为get请求)
req.body; //通常用来获取POST请求中的数据
req.params; // 获取 URL 中,通过 : 匹配到的动态参数
// res 常用
res.send(); //把处理好的内容,发送给客户端
4.4 静态资源托管
概念:创建一个静态资源服务器,文件对外开放访问。
语法:app.use( [url,]express.static("文件夹相对路径") )
注意:如果要托管多个静态资源目录,请多次调用 express.static() 函数。
4.5 Router
概念:路由(Router)指的是客户端的请求与服务器处理函数之间的映射关系。
组成:请求类型、请求 URL 地址、处理函数:app.METHOD(PATH,HANDLER)
基本使用:
const express = require('express')
const app = express()
// 挂载路由
app.get('/', (req, res) => {
res.send('hello world.')
})
app.post('/', (req, res) => {
res.send('Post Request.')
})
app.listen(80, () => {
console.log('http://127.0.0.1')
})
模块化路由的使用方法:
① 创建路由模块对应的 router.js 文件;
② 调用 express.Router() 函数创建路由对象;
③ 向路由对象上挂载具体的路由;
④ 使用 module.exports 向外共享路由对象;
⑤ 使用 app.use() 函数注册路由模块;
⑥ 为路由模块添加前缀:与托管静态资源一样。
eg:
router.js
// 这是路由模块
// 1. 导入 express
const express = require('express')
// 2. 创建路由对象
const router = express.Router()
// 3. 挂载具体的路由
router.get('/user/list', (req, res) => {
res.send('Get user list.')
})
router.post('/user/add', (req, res) => {
res.send('Add new user.')
})
// 4. 向外导出路由对象
module.exports = router
app.js
const express = require('express')
const app = express()
// 1. 导入路由模块
const router = require('./03.router')
// 2. 注册路由模块
app.use('/api', router)
app.listen(80, () => {
console.log('http://127.0.0.1')
})
4.6 CORS
接口的跨域问题: GET 和 POST接口不支持跨域请求。
解决方案:CORS(主流解决方案)
使用方法:
① 安装中间件:npm i cors;
② 导入中间件: const cors = require('cors');
③ 在 路由 之前调用 app.use(cors()) 配置中间件。
app.js
// 导入 cors 中间件
const cors = require("cors");
// 将 cors 注册为全局中间件 在 路由 之前调用!!!!!
app.use(cors());
4.7 接口编写
const express = require('express')
const router = express.Router()
// 在这里挂载对应的路由
// 不区别请求方法,'/*'无匹配路由时返回404
router.all('/*',(req, res) =>{
res.send(404)
})
// 定义 GET 接口
router.get('/get', (req, res) => {
// 通过 req.query 获取客户端通过查询字符串,发送到服务器的数据
const query = req.query
// 调用 res.send() 方法,向客户端响应处理的结果
res.send({
status: 0, // 0 表示处理成功,1 表示处理失败
msg: 'GET 请求成功!', // 状态的描述
data: query, // 需要响应给客户端的数据
})
})
// 定义 POST 接口
router.post('/post', (req, res) => {
// 通过 req.body 获取请求体中包含的 url-encoded 格式的数据
const body = req.body
// 调用 res.send() 方法,向客户端响应结果
res.send({
status: 0,
msg: 'POST 请求成功!',
data: body,
})
})
// 定义 PUT 接口
router.put('/put', (req, res) => {
res.send({
status: 0,
msg: 'PUT请求成功',
})
})
// 定义 DELETE 接口
router.delete('/delete', (req, res) => {
res.send({
status: 0,
msg: 'DELETE请求成功',
})
})
module.exports = router
4.8 中间件
4.8.1 基础
概念:指业务流程的中间处理环节。
作用:多个中间件之间,共享同一份 req 和 res。基于这样的特性,我们可以在上游的中间件中,统一为 req 或 res 对象添加自定义的属性或方法,供下游的中间件或路由进行使用
格式:Express 的中间件,本质上就是一个 function 处理函数,中间件函数的形参列表中,必须包含 next 参数。而路由处理函数中只包含 req 和 res。next 函数的作用:next 函数是实现多个中间件连续调用的关键,它表示把流转关系转交给下一个中间件或路由。
4.8.2 中间件的分类:
① 全局中间件:
客户端发起的任何请求,到达服务器之后,都会触发的中间件;
通过调用 app.use(中间件函数),即可定义一个全局生效的中间件;
可使用 app.use() 连续定义多个全局中间件。客户端请求到达服务器之后,会按照中间件定义的先后顺序依次进行调用。
注意:除了错误级别的中间件,其他的中间件,必须在路由之前进行配置 ! ! !
const express = require('express')
const app = express()
// 定义第一个全局中间件
app.use((req, res, next) => {
console.log('调用了第1个全局中间件')
next()
})
// 定义第二个全局中间件
app.use((req, res, next) => {
console.log('调用了第2个全局中间件')
next()
})
// 定义一个路由
app.get('/user', (req, res) => {
res.send('User page.')
})
app.listen(80, () => {
console.log('http://127.0.0.1')
})
②局部中间件:
不使用 app.use() 定义,只在当前路由生效的中间件。
const express = require('express')
const app = express()
// 1. 定义中间件函数
const mw1 = (req, res, next) => {
console.log('调用了局部生效的中间件1')
next()
}
const mw2 = (req, res, next) => {
console.log('调用了局部生效的中间件2')
next()
}
// 2. 创建路由
app.get('/', mw1,mw2, (req, res) => {
res.send('Home page.')
})
// 也可以写成数组
app.get('/home', [ mw1,mw2 ], (req, res) => {
res.send('Home page.')
})
app.listen(80, function () {
console.log('Express server running at http://127.0.0.1')
})
4.8.3 自定义中间件
const express = require("express");
const app = express();
// 在路由之前,封装向客户端响应 处理失败 的结果,res.cc()
app.use((req, res, next) => {
// status 默认值为1,表示失败的情况
// err 的值可能是一个错误对象,也可能是字符串
res.cc = function (err, status = 1) {
res.send({
status,
message: err instanceof Error ? err.message : err,
});
};
next();
});
//导入使用路由模块 注意路由位置
const router = express.Router()
app.use('/api',router)
// 定义错误级别的中间件 路由之后
app.use((err, req, res, next) => {
if (err instanceof xxx) {
return res.cc(err);
}
// 未知的错误
res.cc(err);
});
app.listen(8080, function () {
console.log("api server running at http://127.0.0.1:8080");
});
4.8.4 常用内置中间件
const express = require('express')
const app = express()
//快速托管静态资源的内置中间件
app.use(express.static('./home'))
// 解析表单中的 JSON 格式的数据
app.use(express.json())
// 解析表单中的application/x-www-form-urlencoded格式的数据
app.use(express.urlencoded({ extended: false }))
app.post('/login', (req, res) => {
// 在服务器端,可以通过 req.body 来获取 JSON 格式的表单数据和 url-encoded 格式的数据
console.log(req.body)
res.send('ok')
})
app.listen(80, function () {
console.log('Express server running at http://127.0.0.1')
})
4.9 express-generato
4.9.1 概念:通过应用生成器工具 express-generator
可以快速创建一个应用的骨架。
4.9.2 实现步骤:
① 安装express-generator
全局包:
npm install -g express-generator
查看所有可执行命令:
express -h
② 创建项目文件夹并进入,终端输入命令: express ,自动生成应用骨架。
eg:创建了一个名称为 myapp 的 Express 应用。此应用将在当前目录下的 myapp 目录中创建,并且设置为使用 Pug 模板引擎(view engine)
express --view=pug myapp
③ 进入项目文件夹并安装所有依赖包
npm install
④ 运行项目(不同操作系统命令查看官网,这里是win指令)
npm start
然后在浏览器中打开 http://localhost:3000/
网址就可以看到这个应用了 。
4.9.3 文档目录简介:
.
├── app.js // 应用核心配置文件
├── bin // 可执行文件目录
│ └── www
├── package.json // 项目依赖配置及开发者信息
├── public // 静态文件根目录,存放图片、脚本、样式等文件
│ ├── images
│ ├── javascripts
│ └── stylesheets
│ └── style.css
├── routes // 路由模块目录
│ ├── index.js
│ └── users.js
└── views // 视图目录,用于存储所有的ejs模板
├── error.pug
├── index.pug
└── layout.pug
Tips: package.json中的scripts属性是用于定义操作命令,比如默认的start,用 npm start 代表执行 node ./bin/www 命令,如果安装了 nodemon ,修改指令 node ./bin/www → nodemon ./bin/www ,可实现保存文件自动开启服务。
目录详细可参考大佬写的文章 → Node.js开发框架Express4.x | 粉丝日志
五、基于 express 案例
5.1 文件上传与下载
基于第三方包 formidable 实现表单 form-data 格式文件的上传。
formidable:最常用、最灵活、最快速的多部分表单数据流式解析器。
5.1.1 文件上传
使用 formidable(3.5.1) 实现文件上传
const express = require("express");
// 导入 formidable 中 IncomingForm 模块 "formidable": "^3.5.1"
const { IncomingForm } = require("formidable");
const router = express.Router();
// formidable 配置项
const options = {
encoding: "utf-8", //设置表单域的字符集
uploadDir: "public/upload", //设置上传目录文件夹
keepExtensions: true, //保留上传文件的后缀
multiples: true, //支持多文本上传
};
router.post("/", (req, res, next) => {
// 创建form对象接收客户端的formdata中的数据:使用formidable模块的IncomingForm
let form = new IncomingForm(options);
// 解析客户端的formdata中的数据并返回结果
form.parse(req, (err, fields, files) => {
if (err) {
return next(err);
}
res.send({ fields, files, meta: { msg: "上传成功", status: 200 } });
});
});
module.exports = router;
关于 formidable 其他配置项可查看官网文档。也可以使用 multer:GitHub - expressjs/multer 实现文件上传功能。
5.1.2 文件的下载
后端部分:路由模块
const express = require("express");
const router = express.Router();
const path = require("path");
// 动态路由
router.get("/download/:file(*)", function (req, res, next) {
// 文件的绝对路径 = path.join(当前文件路径, 目标文件文件夹, 前端提供的文件相对路径)
let _path = path.join(__dirname, "files", req.params.file);
res.download(_path, function (err) {
// 200状态
if (!err) {
return;
}
// 500状态
if (err.status !== 404) {
return next(err);
}
// 404状态
res.statusCode = 404;
res.send("没有这个文件!");
});
});
module.exports = router;
前端部分:点击 a 标签下载 功能
<a href="/download/文件.txt">某文件</a>
<-- 注意:href 属性值= /download + 对应路由模块路径关系 -->
六、Node.js中的MySQL
安装,配置数据库不将赘述
5.1 MySQL 基本使用
在终端中操作(需管理员权限):
卸载数据库:sc delete MySQL80
启动服务:net start MySQL80
停止服务:net stop MySQL80
登录数据库:mysql -u 数据库用户名 -p
输入正确密码即可进入mysql命令行;注意:mysql命令行 “ ; ” 表示命令结束 ,建议使用反引号 ` 避免与关键字冲突。
修改数据库密码:ALTER USER '数据库用户名'@'localhost' IDENTIFIED WITH mysql_native_password BY '新密码';
删除tests数据库:drop database tests;
创建tests数据库:create database tests;
查看数据库:show databases;
使用tests数据库:use tests;
创建test表:create table `test`(字段名+数据类型+字段属性+注释);
查看库中表:show tables;
查看test表结构:desc tests;
删除test表:drop table test;
查看创建表语句:show create table test\G
常用字段属性:
字段属性(关键字) | 释义 |
NULL | 数据列可包含NULL值 |
NOT NULL | 数据列不允许包含NULL值 |
DEFAULT | 默认值 |
PRIMARY KEY | 主键 |
FOREIGN KEY | 外键,是当前表中的字段和另外表的主键字段直接的对应关系 |
AUTO_INCREMENT | 自动递增,适用于整数类型 |
UNSIGNED | 无符号 |
CHARACTER SET 字符集 | 指定一个字符集 |
常用数据类型:
整数类型 | 字节 | 有符号数取值范围 | 无符号数取值范围 |
---|---|---|---|
TINYINT | 1 | -128~127 | 0~255 |
SMALLINT | 2 | -32768~32767 | 0~65535 |
MEDIUMINT | 3 | -8388608~8388607 | 0~16777215 |
INT、INTEGER | 4 | -2147483648~2147483647 | 0~4294967295 |
BIGINT | 8 | -9223372036854775808~9223372036854775807 | 0~18446744073709551615 |
时间 | 名称 | 日期格式 | 最大值 |
YEAR | 年 | YYYY或YY | 2155 |
TIME | 时间 | HH:MM:SS | 838:59:59 |
DATE | 日期 | YYYY-MM-DD | 9999-12-03 |
DATETIME | 日期时间 | YYYY-MM-DD HH:MM:SS | 9999-12-31 23:59:59 |
TIMESTAMP | 日期 时间 | YYYY-MM-DD HH:MM:SS | 2038-01-19 03:14:07UTC |
文本类型 | 长度 | 特点 | 长度范围 |
CHAR(M) | M | 固定长度 | 0 <= M <= 255 |
VARCHAR(M) | M | 可变长度 | 0 <= M <= 65535 |
TINYTEXT | L | 小文本、可变长度 | 0 <= L <= 255 |
TEXT | L | 文本、可变长度 | 0 <= L <= 65535 |
MEDIUMTEXT | L | 中等文本、可变长度 | 0 <= L <= 16777215 |
LONGTEXT | L | 大文本、可变长度 | 0 <= L<= 4294967295 |
5.2 SQL语句
Tips:sql语句不区分大小写。
5.2.1 查询数据
语法①: SELECT * FROM 表名称
释义:从FROM指定的【表中】,查询出【所有的】数据。*表示【所有列】
语法②:SELECT 列表名 FROM 表名称
释义:从FROM指定的【表中】,查询出指定列名称(字段)的数据
-- 通过 * 把 test 表中所有的数据查询出来
select * from test
-- 从test表中把username和password对应的数据查询出来
select username,password from test
5.2.2 插入数据
语法:INSERT INTO table_name (列1,列2,...) VALUES (值1,值2,...)
释义:向指定表中,插入几列数据,列的值通过values 一 一 指定,值用逗号(,)隔开可批量添加。
-- 向test表中,插入新数据,username的值为tony stark password的值为098123
insert into test (username,password) values ('tony stark', '098123')
5.2.3 更新数据
语法:UPDATE 表名称 SET 列名称=新值 WHERE 列名称=某值
释义:用update指定要更新哪个表中的数据,用set指定列对应的新值,用where指定更新的条件。
-- 将id为3的用户密码,更改为88888888
update test set password='88888888' where id=3
-- 更新 id为2的用户,把密码更新为 admin123 同时,把用户状态更新为1
update test set password='admin123',status=1 where id=1
WHERE:WHERE 子句用于限定选择的标准。在 SELECT、UPDATE、DELETE 语句中,皆可使用 WHERE 子句来限定选择的标准;
5.2.4 删除数据
① DELETE 删除表中的所有数据,不会重置自增序列
语法:DELETE FROM 表名称 WHERE 列名称=值
-- 删除 test 表中,id为 3 的用户
delete test users where id=3
② TRUNCATE 删除表中的所有数据,并重置自增序列
语法:TRUNCATE TABLE 表名称
-- 清空test表中数据
truncate table test
5.2.5 AND 和 OR
AND(也可以用 &&)表示必须同时满足多个条件,OR(也可以用 ||) 表示只要满足任意一个条件即可。
-- 使用and来显示所有状态为0且id小于3的用户
select * from test where status=0 and id<3
-- 使用 or 来显示所有状态为1 或username为zs的用户
select * from test where status=1 or username='zs'
5.2.6 ORDER BY排序
升序ASC(默认),降序DESC。
-- 对test表中的数据,按照status字段进行升序排序
select * from test order by status
-- 按照id对结果进行降序的排序
select * from test order by id desc
-- 对test表中的数据,先按照status进行降序排序,再按照username字母的顺序,进行升序的排序
select * from test order by status desc, username asc
5.2.7 AS :设置别名
-- 使用 as 关键字给列起别名
select username as '用户名' from test where id<10
-- 也可以省略 as
select username '用户名' from test where id<10
5.2.8 聚合函数
① 求和 SUM()
-- 给test表中所有id值求和 'id值的和' 为sum(id)的别名
select sum(id) 'id值的和' from test
② 求最值 MAX() 、 MIN()、AVG()
-- 求最大值
select max(id) 'id值的最大值' from test
-- 求最小值
select min(id) 'id值的最小值' from test
-- 求平均值
select avg(id) 'id值的平均值' from test
③ 计数 COUNT(*)
-- 使用count(*)来统计test表中,状态为0的总数量
select count(*) from test where status
5.2.9 分页查询
语法:limit m,n;
m:记录数据库的下标,从零开始;
n:数据长度 。
-- 在 test 表中,返回id,username,第一页10条记录
select id,username from test limit 0,10
Tips:m = ( 当前页 - 1 ) * n
5.2.10 模糊查询
① 包含:like "%关键字%" ;
② 以关键字开头:like "关键字%";
③ 以关键字结尾:like "%关键字"。
-- 在 test 表中查询 包含 "坤"关键字的信息
select * from test like "%坤%";
-- 在 test 表中查询以"坤"关键字 开头 的信息
select * from test like "坤%";
-- 在 test 表中查询以"坤"关键字 结尾 的信息
select * from test like "%坤";
5.3 在项目中操作数据库
使用方法:
① 安装操作MySQl数据库的第三方模块(mysql):npm i mysql;
② 配置mysql并连接MySQl;
③ 在nodejs中调用。
// 1. 导入 mysql 模块
const mysql = require('mysql')
// 2. 建立与 MySQL 数据库的连接关系
const db = mysql.createPool({
host: '127.0.0.1', // 数据库的 IP 地址
user: 'xxx', // 登录数据库的账号
password: 'xxx', // 登录数据库的密码
database: 'test', // 指定要操作哪个数据库
})
// 测试 mysql 模块能否正常工作
db.query('select 1', (err, results) => {
// mysql 模块工作期间报错了
if(err) return console.log(err.message)
// 能够成功的执行 SQL 语句
console.log(results)
})
// 查询 users 表中所有的数据
const sqlStr1 = 'select * from users'
db.query(sqlStr1, (err, results) => {
// 查询数据失败
if (err) return console.log(err.message)
// 查询数据成功
// 注意:如果执行的是 select 查询语句,则执行的结果是数组
console.log(results)
})
// 向 users 表中,新增一条数据,其中 username 的值为 zs,password 的值为 afasfa
const user = { username: 'zs', password: 'afasfa' }
// 定义待执行的 SQL 语句
const sqlStr2 = 'insert into users (username, password) values (?, ?)'
// 执行 SQL 语句
db.query(sqlStr2, [user.username, user.password], (err, results) => {
// 执行 SQL 语句失败了
if (err) return console.log(err.message)
// 成功了
// 注意:如果执行的是 insert into 插入语句,则 results 是一个对象
// 可以通过 affectedRows 属性,来判断是否插入数据成功
if (results.affectedRows === 1) {
console.log('插入数据成功!')
}
})
// 演示插入数据的便捷方式
const user2 = { username: 'zs2', password: 'ervdsf' }
// 定义待执行的 SQL 语句
const sqlStr3 = 'insert into users set ?'
// 执行 SQL 语句
db.query(sqlStr3, user2, (err, results) => {
if (err) return console.log(err.message)
if (results.affectedRows === 1) {
console.log('插入数据成功')
}
})
// 演示如何更新用户的信息
const user3 = { id: 6, username: 'aaa', password: '000' }
// 定义 SQL 语句
const sqlStr4 = 'update users set username=?, password=? where id=?'
// 执行 SQL 语句
db.query(sqlStr4, [user3.username, user3.password, user3.id], (err, results) => {
if (err) return console.log(err.message)
// 注意:执行了 update 语句之后,执行的结果,也是一个对象,可以通过 affectedRows 判断是否更新成功
if (results.affectedRows === 1) {
console.log('更新成功')
}
})
// 演示更新数据的便捷方式
const user4 = { id: 6, username: 'aaaa', password: '0000' }
// 定义 SQL 语句
const sqlStr5 = 'update users set ? where id=?'
// 执行 SQL 语句
db.query(sqlStr5, [user4, user4.id], (err, results) => {
if (err) return console.log(err.message)
if (results.affectedRows === 1) {
console.log('更新数据成功')
}
})
// 删除 id 为 5 的用户
const sqlStr6 = 'delete from users where id=?'
db.query(sqlStr6, 5, (err, results) => {
if (err) return console.log(err.message)
// 注意:执行 delete 语句之后,结果也是一个对象,也会包含 affectedRows 属性
if (results.affectedRows === 1) {
console.log('删除数据成功')
}
})
// 标记删除
const sqlStr7 = 'update users set status=? where id=?'
db.query(sqlStr7, [1, 6], (err, results) => {
if (err) return console.log(err.message)
if (results.affectedRows === 1) {
console.log('标记删除成功')
}
})
Tips:涉及到update、insert 、delete语句都需对 affectedRows 属性进行状态判断。
七、JWT身份认证
6.1 概念
定义:JWT(英文全称:JSON Web Token)是目前最流行的跨域认证解决方案。
工作机制(图片资源来自黑马程序员):
JWT 的组成部分: Header(头部)、Payload(有效荷载)、Signature(签名)
Payload 部分才是真正的用户信息,它是用户信息经过加密之后生成的字符串。
Header 和 Signature 是安全性相关的部分,只是为了保证 Token 的安全性。
6.2 Express 中的 JWT
① 安装 JWT 相关的包并导入相关包;
npm i jsonwebtoken express-jwt
jsonwebtoken用于生成JWT字符串,express-jwt用于将JWT字符串解析还原成JSON对象
// 用于生成JWT字符串
const jwt = require('jsonwebtoken')
// 用于将JWT字符串解析还原成JSON对象 "express-jwt": "^8.4.1",
const { expressjwt: expressJWT } = require("express-jwt");
② 定义 secret 密钥;
用于加密和解密信息的密钥
// 任意字符串
const secretKey = 'jinishizaishitaimei'
③ 生成 JWT 字符串;
// 从数据库获取用户信息,并清空敏感信息
const user = { ...results[0], password: "", user_pic: "" };
// 对用户信息进行加密,生成Token字符串
// expiresIn token保存时长
const tokenStr = jwt.sign(user, secertKey, { expiresIn: '24h' });
④ 将 JWT 字符串还原为 JSON 对象;
// 在路由之前注册
// 只要配置成功了 express-jwt 这个中间件,就可以把解析出来的用户信息挂载到 req.auth 属性上
// 使用.unless({path:[/^\/api\//]})指定哪些接口不需要进行Token的身份认证
app.use(expressJWT({ secret: secretKey , algorithms: ["HS256"] }).unless({ path: [/^\/api\//] }));
⑤ 使用 req.auth(或req.user,依据express-jwt版本)获取token中的用户信息;
⑥ 捕获解析 JWT 失败后产生的错误。
// 错误级别的中间件
app.use((err, req, res, next) => {
// 这次错误是由 token 解析失败导致的
if (err.name === "UnauthorizedError") {
return res.send({
status: 401,
message: "无效的token",
});
}
res.send({
status: 500,
message: "未知的错误",
});
});
免责说明
1、本博客中的文章摘自网上的众多博客,仅作为自己知识的补充和整理,并分享给其他需要的 coder,不会用于商用;
2、因为很多博客的地址已经记不清楚了,所以不会在这里标明出处;
3、强烈推荐一个nodejs学习网站:从零开始nodejs系列文章 | 粉丝日志