Node.js
回顾
-
基础知识回顾
node环境下的模块 - 类似于前端的js插件
前端js插件: 挂载到window身上暴露出去
node模块: 有CommonJS规范导出和导入的方法
node模块, 每个外面运行时, 都会被套一个函数一共有5个本地参数
- module - 模块内置对象
- exports - 导出辅助对象
- require - 引入其他模块的方法
- __filename - 当前文件所在绝对路径
- __dirname - 当前文件夹所在的绝对路径
-
npm作用: node自带的, 可以用于管理第三方包(上传, 下载, 删除)
包: 就是一个/一堆js模块组成的文件夹 - 一般包里得有一个package.json来管理这个包里的东西
常用命令
- npm init - 初始化项目 - 得到package.json
- npm install 包名 - 安装一个包从npm源上下载相关代码到当前工程的node_modules目录下
- npm install 工具包 -g 安装到全局(一般都是带命令的工具包)
- nodemon 替代node命令来启动js文件 - 实时更新
- i5ting_toc 用来把md文件转成网页
- npm install 包名@版本号 (得去npmjs.cm网站上查)
-
开发自己的模块: 单独定义一个.js文件就能引入使用了
开发自己的模块包:
初始化package.json
制定好main属性对应的入口文件(手动建立)
编写相关的模块, 都引入 到入口文件 - 统一挂载暴露出去
-
上传包
先去npmjs.com注册账号
在本地切换会原来官网的镜像地址
在本地npm login 建立登录通道
npm publish 发布到网站上-注意所在目录应该是你开发的模块包下(必须的)
模块加载机制, 引入时, 向父级文件夹找node_modules下有无, 而且频繁引入同一个模块, node内部有缓存机制不会在让模块被重新执行了
10. express
express 介绍
- express 是一个第三方模块,用于快速搭建服务器
- express 是一个基于 Node.js 平台,快速、开放、极简的 web 开发框架
- express基于http模块的基本API,使用express的时候,也能使用http的API
- 使用express的时候,仍然可以使用http模块的方法,比如res.end()、req.url
- express还额外封装了一些新方法,能让我们更方便的搭建服务器
- web网页服务器
- API接口服务器
- express提供了中间件功能,其他很多强大的第三方模块都是基于express开发的
- Express 官网
- Express 中文文档(非官方)
- Express GitHub仓库
- 菜鸟教程
- 腾讯云开发者手册
10.0 安装使用 express
本地生效, 执行 npm i express
不要自己定义文件夹名称和 - 已知的模块包名重名
使用Express构建Web服务器步骤
- 下载express模块包到项目工程中
- 新建服务器文件xxx.js
- 引入express模块
- 创建express服务器对象
- 配置端口, 开启服务器
// 使用express 搭建web服务器
// 1. 引入 express 模块
const express = require('express');
// 2. 创建 express 服务器
const app = express();
// 3. 开启服务器, 设置端口号(每个计算机, 端口号不能重复使用)
app.listen(3006, () => console.log('express服务器开始工作了'));
10.1 后台 - GET接口
// 监听客户端请求
// app.get('请求的URL', (请求对象, 响应对象) => {
// 当前台以get方式, 请求这个URL, 则此函数就执行
//});
app.get('/api/getbooks', (req, res) => {
res.send("GET方式-请求了/api/getbooks"); // 结束响应, 返回内容(自带响应头UTF-8)
});
app.get('/', (req, res) => {
// 客户端没有指定请求的url,在这里处理。
res.send("GET方式-请求的根路径 /")
});
app.get('*', (req, res) => {
res.send("GET方式-没有找到要请求的路径*")
})
app.get('*', (req, res) => {})
他能够匹配到所有的GET请求,所以把它放到所有接口的最后。
10.2 后台 - 获取 - GET参数
- 这种方式的参数,比较常规,参数部分也叫做查询字符串
- 请求地址的写法: http://localhost:3002/test?id=3&bookname=zxx&age=20
- 获取方式:
req.query
// 写接口
// 前台请求: http://localhost:3002/test?id=3&bookname=zxx&age=20
app.get('/test', (req, res) => {
// 获取url?参数=值&参数=值
res.send(req.query); // { id: '3', bookname: 'zxx', age: '20' } - res.send还会内置转成JSON字符串, 再返回给前端
});
10.3 后台 - 获取 - url参数
- 这种方式的参数,叫做动态参数
- 请求地址的写法:http://localhost:3003/test/3/zs/30
- 要求请求的url参数必填,否则不能和定义的接口匹配
// (1): 提前在路径上配置好key的名字, 注意:是必须的(规定)
// (2): express会自动在req的身上绑定req.params属性, 设置好路径对应位置的值和key形成对象
// 1个参数
// 浏览器的请求 http://localhost/test/3
// 测试接口,获取动态参数
app.get('/test/:id', (req, res) => {
res.send(req.params); // req.query是查询字符串, req.params是路径上的参数{id: 3}
});
// 多个参数
// 浏览器的请求 http://localhost/test2/3/zhangsan/20
// 测试接口,获取多个动态参数
app.get('/test2/:id/:name/:age', (req, res) => {
// 可以获取所有的动态参数
// { id: '4', name: 'zhangsan', age: '20' }
res.send(req.params);
});
10.4 后台 - POST接口
// 监听客户端请求
// app.post('请求的URL', (请求对象, 响应对象) => {
// 当前台以post方式, 请求这个URL, 则此函数就执行
//});
app.post('/api/addbook', (req, res) => {
res.send("POST方式, 请求/api/addbook");
});
app.post('*', (req, res) => {
// 处理所有的POST请求
res.send("post方式, 未找到你要请求的url");
})
10.5 后台 - 获取 - POST请求体 - 查询字符串
- GET方式没有请求体,POST方式才有请求体。
- 请求体,即客户端提交的数据。
- POST请求体, 前端请求头使用
Content-Type: application/x-www-form-urlencoded
// 客户端,需要发送POST请求,提交的数据随便写
// 写接口之前,配置
// 可以帮我们接收 content-type: application/x-www-form-urlencoded类型的请求体
app.use(express.urlencoded({extended: false}));
// 后续,任何一个POST接口,都可以通过req.body获取到请求体的内容
app.post('/api/addbook', (req, res) => {
// 获取请求体
console.log(req.body);
res.send('你的请求体收到了');
});
10.6 后台 - 获取 - POST请求体 - JSON字符串
// 客户端,需要发送POST请求,提交的数据随便写
// 写接口之前,配置
// 可以帮我们接收 content-type: application/json
app.use(express.json());
// 后续,任何一个POST接口,都可以通过req.body获取到请求体的内容
app.post('/api/addbook', (req, res) => {
// 获取请求体
res.send(req.body);
});
10.7 后台 - 获取 - POST请求体 - FormData
- POST请求头: FormData对象 – 对应的
Content-Type: multipart/form-data; --XXADFsdfssf
需要使用第三方模块(multer)才能够获取到。
- 下载安装第三方模块 multer
- 使用步骤
- 加载multer
const multer = require('multer')
- 配置上传目录
const upload = multer({ dest: 'uploads/' })
- app.post(’/test’,
upload.single('cover_img')
, (req, res) => {}) - 接口内部,可以使用req.body接收文本值,使用req.file接收文件信息
- 同时,文件已经上传到了服务器
- 加载multer
// 客户端,需要发送POST请求,提交的数据随便写
// 写接口之前,配置
const multer = require('multer');
// 配置上传目录
const upload = multer({ dest: 'uploads/' });
// 前台图片参数名: cover_img
app.post('/test', upload.single('cover_img'), (req, res) => {
// 可以使用 req.body 接收文本信息
console.log(req.body); // { username: 'laotang', nickname: 'boy' }
console.log('=====================');
// 可以使用 req.file 获取文件信息
console.log(req.file);
/**
* {
fieldname: 'cover_img',
originalname: '1.jpg',
encoding: '7bit',
mimetype: 'image/jpeg',
destination: 'uploads/', // 上传的目录
filename: '346d149a0a79c5c9792367c49d3db5ae', // 上传之后新文件的名字
path: 'uploads/346d149a0a79c5c9792367c49d3db5ae',
size: 35774
}
*/
res.send({
...req.body,
fileUrl: req.file.filename
});
});
小结
- 搭建服务器的步骤
- const express = require(‘express’);
- const app = express();
- app.listen(3006, () => console.log(’…’));
- 配置及写接口
- GET方式如何获取请求参数
- 查询字符串格式
- req.query
- url参数(动态参数)
- req.params
- 查询字符串格式
- POST方式如何获取请求体
- 查询字符串格式
- 配置:app.use(express.urlencoded({ extended: false }))
- req.body
- FormData格式
- 配置:
- 下载安装 multer
- const multer = require(‘multer’);
- const upload = multer({ dest: ‘uploads’ });
- app.post(‘接口地址’, upload.single(‘文件的key’), ‘处理函数’);
- req.body – 获取文本信息
- req.file – 获取文件信息
- 同时,文件已经上传完毕
- 配置:
- 查询字符串格式
10.8 multer高级用法 - 改文件名
// 1. 引入multer, 下载并引入uuid模块(用于生成一个唯一不重复的字符串)
const multer = require('multer');
const uuidv4 = require("uuid").v4;
// 2. multer - diskStorage 磁盘存储控制
const storage = multer.diskStorage({
destination: "uploads/", // 文件存到哪里
filename: function (req, file, cb) { // 设置文件名
const parts = file.originalname.split('.') // 用.分割
const ext = parts[parts.length - 1] // 提取最后一个是扩展名
cb(null, `${Date.now()}_${uuidv4().replace(/-/g, '')}.${ext}`) // 时间戳_随机字符串.扩展名格式作为文件的filename名字
}
})
// 3. 生成multer上传工具
const upload = multer({ storage });
// 4. single方法在原地返回一个函数体, 接受名称为的单个文件fieldname。单个文件将存储在中req.file。
// app.post()接收到请求, 先被第二个函数执行, 再被第三个函数执行, 直到遇到res.send结束本次请求
app.post('/test', upload.single('cover_img'), (req, res) => {
res.send({
...req.body,
fileUrl: "http://localhost:3008/" + req.file.filename
});
});
// 5. 设置静态资源路径, 当前端请求url在上面未命中, 就会执行这里. 尝试在uploads下面匹配资源
app.use(express.static("uploads"));
11. express更多用法
11.0 静态资源配置
服务器提供的资源分为2种
- 文件等资源
- 接口数据资源
浏览器想直接访问后台服务器上的资源, 填写路径就能访问, 需要后端先配置静态资源目录
// 配置静态资源目录
// (1): 这个文件夹不会出现在前端的路径上
// (2): 前端可以直接访问这个路径下的一切
app.use(express.static("静态资源")); // app.use()让app对象使用这个功能
// 如果要是还有一个文件夹, 接着写即可, 从上到下的顺序匹配
app.use(express.static("uploads"));
11.1 前缀虚拟路径
让别人猜不出你文件夹真实的名字, 随便写一个虚拟的前缀路径来使用
app.use("/abc", express.static("静态资源"));
app.use(express.static("uploads"));
11.2 express路由
- 路由:即请求和处理程序的映射关系
12. 案例 - 图书管理案例
现在还没有学数据, 所以在后台的数组里先保存下(只要后台不重启后台的js文件, 那么数组里的值一直再, 如果重新数组就回归初始化)
12.0 搭建web服务器
- 配置静态资源 - 既要提供图书管理案例的网页 - so不要再vscode启动(不用vscode的插件live server内置的微型服务器) - 直接打开浏览器访问
let arr = [];
// 静态资源网页
app.use("/html", express.static(__dirname + "/图书管理系统"));
// 解析application/x-www-form-urlencoded
app.use(express.urlencoded({extended: false}));
// 解析application/json
app.use(express.json());
12.1 新增功能前后台使用
// resetFul API规范的接口
// 评论管理 - restFul API
// RESTFUL是一种网络应用程序的设计风格和开发方式接口的场景,动作类型为查询, 新增、变更、删除所调用资源。
// 1、每一个URI代表1种资源, 路径带参
// 2、客户端使用GET、POST、PUT、DELETE4个表示操作方式的动词对服务端资源进行操作
app.post("/api/book", (req, res) => {
req.body.id = arr.length + 1;
arr.push(req.body);
res.status(201).send({
msg: "新增成功",
status: 0,
data: req.body
})
})
前台:
// 1.1 写一段代码,请求接口,添加图书
$('#btnAdd').click(function () {
// 1.2 获取输入框的值(三个)
var bookname = $('#iptBookname').val().trim();
var author = $('#iptAuthor').val().trim();
var publisher = $('#iptPublisher').val().trim();
// 1.3 判断不能为空,如果有一个为空,提示并终止代码继续执行
if (bookname == '' || author == '' || publisher == '') {
return alert('内容不能为空');
}
// 1.4 按照接口文档,调用添加书籍的接口,完成添加
axios({
method: "POST",
url: "http://localhost:3000/api/book",
data: {
bookname: bookname,
author: author,
publisher: publisher
}
}).then(res => {
// 添加成功
var obj = res.data.data; // 接受后台返回插入成功的这个对象(带后台生成的id的)
console.log(obj);
$("#tb").prepend($(`<tr>
<td>${obj.id}</td>
<td>${obj.bookname}</td>
<td>${obj.author}</td>
<td>${obj.publisher}</td>
<td>
<a href="javascript:;" class="del" data-id="${obj.id}">删除</a>
</td>
</tr>`))
})
});
12.2 查询功能前后台实现
后台:
app.get("/api/book", (req, res) => {
res.send({
msg: "获取成功",
status: 0,
data: arr
})
});
前台:
// 写一段代码,请求接口,获取所有的图书
function getBooks() {
$("#tb").empty();
// 把数据请求回来
axios({
url: "http://localhost:3000/api/book",
method: "GET"
}).then(res => {
// 提取数据 - 铺设页面
$(res.data.data).each(function (index, obj) {
$("#tb").prepend($(`<tr>
<td>${obj.id}</td>
<td>${obj.bookname}</td>
<td>${obj.author}</td>
<td>${obj.publisher}</td>
<td>
<a href="javascript:;" class="del" data-id="${obj.id}">删除</a>
</td>
</tr>`))
})
})
}
// 1.5 网页打开 - 到这里直接调用
getBooks();
12.3 删除功能前后台实现
后台:
app.delete("/api/book/:id", (req, res) => {
// 找到id在数组里对象的索引
let ind = arr.findIndex(obj => obj.id == req.params.id);
arr.splice(ind, 1);
res.send({
msg: "获取成功",
status: 0,
data: arr
})
})
前台:
// // 3.1 因为a是动态创建的, 所以在这里要执行 - 只能用事件委托, 不然得写在getBoods里面的下面 再绑定, 代码写在一起不好
$('#tb').on('click', '.del', function () {
// 3.2 对于敏感操作,最好给一个提示(询问是否要删除)
if (!confirm('你确定要删除吗,你好狠!')) {
return;
}
// 3.3 如果确定是删除,按照接口,完成接口的要求
var id = $(this).attr('data-id');
axios({
method: "DELETE",
url: "http://localhost:3000/api/book/" + id
}).then(res => {
getBooks();
})
});
如有不足,请多指教,
未完待续,持续更新!
大家一起进步!