基础知识回顾
node环境下的模块 - 类似于前端的js插件
前端js插件: 挂载到window身上暴露出去
node模块: 有CommonJS规范导出和导入的方法
node模块, 每个外面运行时, 都会被套一个函数一共有5个本地参数
npm作用: node自带的, 可以用于管理第三方包(上传, 下载, 删除)
包: 就是一个/一堆js模块组成的文件夹 - 一般包里得有一个package.json来管理这个包里的东西
常用命令
开发自己的模块: 单独定义一个.js文件就能引入使用了
开发自己的模块包:
初始化package.json
制定好main属性对应的入口文件(手动建立)
编写相关的模块, 都引入 到入口文件 - 统一挂载暴露出去
上传包
先去npmjs.com注册账号
在本地切换会原来官网的镜像地址
在本地npm login 建立登录通道
npm publish 发布到网站上-注意所在目录应该是你开发的模块包下(必须的)
模块加载机制, 引入时, 向父级文件夹找node_modules下有无, 而且频繁引入同一个模块, node内部有缓存机制不会在让模块被重新执行了
express 介绍
本地生效, 执行 npm i express
不要自己定义文件夹名称和 - 已知的模块包名重名
使用Express构建Web服务器步骤
// 使用express 搭建web服务器
// 1. 引入 express 模块
const express = require('express');
// 2. 创建 express 服务器
const app = express();
// 3. 开启服务器, 设置端口号(每个计算机, 端口号不能重复使用)
app.listen(3006, () => console.log('express服务器开始工作了'));
// 监听客户端请求
// 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请求,所以把它放到所有接口的最后。
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字符串, 再返回给前端
});
// (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);
});
// 监听客户端请求
// 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");
})
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('你的请求体收到了');
});
// 客户端,需要发送POST请求,提交的数据随便写
// 写接口之前,配置
// 可以帮我们接收 content-type: application/json
app.use(express.json());
// 后续,任何一个POST接口,都可以通过req.body获取到请求体的内容
app.post('/api/addbook', (req, res) => {
// 获取请求体
res.send(req.body);
});
Content-Type: multipart/form-data; --XXADFsdfssf
需要使用第三方模块(multer)才能够获取到。
const multer = require('multer')
const upload = multer({ dest: 'uploads/' })
upload.single('cover_img')
, (req, res) => {})// 客户端,需要发送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
});
});
// 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"));
服务器提供的资源分为2种
浏览器想直接访问后台服务器上的资源, 填写路径就能访问, 需要后端先配置静态资源目录
// 配置静态资源目录
// (1): 这个文件夹不会出现在前端的路径上
// (2): 前端可以直接访问这个路径下的一切
app.use(express.static("静态资源")); // app.use()让app对象使用这个功能
// 如果要是还有一个文件夹, 接着写即可, 从上到下的顺序匹配
app.use(express.static("uploads"));
让别人猜不出你文件夹真实的名字, 随便写一个虚拟的前缀路径来使用
app.use("/abc", express.static("静态资源"));
app.use(express.static("uploads"));
现在还没有学数据, 所以在后台的数组里先保存下(只要后台不重启后台的js文件, 那么数组里的值一直再, 如果重新数组就回归初始化)
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());
// 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($(`
${obj.id}
${obj.bookname}
${obj.author}
${obj.publisher}
${obj.id}">删除
`))
})
});
后台:
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($(`
${obj.id}
${obj.bookname}
${obj.author}
${obj.publisher}
${obj.id}">删除
`))
})
})
}
// 1.5 网页打开 - 到这里直接调用
getBooks();
后台:
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();
})
});
如有不足,请多指教,
未完待续,持续更新!
大家一起进步!