开始
yarn add -g nodemon
Express 应用程序生成器
通过应用生成器工具 express-generator 可以快速创建一个应用的骨架。 你可以通过 npx (包含在 Node.js 8.2.0 及更高版本中)命令来运行 Express 应用程序生成器。
npx express-generator
对于较老的 Node 版本,请通过 npm 将 Express 应用程序生成器安装到全局环境中并使用:
npm install -g express-generator
-h 参T数可以列出所有可用的命令行参数:
express -h
Usage: express [options] [dir]
Options:
-h, --help 输出使用方法
--version 输出版本号
-e, --ejs 添加对 ejs 模板引擎的支持
--hbs 添加对 handlebars 模板引擎的支持
--pug 添加对 pug 模板引擎的支持
-H, --hogan 添加对 hogan.js 模板引擎的支持
--no-view 创建不带视图引擎的项目
-v, --view <engine> 添加对视图引擎(view) <engine> 的支持 (ejs|hbs|hjs|jade|pug|twig|vash) (默认是 jade 模板引擎)
-c, --css <engine> 添加样式表引擎 <engine> 的支持 (less|stylus|compass|sass) (默认是普通的 css 文件)
--git 添加 .gitignore
-f, --force 强制在非空目录下创建
例如,如下命令创建了一个名称为 myapp 的 Express 应用。此应用将在当前目录下的 myapp 目录中创建,并且设置为使用 Pug 模板引擎(view engine):
方式1:express myapp
方式2:express --view=pug myapp
express --view=pug myapp
create : myapp
create : myapp/package.json
create : myapp/app.js
create : myapp/public
create : myapp/public/javascripts
create : myapp/public/images
create : myapp/routes
create : myapp/routes/index.js
create : myapp/routes/users.js
create : myapp/public/stylesheets
create : myapp/public/stylesheets/style.css
create : myapp/views
create : myapp/views/index.pug
create : myapp/views/layout.pug
create : myapp/views/error.pug
create : myapp/bin
create : myapp/bin/www
然后在浏览器中打开 http://localhost:3000/ 网址就可以看到这个应用了。
注意:一定要安装nodemon模块,否则在修改代码后,需要手动重启服务器才能看到效果。
"scripts": {
"dev": "nodemon ./bin/www"
},
通过生成器创建的应用一般都有如下目录结构:
.
├── app.js
├── bin
│ └── www
├── package.json
├── public
│ ├── images
│ ├── javascripts
│ └── stylesheets
│ └── style.css
├── routes
│ ├── index.js
│ └── users.js
└── views
├── error.pug
├── index.pug
└── layout.pug
启动MongoDB数据库服务器
mongod --dbpath=用于指定数据库文件的存储/启动路径
mongod是MongoDB数据库的守护进程,用于启动和管理数据库服务器。 --dbpath=是一个命令行选项,用于指定数据库文件的存储路径。你需要在等号后面提供一个有效的路径,MongoDB将在该路径下创建并存储数据库文件。 注意:你需要确保指定的路径是存在且可写的。如果路径不存在,MongoDB将无法启动;如果路径不可写,MongoDB将无法创建或写入数据库文件。
Mongoose库来连接到MongoDB数据库
yarn add mongoose
指令:npm i mongoose "mongoose" 是一个 Node.js 库。并进行数据操作 1,连接到 MongoDB 数据库:使用 "mongoose.connect()" 方法,你可以建立与 MongoDB 数据库的连接。 2,定义模式和模型:使用 "mongoose.Schema" 和 "mongoose.model()" 方法,你可以定义数据的结构和模型。 3,执行查询和操作:使用 "Model.find()"、"Model.create()"、"Model.update()" 等方法,你可以执行数据库查询和操作。 4,实现数据验证和中间件:使用 "Schema" 的预定义和自定义验证规则,以及 "pre" 和 "post" 中间件钩子函数,你可以处理数据验证和操作事件。 5,支持事务处理:"mongoose" 提供了事务处理的支持,可以确保一系列数据库操作的原子性和一致性。
连接步骤: 1,新建文件夹db.config,并新建文件db.config.js用于存储连接数据库的代码; 2.将该文件引入到入口文件 bin > www,require("../db.config/db.config")
// 引入mongoose模块并赋值给变量mongoose
const mongoose = require("mongoose");
// 连接数据库
// mongoose.connect()方法用于连接到MongoDB数据库
// 参数1:数据库的连接字符串,指定了数据库的地址和名称
// mongodb://:这是MongoDB连接URL的协议部分,指示使用MongoDB协议进行连接。
// 127.0.0.1:这是MongoDB服务器的IP地址或主机名。在这个例子中,127.0.0.1代表本地主机,也可以使用其他IP地址或主机名。
// 27017:这是MongoDB服务器的端口号。默认情况下,MongoDB使用27017作为默认端口。
// company-system:这是要连接的数据库的名称。在这个例子中,数据库名称为company-system。
// 参数2:配置对象,用于设置连接选项
// useNewUrlParser:启用新的URL解析器
// useUnifiedTopology:使用新的服务器发现和监视引擎
mongoose.connect("mongodb://127.0.0.1:27017/company-system", {
useNewUrlParser: true,
useUnifiedTopology: true
}).then(() => {
console.log("数据库连接成功!");
}).catch(error => {
console.error("连接失败:", error.message);
});
mongoose.Schema()方法创建数据的结构模型
创建步骤: 1.新建文件夹models> 新建文件(见名之意)News.js 2.导出到services文件夹下使用,services文件夹用于调用模型做增删改查等操作 3.数据结构写好后,进入可视化工具使用 F5 刷新,就能看见数据表了
UserModel.js
const mongoose = require('mongoose')
const Schema = mongoose.Schema
// user模型===>users集合
const UserType={
username:String,//用户名
password:String,//密码
avatar:String,//头像
role:Number//管理员1,编辑2
}
const UserModel= mongoose.model("user",new Schema(UserType))
// "user":表示这个集合的名称,它将映射到MongoDB中的users集合
// new Schema(UserType):表示使用之前定义的UserType作为模型的结构
module.exports = UserModel
routers定义接口
操作步骤: 1.在routers文件夹下创建我的接口,可以新建文件夹(admin)进行统一管理项目接口; 2.新建文件NewsRouter.js,书写接口;
// 3.引入app.js文件,进行挂载使用
const NewsRouter = require("./routes/admin/NewsRouter");
// use一定要放到靠下面一点,否则获取req.body是空值
app.use(NewsRouter)
3.此文件只用作定义接口地址,其余操作交由 controllers 处理
var express = require('express')
const NewsController = require('../../controllers/admin/NewsController')
// multer是一个用于处理文件上传的中间件
const multer = require('multer')
const upload = multer({
// dest:指定了上传文件的存储地址
dest: 'public/avatarupload',
})
var NewsRouter = express.Router()
/* post users listing. */
NewsRouter.post('/adminapi/users/login', NewsController.login)
// 涉及文件上传,普通 post 不行,需要加上 multer 中间件
NewsRouter.post('/adminapi/users/add',upload.single('file'),NewsController.add)
UserRouter.post("/adminapi/users/updateOne", upload.single("file"), NewsController.updateOne);
UserRouter.get("/adminapi/users/list", NewsController.getListById);
UserRouter.get("/adminapi/users/list/:id", NewsController.getListById);
UserRouter.delete("/adminapi/users/list/:id", NewsController.deleteById);
module.exports = NewsRouter
controllers处理接口
操作步骤: 1.在controllers文件夹下创建处理接口的 js 文件,可以新建文件夹(admin)进行统一管理项目接口; 2.新建文件NewsController.js,处理接口;
login - 查
const NewsService = require("../../services/admin/NewsService");
const JWT = require("../../utils/JWT");
const NewsController = {
login: async (req, res) => {
console.log("请求体的内容", req.body);
try {
const userInfo = await NewsService.login(req.body);
if (userInfo.length > 0) {
const token = JWT.generate(
{
_id: userInfo[0]._id,
username: userInfo[0].username,
},
"1d"
);
res.header("Authorization", token);
res.status(200).send({
message: "恭喜你, 登录成功了 ! ^_^ ",
code: 1,
data: {
msg: "这是你目前登录的账户的信息, 喏, 给你 -_- !",
username: userInfo[0].username, //用户名
avatar: userInfo[0].avatar, //头像
role: userInfo[0].role, // 管理员1,编辑2
UserKey: userInfo[0]._id, ///用户id
},
tips: "登录有效期是 24小时, 你的登录状态会在 24小时后自动注销",
token: token,
});
} else {
res.status(204).send({
code: -1,
error: "用户名密码不匹配",
});
}
} catch (error) {
console.error(error);
res.status(500).send({
code: -1,
error: "服务器错误,登录失败!",
});
}
},
};
module.exports = NewsController;
add - 增
const NewsService = require('../../services/admin/NewsService');
const fs = require('fs');
const path = require('path');
const AVATAR_UPLOAD_PATH = '/avatarupload/';
const NewsController = {
add: async (req, res) => {
try {
const { username, password, role } = req.body;
const avatar = req.file ? `${AVATAR_UPLOAD_PATH}${req.file.filename}` : '';
const existingUsers = await NewsService.removeDuplicate({ username });
if (existingUsers.length > 0) {
if (avatar) {
const tempPath = path.join(process.cwd(), '/public' + avatar);
if (fs.existsSync(tempPath)) {
fs.unlinkSync(tempPath);
console.log(`${tempPath} 文件已成功删除`);
}
}
res.status(204).send({
code: 0,
message: '添加失败,该账户名已存在!',
});
} else {
await NewsService.add({ username, password, role: Number(role), avatar });
res.status(200).send({
code: 1,
message: '恭喜你, 添加成功了 ! ^_^ ',
});
}
} catch (error) {
console.error(error);
res.status(500).send({
code: -1,
message: '服务器错误,添加失败!',
});
}
},
};
module.exports = NewsController;
updateOne - 改
const NewsService = require('../../services/admin/NewsService')
const fs = require('fs')
const path = require('path')
const NewsController = {
updateOne: async (req, res) => {
const { username } = req.body
const token = req.headers['authorization'].split(' ')[1]
const payload = JWT.verify(token)
const avatar = req.file ? `/avatarupload/${req.file.filename}` : ''
// 删除旧文件
if (avatar) {
await deleteOldAvatar(payload._id)
}
// 更新数据库中的数据
await NewsService.upload({
_id: payload._id,
username,
avatar,
})
// 返回结果
if (avatar) {
res.status(200).send({
code: 1,
message: '恭喜你, 更新成功了 ! ^_^ ',
data: {
username,
avatar, // 更新后的文件一并返回
},
})
} else {
res.status(200).send({
code: 1,
message: '恭喜你, 更新成功了 ! ^_^ ',
data: {
username,
},
})
}
},
}
// 删除旧文件的函数
async function deleteOldAvatar(userId) {
let temp = await NewsService.find({ _id: userId })
if (temp[0].avatar) {
let tempPath = path.join(process.cwd(), '/public' + temp[0].avatar)
fs.unlink(tempPath, err => {
if (err) {
console.error(err)
return
}
console.log(`${tempPath}文件已成功删除`)
})
}
}
module.exports = NewsController
deleteById- 删
const NewsService = require('../../services/admin/NewsService');
const fs = require("fs");
const path = require("path");
const NewsController = {
deleteById: async (req, res) => {
// 删除用户信息
await NewsService.deleteById({ _id: req.params.id });
// 同时需要删除相应的用户文件信息(头像文件)
let user = await UserService.findById(req.params.id);
if (user[0].avatar) {
let avatarPath = path.join(process.cwd(), '/public' + user.avatar);
fs.unlink(avatarPath, err => {
if (err) {
console.error(err);
return;
}
console.log(`${avatarPath} 文件已成功删除`);
});
}
res.status(200).send({
code: 1,
message: '恭喜你,删除成功了!^_^',
});
},
};
module.exports = NewsController;
getListById
const NewsService = require('../../services/admin/NewsService');
const NewsController = {
getListById: async (req, res) => {
try {
// 通过路由参数获取 id
const result = await NewsService.getListById({ _id: req.params.id });
if (result) {
res.status(200).send({
code: 1,
message: "恭喜你,获取列表成功了!^_^",
data: result,
});
} else {
res.status(404).send({
code: -1,
message: "未找到相应数据",
});
}
} catch (err) {
console.error(err);
res.status(500).send({
code: -1,
message: "获取列表时出现错误",
});
}
},
};
module.exports = NewsController;
services查询模块
操作步骤: 1.在services文件夹下创建处理接口的 js 文件,可以新建文件夹(admin)进行统一管理项目接口; 2.新建文件NewsService.js,处理接口;
login
// NewsServices.js
const UserModel = require('../../models/UserModel')
const NewsService = {
login: async ({ username, password }) => {
return UserModel.find({ username, password })
},
}
module.exports = NewsService
add
const UserModel = require('../../models/UserModel')
const NewsService = {
add: async ({ username, password, role, avatar }) => {
return UserModel.create({ username, password, role, avatar })
},
}
module.exports = NewsService
updateOne
const UserModel = require('../../models/UserModel')
const NewsService = {
updateOne: async ({ _id, username, avatar }) => {
// 判断 avatar 是否为真,更新头像就是真,不更新就是假;真:就需要更新头像;假:不需要更新头像
if (avatar) {
return UserModel.updateOne({ _id }, { username, avatar });
} else {
return UserModel.updateOne({ _id }, { username });
}
},
}
module.exports = NewsService
deleteById
const UserModel = require('../../models/UserModel')
const NewsService = {
deleteById: async ({ _id }) => {
return UserModel.deleteOne({ _id });
},
}
module.exports = NewsService
getListById
const UserModel = require('../../models/UserModel')
const NewsService = {
getListById: async ({ _id }) => {
// 两种情况:一种是带 id 查询;一种是全部查询
return _id
? UserModel.find({ _id }, ["username", "password", "role", "introduction"])
: UserModel.find({}, ["username", "role", "avatar"]);
},
}
module.exports = NewsService
utils
jsonwebtoken
// yarn add jsonwebtoken
// Expected version ">=16.20.1"
// node : Expected version ">=16.20.1".
const jsonwebtoken = require("jsonwebtoken");
const secret = "key"; // 设置签名的密钥
const JWT = {
// value:是你要传递的数据 exprires:设置过期时间
generate(value, exprires) {
// 生成 token
return jsonwebtoken.sign(value, secret, { expiresIn: exprires });
},
verify(token) {
// 验证 token
// try{}catch(e){} 捕获 防止超时验证报错
try{
return jsonwebtoken.verify(token, secret);
}catch(e){
return false;
}
},
};
// 测试
// const token = JWT.generate({ name: "king" }, "10s");
// console.log(JWT.verify(token));
// 模拟错误发生
// setTimeout(() => {
// // 超时验证 token
// console.log(JWT.verify(token));
// },11000)
// 导出到 suercontroller 文件
module.exports = JWT;
appjs的中间件
// ⬆ 上面的接口不受影响
app.use((req, res, next) => {
// 中间件
if (req.url === "/adminapi/users/login") {
// 登录接口不需要验证token,直接放行
next();
} else {
const token = req.headers["authorization"] ? req.headers["authorization"].split(" ")[1] : null; // 取出 token
if (token) {
try {
const payload = JWT.verify(token); // 验证 token 是否失效
if (payload) {
// 重新生成 token 并返回给前端
const newToken = JWT.generate({ _id: payload._id, username: payload.username }, '1d');
res.header("Authorization", newToken);
next();
} else {
// token 过期,返回给前端 401 错误
res.status(401).send({ error: 401, message: "token已过期" });
}
} catch (err) {
console.error(err);
res.status(500).send({ error: 500, message: "服务器内部错误" });
}
} else {
// 没有提供 token,返回给前端 401 错误
res.status(401).send({ error: 401, message: "未提供有效的 token" });
}
}
});
// ↓ 下面的接口会受中间件的影响
第三方中间件
文件上传
yarn add multer
// Expected version ">=16.20.1"
// multer是一个用于处理文件上传的中间件,在Node.js中处理上传的文件
const multer = require('multer')
// 创建upload对象,该对象是multer中间件的实例。通过传递一个配置对象给multer,我们可以定义文件上传的行为和设置
const upload = multer({
// dest:指定了上传文件的存储地址
dest: 'public/avatarupload',
})
NewsRouter.post(
'/adminapi/users/add',
upload.single('file'),
NewsController.add
)
// upload.single('file')的作用是使用multer中间件来处理上传的文件,指定了上传文件需要将文件数据放在名为file的字段中。这样可以让服务器端从请求中提取出上传的文件,并进行相应的处理;
// 前段需要将文件数据放在名为 file 的字段里
fs
介绍:fs是node的核心模块之一,提供文件操作功能;
// fs.unlink(path, callback) 通常用于处理删除操作的结果或错误
// 参数1:path:要删除的文件的路径
// 参数2:callback:回调函数,可选参数;在删除文件完成后执行的回调函数
fs.unlink(tempPath, err => {
if (err) {
console.error(err)
return
}
console.log(`${tempPath}文件已成功删除`)
})
path
介绍:path模块是一个内置模块,用于处理文件路径和目录路径;
let tempPath = path.join(process.cwd(), '/public' + temp[0].avatar)
// path.join():将两个路径拼接成一个完整的路径
// process.cwd():获取 Node.js 进程当前的工作目录,通常是运行 node 命令时所在的目录。
// '/public' + temp[0].avatar:将 '/public' 字符串与 temp[0].avatar 变量相加,生成一个新的字符串,用于表示文件路径