node+express+mongodb (二)nodejs服务初始化

Express 中文网

使用express搭建项目

// 安装express生成器
npm install -g express-generator
express
y
pnpm i
pnpm run start

添加.gitignore文件

node_modules/
public/uploads

Snipaste_2024-03-08_17-08-06.png

安装本地mongobd使用nodejs链接成功

这里就不细说mongodb 默认大家都安装好了并且开启了mongodb服务

Snipaste_2024-03-08_17-15-43.png

新建上图对应的文件夹

 pnpm i mongoose

db/index.js

const mongoose = require("mongoose");
mongoose.connect("mongodb://127.0.0.1:27017/vue3admin", {});
const db = mongoose.connection;

db.on("error", console.error.bind(console, "connection error:"));
db.once("open", (res) => console.log("db ok"));

在app.js中添加

// 链接mongodb
...
require("./db");
...

重新启动 pnpm run start

Snipaste_2024-03-08_17-23-10.png

刚好到这里 我们可以引入入 nodemon

// 安装
pnpm i nodemon
// 修改packagejson
"scripts": {
   "start": "nodemon ./bin/www"
 },
 // 重新启动
 pnpm run start

nodemon用来监视node.js应用程序中的任何更改并自动重启服务,非常适合用在开发环境中。以前,我们开发一个node后端服务时,每次更改文件,均需重启一下,服务才能生效。这使我们的开发效率降低了很多。nodemon的出现,可以随时监听文件的变更,自动重启服务,我们开发时只需关注代码即可,不再需要手动重启服务

这样就OK了

Snipaste_2024-03-08_17-29-24.png

用户模块的构建

直接上代码 db/model/user.js

const mongoose = require("mongoose");
const schemaRules = {
  id: { type: String }, // 用户ID,必需且唯一
  username: {
    type: String,
    required: true,
    unique: true,
    minlength: 2,
    maxlength: 20,
  }, // 用户名,必需且唯一
  password: {
    type: String,
    required: true,
    match: /^(?=.*[a-zA-Z])(?=.*\d)[a-zA-Z\d\S]{6,}$/, // 长度至少6位,包含字母和数字或字符
  }, // 密码,必需
  address: {
    provinceName: { type: String },
    cityName: { type: String },
    areaName: { type: String },
    countyName: { type: String },
    streetName: { type: String },
    provinceCode: { type: String },
    cityCode: { type: String },
    areaCode: { type: String },
    countyCode: { type: String },
    streetCode: { type: String },
  },
  nickname: { type: String }, // 昵称
  createdTime: { type: Date, default: Date.now }, // 创建时间,默认为当前时间
  updatedTime: { type: Date, default: Date.now }, // 最后更新时间,默认为当前时间
  roleIds: { type: Array }, // 角色ids
  age: { type: Number }, // 年龄
  region: { type: Object }, // 地区
  email: { type: String }, // 邮箱
  phone: { type: String }, // 手机号
  description: { type: String }, // 个人简
  qq: { type: String }, // QQ号
  roleIds: { type: Array }, // 绑定的角色ID
  status: { type: String, default: 0 }, // 是否启用,
  sex: { type: String, default: 0 }, // 性别,1代表男性,0代表女性,默认为0
  avatar: { type: String, default: "" }, // 头像
  openid: { type: String }, // 微信扫码OpenID
  // 是否是管理员
  isManager: { type: Boolean, default: false },
};
// 定义用户模型
const userSchema = new mongoose.Schema(schemaRules);

// 创建用户模型
const User = mongoose.model("User", userSchema);

// 导出用户模型
module.exports = { User, schemaRules };

utils/index.js

pnpm i uuid
// 使用  UUID v4 生成全局唯一 ID
function generateUUID() {
  const { v4: uuidv4 } = require("uuid");
  return uuidv4();
}

module.exports = {
  generateUUID,
};

在app.js中 添加

// 定义全局方法
const { generateUUID } = require("./utils");
global.$generateUUID = generateUUID;

routes/user.js

var express = require("express");
var router = express.Router();
const { User, schemaRules } = require("../db/model/user");

// 新增用户
router.post("/add", async (req, res) => {
  const { body } = req;
  try {
    await User.create({ ...body, id: $generateUUID() }); // 创建新用户
    res.send({ code: 200, message: "创建成功" });
  } catch (error) {
    res.send({ code: 500, message: error });
  }
});
// 删除用户
router.delete("/delete", async (req, res) => {
  try {
    const { id, ids } = req.body;
    if (id) {
      await User.deleteOne({ id });
    } else {
      await User.deleteMany({ id: { $in: ids } });
    }
    res.send({ code: 200, message: "删除成功" });
  } catch (error) {
    res.send({ code: 500, message: error });
  }
});
// 修改用户
router.put("/update", async (req, res) => {
  try {
    const { id } = req.body;
    await User.updateOne({ id }, { ...req.body, updatedTime: Date.now() });
    res.send({ code: 200, message: "更新成功" });
  } catch (error) {
    res.send({ code: 500, message: error });
  }
});
// 查询用户列表
router.post("/list", async (req, res) => {
  const { username, page = { pageSize: 10, page: 1 } } = req.body;
  const query = {};
  // 添加 username 模糊查询条件
  if (username) {
    query.username = { $regex: username };
  }
  try {
    const users = await User.find(query)
      .skip((page.page - 1) * page.pageSize)
      .limit(page.pageSize)
      .sort({ createdTime: -1 }); // 按创建时间倒序排序
    const total = await User.countDocuments(query); // 获取符合条件的用户总数
    res.send({ code: 200, data: users, page: { ...page, total } });
  } catch (error) {
    res.send({ code: 500, message: error });
  }
});
// 用户详情
router.get("/detail", async (req, res) => {
  try {
    const userId = req.query.id;
    const user = await User.findOne({ id: userId }); // 根据ID查询用户
    if (!user) res.send({ code: 500, message: `没有ID为${userId}的用户信息` });
    res.send({ code: 200, data: user });
  } catch (error) {
    res.send({ code: 500, message: error });
  }
});
// 注册接口
// 登录接口
module.exports = router;

jsonwebtoken验证模块=》token

utils/token.js

pnpm i jsonwebtoken
const jwt = require("jsonwebtoken");
// 秘钥
const screat = "vue3admin";
//生成token
function createToken(playload) {
  playload.ctime = Date.now();
  playload.exps = 24 * 60 * 60 * 1000; // 手动设置过期时间 60s
  return jwt.sign(playload, screat);
}
// 验证token
function verifyToken(token) {
  return new Promise((resolve, reject) => {
    jwt.verify(token, screat, (err, data) => {
      if (!token) return reject({ code: 401, msg: "请携带token请求" });
      if (err) return reject({ code: 401, msg: "token 验证失败" });
      const beforeTime = data.ctime + data.exps;
      const nowTime = Date.now();
      if (nowTime > beforeTime) return reject({ code: 401, msg: "token 过期" }); // 比对当前时间戳  jwt创建的时间+有效期  前端收到重新获取token
      resolve(data);
    });
  });
}
// 定义全局校验 token 的中间件
async function checkToken(req, res, next) {
  // 定义不需要 token 验证的接口路径
  const whiteList = [
    "/user/login",
    "/user/register",
    "/user/get-by-token",
    /^\/utils/,
  ]; // 使用正则表达式来匹配 /xx 开头的路径
  const path = req.path;
  // 如果当前请求路径在白名单内,则不需要 token 验证,直接通过
  if (
    whiteList.some((item) =>
      item instanceof RegExp ? item.test(path) : item === path
    )
  ) {
    return next();
  }
  // 检查是否存在 skipToken 参数
//   const skipToken = req.headers.skipToken;
//   if (skipToken) {
//     return next();
//   }
  // 否则,检查请求头是否包含 token
  const token = req.headers.token;
  if (!token) {
    // 如果没有携带 token,则返回 401 Unauthorized 状态码
    return res.json({ code: 401, message: "请先登录" });
  }
  try {
    // 验证 token 是否有效
    const data = await verifyToken(token);
    // 将解析出来的用户信息挂载到 req 对象上,方便后面路由的使用
    req.user = data;
    next();
  } catch (err) {
    // token 验证失败或过期,则返回 401 Unauthorized 状态码
    return res.json({ code: 401, message: err.msg });
  }
}
module.exports = {
  createToken,
  verifyToken,
  checkToken,
};

整理下app.js

var express = require("express");
var path = require("path");
var app = express();

require("./db"); // 引入数据库连接对象
app.use(express.json()); // 解析 JSON 请求体
app.use(express.urlencoded({ extended: false })); // 解析 URL 编码请求体
// 静态资源路径
app.use(express.static(path.join(__dirname, "public")));
// 设置视图模板引擎和所在目录
app.set("views", path.join(__dirname, "views"));
app.set("view engine", "jade");
//  跨域
app.all("*", function (req, res, next) {
  res.header("Access-Control-Allow-Origin", "http://localhost:5173"); // 允许的来源域名
  res.header(
    "Access-Control-Allow-Headers",
    "Content-Type,Content-Length,Authorization,Accept,X-Requested-With,token"
  );
  res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");
  res.header("Access-Control-Allow-Credentials", "true"); // 允许携带凭据
  res.header("X-Powered-By", "3.2.1");
  next();
});
// 定义全局方法
const { generateUUID } = require("./utils");
global.$generateUUID = generateUUID;
// 全局 token 拦截中间件
const { checkToken } = require("./utils/token");
app.use(checkToken);
// 注册路由
require("./utils/route")(app);
module.exports = app;

自动注册路由 utils/route.js

const fs = require("fs");
const path = require("path");

module.exports = function (app) {
  const routesDir = path.join(__dirname, "../routes"); // 路由文件所在目录
  const routeFiles = fs.readdirSync(routesDir); // 获取所有路由文件
  routeFiles.forEach((file) => {
    const router = require(path.join(routesDir, file));
    let routerPath = `/${file.split(".")[0]}`; // 根据文件名生成访问路径
    routerPath = routerPath == "/index" ? "/" : routerPath; // 将 "/index" 路径映射为根路径 "/"
    app.use(routerPath, router); // 注册路由
  });
};

这样的话我们就可以编写登录接口注册接口了

可以直接访问 localhost:3000

Snipaste_2024-03-08_18-10-30.png

那这样我们的token拦截也成功了

登录接口

// 登录接口
router.post("/login", async (req, res) => {
  const { username, password } = req.body;
  if (!username || !password)
    return res.send({ code: 500, message: "用户名或密码不能为空" });
  try {
    const user = await User.findOne({ username, password });
    if (!user) return res.send({ code: 403, message: "用户名或密码错误" });
    let { id, avatar, status } = user;
    if (status != 0) return res.send({ code: 403, message: "账号状态问题!" });
    let token = createToken({ login: true, name: username, id, avatar });
    res.send({
      code: 200,
      message: "登录成功",
      data: { username, id, avatar, token },
    });
  } catch (error) {
    res.send({ code: 500, message: error });
  }
});
注册接口

这个接口就比较麻烦 我们先简单完成功能后续可以对接邮箱验证码辅助账号注册
目前可以先调试下新增和登录就行

到这里我们就完成了简单服务node服务搭建

从零开始vue3+vite+ts+pinia+router4手把手搭建后台管理系统(1)搭建项目

最后

欢迎 点赞 收藏 有问题欢迎留言评论!!

  • 12
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Endless-y

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值