Skip to content

NodeJS 案例 记账本

一、构建项目基本功能

1.1 使用Express 搭建项目

express -e accounts // -e 使用 ejs 模板引擎

cd accounts // 切换到项目目录下

npm i // 安装全部依赖

1.2 基本配置路由

在 index.js 路由文件中添加:

js
// 渲染记账本的列表
router.get('/account', function(req, res, next) {
  res.render('list'); 
});

// 渲染添加记录
router.get('/account/create', function(req, res, next) {
  res.render('create');
});

注意:

访问静态资源时,要使用绝对路径形式,而不使用相对路径

js
<script src="" data-missing="jquery.min.js"></script>  // 相对路径 访问时可能会出现路径错误

<script src="/js/jquery.min.js"></script>  // 绝对路径:拼接当前网页的 协议域名和端口 形成绝对路径,然后去网站的静态目录下寻找资源,不会出错

1.3 获取表单数据

使用form表单的提交数据功能,必须为要获取数据的表单组件添加 name 属性值,和设置 form 的属性

  • 添加 name 属性

    html
    <input name="title" type="text" class="form-control" id="item" />
    ...
    <textarea name="remarks" class="form-control" id="remarks"></textarea>
  • 设置 form 属性

    html
    <form method="post" action="/account">  // 设置为绝对路径
  • 配置获取表单内容的路由

    js
    router.post("/account", (req, res) => {
      console.log(req.body); // 获取请求体的数据
      // req.body 以对象形式获取到表单的数据,name为键名,value为值
      res.send("ok");
    });

1.4 lowdb介绍与使用

1.4.1 lowdb介绍

Lowdb是一个基于Node.js的轻量级JSON数据库,它允许您使用JavaScript对象和数组来操作数据。

1.4.2 lowdb安装

npm i lowdb@1.0.0

最新版本使用 ES6 语法进行导入,这里安装老版本,使用 node 模块导入

14.3 使用lowdb

初始化:

js
// 导入 lowdb
const low = require("lowdb");
const FileSync = require("lowdb/adapters/FileSync");
// 将数据存放到 db.json 文件中 会自动创建
const adapter = new FileSync("db.json");
// 获取 db 对象
const db = low(adapter);

// 初始化数据
db.defaults({ posts: [], user: {} }).write();

写入数据:

js
// 顺序写入数据
db.get('posts') // 获取posts 数组
	.push({ id: 1, title: 'hello'}) // 向数组中添加数据
	.write(); 	// 写入数据
// 倒序插入数据
db.get("posts").unshift({ id: 2, title: "world" }).write();

获取数据:

js
// 获取指定数据
console.log(db.get("posts").find({ id: 2 }).value());
// 获取全部数据
console.log(db.get('posts').value());

删除指定数据:

js
let res = db.get("posts").remove({ id: 1 }).write(); // 返回被删除的数据
console.log(res); // res 为被删除的数据

更新数据:

js
db.get("posts") // 获取 posts 数组
  .find({ id: 2 }) // 查找 id 为 2 的数据
  .assign({ title: "good" }) // 更新数据
  .write(); // 写入数据

1.5 使用lowdb保存账单数据

安装 shortid 用于将保存的 json 数据进行 id 自动编号

npm i shortid

js
// 导入 lowdb
const low = require("lowdb");
const FileSync = require("lowdb/adapters/FileSync");
// 将数据存放到 db.json 文件中
const adapter = new FileSync(__dirname + "/../data/db.json"); // 绝对路径,保存在data文件夹下的db.json 文件中
// 获取 db 对象
const db = low(adapter);
// 导入 shortid  用于将json数据自动编号
const shortid = require("shortid");
....
// 新增记录/处理获取到的表单数据
router.post("/account", (req, res) => {
  console.log(req.body); // 获取请求体的数据
  // req.body 以对象形式获取到表单的数据,name为键名,value为值
  // 生成 id
  let id = shortid.generate();
  // 将获取到的数据保存到 db.json中
  db.get("accounts")
    .unshift({ id: id, ...req.body }) // ...req.body 扩展运算符
    .write();
  res.send("ok");
});

1.6 完善成功提醒功能

在成功添加数据后,跳转到一个新的页面

创建一个 success.ejs 页面用于跳转使用,并接收传递的值

html
      <h1>:) <%= msg%></h1>
      <p><a href=<%= url%>>点击跳转</a></p>

在路由中渲染页面,并传递参数

js
  res.render("success",{msg: "添加成功~",url:'/account'});

1.7 账单列表动态渲染

设置路由:

获取 db.json 中存储的全部账单信息,并使用模板引擎渲染到页面中。

js
router.get("/account", function (req, res, next) {
  // 获取到 db.json 中 accounts 对象存储的所有账单数据 
  let accounts = db.get('accounts').value();
  res.render("list",{accounts});  // 动态渲染到账单列表中
});

使用模板引擎渲染账单数据:

html
<div class="accounts">
  <% for(let item of accounts) {%> // 遍历数据
  <div class="panel <%= item.type==='-1'? 'panel-danger' : 'panel-success'%>">//三目运算  type = -1 支出  type = 1 收入
    <div class="panel-heading"><%= item.time%></div>
    <div class="panel-body">
      <div class="col-xs-6"><%= item.title %></div>
      <div class="col-xs-2 text-center">
        <span
          class="label <%= item.type==='-1'? 'label-warning' : 'label-success'%>"
          ><%= item.type==='-1' ? '支出' : '收入'%></span
        > // 根据 type 的值判断是收入还是支出
      </div>
      <div class="col-xs-2 text-right"><%=item.account%></div> // 动态渲染值
      <div class="col-xs-2 text-right">
        <span class="glyphicon glyphicon-remove" aria-hidden="true"></span>
      </div>
    </div>
  </div>
  <% }%>
</div>

1.8 删除账单功能

删除账单的某条记录,只需在db.json/数据库中将该条记录删除掉。

  • 设置删除路由

    js
    router.get("/account/delete", (req, res) => {
      // 获取传递来的 id
      let id = req.query.id; // get 请求使用query
      // 删除db.json 中的对应 id 的数据
      db.get("accounts").remove({ id }).write(); // 找到accounts数组中对应id的值并删除
      // 重定向到列表页 即将地址栏的 地址改为 /account 获取列表页面
      res.redirect("/account");
      // 或渲染一个删除成功的页面
      // res.render('success',{msg:"删除成功",url:"/account"})
    });
  • 在 ejs 模板引擎中 发送请求并传递要删除列表的 id 值

    html
    <!-- 向服务端发送get请求并传递查询字符串-->
    <a href="/account/delete?id=<%= item.id%>" 
       <!-- /account/delete 使用绝对路径,与地址栏的协议域名端口进行拼接-->
      ><span class="glyphicon glyphicon-remove" aria-hidden="true"></span
    ></a>

二、连接mongodb数据库

2.1 创建mongodb数据库连接

在入口文件www 导入db.js

安装 mongoose

npm i mongoose

导入db 并将www 下的全部代码 放在db 的成功回调函数中

在连接数据库成功后,才进行http服务的创建

js
// 导入 db 函数
const db = require('../db/db');
// 调用 db 函数
db(()=>{  // 连通数据库后,在进行创建http服务的操作
/**
 * Module dependencies.
 */

var app = require('../app');
var debug = require('debug')('accounts:server');
var http = require('http');

.......

})

2.2 准备模型文件

在models 文件夹下创建 AccountModels.js 文件

创建账单的模型对象

js
// 导入 mongoose
const mongoose = require("mongoose");
// 创建文档的结构对象
let AccountSchema = new mongoose.Schema({
  title: {
    // 标题
    type: String,
    required: true,
  },
  time: Date, // 日期
  type: {
    // 类型
    type: Number,
    default: -1,
    enum: [1, -1],
  },
  account: {
    type: Number,
    required: true,
  }, // 金额
  remarks: String, //备注
});
// 创建模型对象  创建和使用 accounts 集合
let AccountModel = mongoose.model("accounts", AccountSchema);
// 暴露模型对象
module.exports = AccountModel;

2.3 插入数据库

2.3.1 moment格式化时间

使用moment 模块将 2024-02-10 格式的日期转换为 new Date格式的日期

安装 moment

npm i monent

使用:

js
// 1. 2024-02-10 格式转换为 Date()对象格式
moment('2024-02-10').toDate()
// 2. 将Date()对象转换为 2024-02-10 格式
moment(Date).format('YYYY-MM-DD')
2.3.2 将数据插入数据库
js
const AccountModel = require('AccountModel');

router.post("/account", (req, res) => {
  console.log(req.body); // 获取请求体的数据
  // 插入数据库
  AccountModel.create({
    ...req.body, // ES6 解构
    // 修改time属性的值
    time:moment(req.body.time).toDate()
  }).then((data)=>{
    res.render("success", { msg: "添加成功~", url: "/account" });
  }).catch((err)=>{
    res.render("success", { msg: "添加失败~", url: "/account" });
  })
});

2.4 读取数据库

读取数据库bilibili 中的 accounts 集合,并将其中的数据渲染到 页面中

js
router.get("/account", function (req, res, next) {
	AccountModel.find()
    .sort({ time: -1 }) // 按照添加时间降序排列
    .then((data) => {
      res.render("list", { accounts:data ,moment}); 
      // 动态渲染到账单列表中 并将momemt对象传入到模板中 对time进行格式化转换
    })
    .catch((err) => {
      res.status(500);
      res.render('success',{msg:'出错了',url:'/account/create'})
    });
});

2.4 删除文档

删除对应的记录时,将数据库中对应的文档删除

js
router.get("/account/delete", (req, res) => {
  // 获取传递来的 id
  let id = req.query.id;
  // 删除mongodb中的数据
  AccountModel.deleteOne({_id:id})
    .then((date)=>{
      // 重定向到列表页 即将地址栏的 地址改为 /account
      res.redirect("/account");
    })
    .catch((err)=>{
      res.render('success',{msg:'删除失败',url:'/account'})
    })
});

2.6 完善删除功能

使用 js完善删除操作,防止用户误删除

html
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
  <script>
    let delBtn = $(".delBtn"); // jq选择器返回的是一个jquery对象
    delBtn.click(function (e) {
      // 这里不需要对其遍历绑定事件
      if (confirm("您确定要确认删除吗?")) {
        // window.confirm()方法返回true(确定)/false(取消)
        return true;
      } else {
        // 阻止a标签的默认行为
        e.preventDefault();
      }
    });
</script>

三、结合 API 功能

3.1 创建接口文件

在routes 文件夹下 创建一个 account.js 用于设置 API 接口 ,用于接收安卓,ios用户上传的数据和返回一个 json 格式的数据,交由前端工程师进行手机端页面的渲染。(前后端分离)

web 文件夹下的 accounts.js 用于响应浏览器发出 http 请求,并由服务器端web渲染页面(服务器端渲染)

设置 api 接口中间件

js
const accountRouter = require("./routes/api/account");
......
app.use("/api", accountRouter);  // 路由前缀加一个 /api

使用 RESTful API

3.2 获取账单全部数据接口

js
router.get("/account", function (req, res, next) {
  // 读取mongodb数据库中 accounts集合数据
  AccountModel.find()
    .sort({ time: -1 }) // 按照添加时间降序排列
    .then((data) => {
      // 成功获取数据,将json数据返回给客户端
      res.json({
        // 响应编号
        code: "0000", // 成功
        // 响应信息
        msg: "读取成功",
        // 响应的数据
        data: data,
      });
    })
    .catch((err) => {
      res.json({
        // 获取数据失败处理
        code: "1001", // 失败
        msg: "读取失败",
        data: null,
      });
    });
});

请求方式:GET

url:/account

请求体:none

3.3 添加账单接口

json
router.post("/account", (req, res) => {
  // 表单验证,对表单数据进行验证,返回不同的错误信息
   ........
  // 插入到 mongodb 数据库
  AccountModel.create({
    ...req.body, // ES6 解构
    // 修改time属性的值
    time: moment(req.body.time).toDate(),
  })
    .then((data) => {
      res.json({
        code: "0000",
        msg: "添加成功",
        data: data,
      });
    })
    .catch((err) => {
      res.json({
        code: "1001",
        msg: "添加失败",
        data: "null",
      });
    });
});

请求方式:POST

url:/account

请求体:row:{

"title": "买西瓜",

"time": "2022-06-10",

"type": -1,

"account": 30,

"remarks": "天太热了"

}

3.4 删除账单接口

js
router.delete("/account/delete", (req, res) => {
  // 获取传递来的 id
  let id = req.query.id;

  // 删除mongodb中的数据
  AccountModel.deleteOne({ _id: id })
    .then((date) => {
      res.json({
        code: "0000",
        msg: "删除成功",
        data: date,
      });
    })
    .catch((err) => {
      res.json({
        code: "1003",
        msg: "删除失败",
        data: null,
      });
    });
});

请求方法:DELETE

url: /account/delete?id=xxx

请求体:none

3.5 获取单个账单接口

js
router.get("/account/:id", (req, res) => {
  // 获取 id参数
  let {id} = req.params; // 对req.params对象进行结构
  // 查询数据库
  AccountModel.findById(id)
    .then((data) => {
      res.json({
        code: "0000",
        msg: "获取成功",
        data: data,
      });
    })
    .catch((err) => {
      res.json({
        code: "1004",
        msg: "获取失败",
        data: null,
      });
    });
});

请求方法:GET

url: /account/id

请求体:none

3.6 更新账单接口

js
router.patch("/account/:id", (req, res) => {
  // 获取要更新账单的 id 值
  let { id } = req.params;
  // 更新数据库     将要更新的数据放在请求体中
  AccountModel.updateOne({ _id: id }, req.body)
    .then((data) => {
      // 获取更新后的数据
      AccountModel.findById(id).then((data) => {
        res.json({
          code: "0000",
          msg: "更新成功",
          data: data,
        });
      });
    })
    .catch((err) => {
      res.json({
        code: "1005",
        msg: "更新失败",
        data: null,
      });
    });
});

请求方法:PATCH

url: /account/id

请求体:row:{ // 要更新的数据

"account":20000

}

四、注册登录页面&用户校验session

4.1 注册页面

创建auth.js 路由文件

设置路由渲染 注册页面

js
var express = require("express");
var router = express.Router();
// 导入 moment 用于格式化时间
const moment = require("moment");

// 导入 AccountModel 模型
const AccountModel = require("../../models/AccountModel");

router.get('/reg',(req,res)=>{
  // 响应登录页面
  res.render('auth/reg');
});

module.exports = router;

4.2 注册用户

4.2.1 渲染用户注册页面

登录注册使用同一个 ejs 模板

js
// 响应注册页面
router.get("/reg", (req, res) => {
  // 响应登录页面
  res.render("auth/reg", { title: "注册", action: "/reg" }); 
});

添加表单nam属性和设置form表单

4.2.2 创建 UserModel 模型对象
js
// 导入mongoose
const mongoose = require("mongoose");
// 创建文档的结构对象
const UserSchema = new mongoose.Schema({
  // 用户名
  username: {
    type: String,
    required: true,
    unique: true,
  },
  // 密码
  password: {
    type: String,
    required: true,
  },
});

// 创建模型对象
let UserModel = mongoose.model("users", UserSchema);
// 暴露模型对象
module.exports = UserModel;
4.2.4 密码加密

md5:单向不可逆加密

安装 npm i md5

使用:

js
// 导入 md5 模块
const md5 = require("md5");
// 使用 md5 对密码进行单向加密处理
password: md5(req.body.password)
4.2.3 配置路由

将表单数据添加到数据库中,并做出响应

js
router.post("/reg", (req, res) => {
  // 获取请求体的数据
  console.log(req.body);
  // 将数据插入到mongodb中    使用md5 将密码单向加密处理
  // 在插入前先判断是否存在该用户名,用户名作为唯一标识
  UserModel.findOne({ username: req.body.username }).then((data) => {
    res.render("success", { msg: "该用户名已存在", url: "/reg" });
  });
  UserModel.create({ ...req.body, password: md5(req.body.password) })
    .then((data) => {
      // 响应注册成功页面
      res.render("success", { msg: "注册成功", url: "/login" });
    })
    .catch((err) => {
      res.status(500), res.render("success", { msg: "注册失败", url: "/reg" });
    });
});

4.3 用户登录

4.3.1 渲染登录页面
js
// 用户登录页面
router.get("/login", (req, res) => {
  res.render("auth/reg", { title: "登录", action: "/login" });
});
4.3.2 登录路由配置
js
router.post("/login", (req, res) => {
  // 在数据库中查找用户信息判断账号和密码是否匹配
  let { username, password } = req.body; // 解构获取 username 和 password
  UserModel.findOne({
    // 用户名和密码同时匹配
    username: username,
    password: md5(password),
  }).then((data) => {
    if (data !== null) {
      // 匹配成功 重定向到账单页面
      return res.redirect("/account");
    }
    // 渲染登录失败页面
    res.render("success", { msg: "账号或密码错误", url: "/login" });
  });
});

4.4 写入session

使用session 限制浏览器用户访问账单列表,只能在登录后才能访问。

4.4.1 安装session

npm i express-session connect-mongo

connect-mongo:连接mongodb 数据库将session存储在mongodb数据库中。

4..4.2 导入express-session

在app.js中导入session 和 MongoStore模块

js
const session = require("express-session");
const MongoStore = require("connect-mongo");
4.4.3 设置session 中间件

在 app.js 中设置session中间件

js
// 导入配置文件
const { DBHOST, DBPORT, DBNAME } = require("./config/config");

app.use(
  session({
    name: "sid", //设置cookie的name,默认值是:content.sid
    secret: "atguigu", //参与加密的字符串(又称签名)
    saveUninitialized: false, //是否为每次请求都设置一个cookie用来存储session的is
    resave: true, //设置为true表示每次请求都会重新保存session(无感刷新)
    store: MongoStore.create({ // 设置存储session的数据库
      mongoUrl: `mongodb://${DBHOST}:${DBPORT}/${DBNAME}`,
    }),
    cookie: {
      httpOnly: true, // 开启前端无法通过JS操作cookie
      maxAge: 1000 * 60 * 60 * 24 * 7, // 设置有效期7天
    },
  })
);
4.4.4 创建session

当用户名和密码匹配成功,用户登录时将session要存储的用户信息写入数据库,并通过响应体向前端返回sid

js
req.session.username = data.username;  // 写入 用户名
req.session_id = data._id;	// 写入 用户_id

登录成功后,会在数据库创建一个session集合存储session信息

![image-20240208180944917](./NodeJS 案例 记账本.assets/image-20240208180944917.png)

4.5 登录检测

下次登录时,浏览器会自动携带 sid 作为cookie发送给服务器端,服务器获取到sid 后会在数据库中查找session数据

![image-20240208193124534](./NodeJS 案例 记账本.assets/image-20240208193124534.png)

声明路由中间价,获取 session中的数据判断用户是否已经登录且session未过期

js
let checkLoginMiddleware = (req,res,next) => {
  if(!req.session.username){ // 获取不到该 sid 对应的session数据
    return res.redirect("/login") //跳转到登录页面
   }
   next();  // 执行后面的回调
}

为需要登录检测的页面和操作进行登录判断

便于以后其它路由使用该中间件,将其独立成一个文件,并暴露出去,在需要使用的地方进行引入

  • 创建一个 middlewares 文件夹

  • 创建一个 checkLoginMinddleWares.js文件

js
// 判断是否登录的路由中间件
module.exports = (req,res,next) => {
  if(!req.session.username){ // 获取不到该 sid 对应的session数据
    return res.redirect("/login") //跳转到登录页面
   }
   next();  // 执行后面的回调
}
  • 导入checkLoginMinddleWares中间件
js
const checkLoginMiddleware = require("../../middlewares/checkLoginMiddleware")
  • 使用路由中间件
js
router.get("/account", checkLoginMiddleware, function (req, res, next) {
 .......
});

4.6 退出登录

退出登录时销毁session数据(将session设置为立即过期且将数据库中的session数据删除,网站请求头携带的sid无法匹配到数据),下次访问时需要重新登录

设置退出登录路由

js
router.get("/logout", (req, res) => {
  req.session.destroy(() => {   // 清除session,将数据库中的session删除掉
    res.render("success", { msg: "退出登录,bey", url: "/login" });
  });
});

4.7 跨站请求伪造 CSRF

使用 Live Server 打开attack.html 文件

attack.html

html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <link rel="stylesheet" href="http://127.0.0.1:3000/logout">
</head>
<body>
</body>
</html>

刷新 记事本已经登录的页面,将会退出登录,记事本网站就遭受到了攻击。

跨站:由网站B 向网站A 发送请求,且会携带A网站的cookie

A网页会退出登录原因:B网站携带了A网站的cookie,访问了A网站的退出登录路由,导致session被销毁。

解决办法:

将退出登录的请求改为post

在前端通过post请求去退出登录,使用form或ajax

使用ajax会出现跨域,form表单提交数据时,是通过HTTP协议的POST或GET方法直接向服务器发送请求,这个过程并不涉及到JavaScript的同源策略限制,因此不会出现跨域问题。

但本机通过使用post请求去跨站请求伪造还是可以实现

4.8 首页与404页

4.8.1 首页跳转

将根目录 / 重定向到 /account 路由下

js
// 添加首页的路由规则
router.get("/",(req,res)=>{
  //将路由根目录/  重定向到 /account 页面
  res.redirect("/account"); 
})
4.8.2 设置404页面

在app.js 中设置错误处理,渲染404页面

创建一个 404.ejs 模板引擎用于渲染使用

![image-20240208205905836](./NodeJS 案例 记账本.assets/image-20240208205905836.png)

公益404

引入公益404的script标签即可

五、接口token响应与校验

设置token 校验,当移动端app 用户登录后才能访问aip中的数据。浏览器端使用session进行登录验证。

5.1设置登录路由响应token

创建auth.js 路由文件

安装 jsonwebtoken

npm i jsonwebtoken

引入 jwt

js
const jwt = require("jsonwebtoken");

创建当前用户的token

js
        let token = jwt.sign(
          { _id: data._id, username: username }, // 用户数据
          "abcde", // 加密字符串
          {
            expiresIn: 60 * 60 * 24 * 7, // token 7天有效期
          }
        );

用户名和密码与数据库验证成功,响应token

js
        res.json({
          code: "0000",	// 响应码
          msg: "登录成功",	// 响应信息
          data: token,	 // 发送token
        });

设置用户名和密码与数据库验证失败的响应

js
      res.json({
        code: "2002",
        msg: "用户名或密码错误",
      });

5.2 配置路由规则

在app.js 中 导入并使用auth.js

js
// 导入api auth 路由文件
const authAipRouter = require("./routes/api/auth")
.....
// 使用
app.use("/api",authAipRouter)

5.3 登录接口测试

![image-20240209231207562](./NodeJS 案例 记账本.assets/image-20240209231207562.png)

5.4 校验token

5.4.1 封装中间件文件

在middlewares文件夹下创建 checkTokenMiddleware.js 文件 用于设置token校验的路由

js
// 导入 jwt 用于校验token
const jwt = require("jsonwebtoken");

// 声明中间件,用于token的获取和校验
module.exports = (req, res, next) => {
  // 获取token
  let token = req.get("token");
  // 判断 token 是否存在
  if (!token) {
    return res.json({
      // 使用return 语句停止执行后续代码
      code: "2003",
      msg: "token缺失",
      data: null,
    });
  }
  // 校验token	   加密字符串
  jwt.verify(token, "abcde", (err, data) => {
    if (err) {
      return res.json({
        code: "2004",
        msg: "token校验失败", // token过期或不正确
        data: null,
      });
    }
    // token 存在且校验成功,继续执行后续中间件
    next();
    // 此处token只用于检验用户是token是否正确且token不过期,
    // 不用于获取用户信息再做数据匹配和获取
  });
};
5.4.2 引入路由中间件
js
const checkTokenMiddleware = require("../../middlewares/checkTokenMiddleware");
5.4.3 使用路由中间件
js
router.get("/account", checkTokenMiddleware, function (req, res, next) {
  ......
});

5.5 token功能完善

5.5.1 将token加密字符串添加到配置文件

在token加密和解密时都用到了加密字符串,将token加密字符串添加到配置文件config.js中,便于修改。

config.js

js
// 配置文件
module.exports={
  DBHOST:'127.0.0.1', // 主机名
  DBPORT:27017,   // 数据库端口号
  DBNAME:'bilibili', // 数据库名
  secret:"abcde"  // token 加密字符串
}

导入 config.js 配置文件中的secret加密字符串

js
const { secret } = require("../config/config");

使用 secret

js
jwt.verify(token, secret, (err, data) => {}
5.5.2 保存当前用户信息

当记事本项目为一个多人用户时,需要根据当前用户的信息,去获取响应的数据

在 checkTokenMiddleware.js 的token校验成功的回调中,保存当前用户的信息

js
req.user = data; // 将当前用户信息,保存到req的user属性中

获取在其它路由规则中获取当前用户的信息

js
userdata = req.user;
console.log(userdata);
/*
{
  _id: '65c49ee4b6b61362ee7de935',
  username: 'admin',
  iat: 1707549503,
  exp: 1708154303
}
*/
5.5.3 req属性

中间件可以访问操作req和res对象,将一些获取到的数据存储到req请求和res响应对象中

因此可以可以在路由回调中,通过 req.body req.query req.session....... 来获取数据

六、本地域名

6.1 什么是本地域名

本地域名就是 只能在本机使用的域名 一般在开发阶段使用

6.2 操作流程

编辑文件 C:\windows\Ststem32\dirvers\etc\hosts

127.0.0.1	www.xin.com

如果修改失败,可以修改该问价的权限,将users的权限设置为完全控制。

![image-20240210163042919](./NodeJS 案例 记账本.assets/image-20240210163042919.png)

6.3 使用本地域名

在浏览器中输入

http://www.xin.com:3000/login

http://127.0.0.1:3000/login 效果相同都可以进行访问项目

6.4 设置默认端口号

http 默认端口号为 80

https 默认端口号为 443

修改项目的端口号

在www.js文件中修改 端口号3000为80

js
// var port = normalizePort(process.env.PORT || '3000');
var port = normalizePort(process.env.PORT || '80');

此时就可以通过 www.xin.com 访问项目了

6.5 原理

在地址栏输入 域名 之后,浏览器会先进行 DNS 查询,获取该域名对应的IP地址

请求会发送到 DNS 服务器,可以 根据域名返回IP地址

可以通过 ipconfig/all 查看本机的 DNS 服务器

hosts 文件也可以设置域名与IP 的映射关系,在发送请求之间,可以通过该文件获取域名的IP地址

![image-20240210164808760](./NodeJS 案例 记账本.assets/image-20240210164808760.png)

七、项目上线

7.1 代码上传仓库

7.1.1 点击git初始化仓库
7.1.2 忽略node_modules文件夹

在项目根目录下创建.gitignore文件,并添加以下代码

js
// 忽略node_modules文件夹下的文件
node_modules
// 忽略upload中的文件
/public/upload

当文件夹被忽略后,在项目目录中会变色image-20240210165933226

可以在vsode中设置全局忽略 node_modules文件夹

7.1.3 提交到本地仓库

输入备注信息:init 点击提交

7.1.4 创建远程仓库

使用码云或github创建远程仓库

复制远程仓库的地址:https://github.com/webXin123/account.git

7.1.5 添加远程存储库
image-20240210171331475

根据提示输入用户名和密码

7.1.6 提交

点击 发布Branch

7.1.7 查看

在github中查看提交的项目

7.2 购买云服务器

7.2.1 打开阿里云官网

https://www.aliyun.com/

产品 -> 云服务器ECS -> 立即购买

7.3 连接云服务器并安装软件

7.4 克隆代码启动服务

git clone url 克隆代码

gti pull 拉取更新

7.5 购买域名及域名解析

7.6 配置https证书