本项目源码地址 my_express_server

参考资料 NodeJS服务端开发极速入门

准备工作

安装一些必要的全局依赖

# 全局暴力设置淘宝源
npm config set registry http://registry.npm.taobao.org/# 安装热更新工具nodemon(代码更新自动重启服务器)
npm i -g nodemon

创建Express工程

创建工程

# 创建文件夹并使用终端开发
cd my_express_server# 初始化工程
npm init -y# 安装依赖
npm i express mongodb multer jsonwebtoken

编辑入口文件

src/app.js

/* 引入依赖 */
const express = require("express");/* 创建express实例 */
const app = express();/* 定义路由接口 */
app.get("/", function (req, res) {res.send("Hello Express");
});/* 挂载到指定端口 */
const server = app.listen(8002, function () {const host = server.address().address;const port = server.address().port;console.log("应用实例,访问地址为 http://%s:%s", host, port);
});

配置启动脚本

package.json

  "scripts": {"test": "echo \"Error: no test specified\" && exit 1","start": "nodemon src/app.js"},

运行工程

npm run start

路由派发

定义路由模块

定义首页路由
src/views/indexRouter.js

const express = require("express");const router = express.Router();/* 定义路由接口 */
// GET /
router.get("/", function (req, res) {res.send("Hello From indexRouter");
});// GET /headers
router.get("/headers", (req, res, next) => {res.send(JSON.stringify({headers: req.headers,}));
});/* 使用自定义中间件 */
router.get("/testtoken", loginCheck, (req, res, next) => res.end("test ok"));module.exports = router;

定义用户路由
src/views/userRouter.js

const express = require("express");
const controller = require("../controllers/userController");
const { getUser, updateUser } = require("../models/userModel");const userRouter = express.Router();userRouter.get("/", function (req, res, next) {res.send("用户首页");
});userRouter.post("/register", async (req, res) => {res.send("register")
});userRouter.post("/login", async (req, res) => {res.send("login")
});module.exports = userRouter;

派发路由到指定模块

src/app.js 核心代码

/* 引入路由 */
const indexRouter = require("./views/indexRouter");
const userRouter = require("./views/userRouter");
const fileRouter = require("./views/fileRouter");/* 添加路由中间件 */
app.use("/", indexRouter);
app.use("/user", userRouter);
app.use("/file", fileRouter);

使用Postman测试接口

GET http://localhost:8002/get
POST http://localhost:8002/post
POST http://localhost:8002/user/register
POST http://localhost:8002/user/login

读取GET/POST数据

配置全局中间件

src/app.js

const multer = require("multer");/* 添加全局中间件 */
app.use(express.json());//json读写支持
app.use(express.urlencoded({ extended: false }));//表单支持

读取查询参数

// GET http://localhost:8002/get?a=12&b=34
router.get("/get", function (req, res) {res.send(JSON.stringify({...req.query,}));
});

读取请求体

// POST http://localhost:8002/post
// router.post("/post", urlencodedParser, function (req, res) {router.post("/post", function (req, res) {// res.send(//     JSON.stringify({//         msg:"msg from /post",//         ...req.body//     })// );res.json({msg: "msg from /post",...req.body,});
});

MongoDB的安装和基本使用

MongoDB数据库简介

  • 非关系型数据库(不能做关联查询),但小快轻,适合中小复杂度的工程
  • 存储形式为【集合+Bson文档】,可以理解为【文件夹+JSON文件】(Bson即二进制存储的Json)
  • Json数据形式为对象和数组的嵌套组合,跟JS语言具有天然亲和性
  • 因此,NodeJS后台 + MongoDB数据库成为常用的黄金组合

下载安装MongoDB

社区版下载地址

  • windows版一路傻瓜式安装即可
  • mac版安装请参见:https://www.runoob.com/mongodb/mongodb-osx-install.html

MongoDB官方文档

MongoDB中文网文档

初始化数据库

  • 使用客户端MongoDB Compass手动创建数据库
  • 在Compass终端中输入命令use 数据库名称;即可切换到指定数据库

基本增删改查CRUD命令

# MongoDB数据存储结构
# MongoDB数据服务(后台进程-无界面)
#     其它数据库
#     my_express_server数据库
#         user        collection(文件夹)
#         xx.bson     文档(文件)
# MongoDB Compass(前台进程-有界面)# 切换到指定数据库(不存在则创建)
use my_express_server;# CRUD操作
# Create创建/增加 Retrieve获取 Update更新 Delete删除# 添加一条数据(如果集合user不存在会默认创建)
db.user.insertOne({ username:"heige",password:"123456" }
);# 一次性插入多条数据(如果集合products不存在会默认创建)
db.products.insertMany( [{ item: "card", qty: 15 },{ item: "envelope", qty: 20 },{ item: "stamps" , qty: 30 },{ item: "stamps" , qty: 40 },{ item: "stamps" , qty: 50 },
] );# 删除一条数据
db.products.deleteOne( { "_id" : ObjectId("62eb3d94ad6e2095b8ea917c") }
);# 删除多条数据
db.products.deleteMany( { "item" : "stamps" }
);# $gt = greaterThan
# $lt = lessThan gte
# $gte = greaterThan or Equal
# $lte = lessThan or Equal
# qty>40的producets数据都死光光
db.products.deleteMany( { "qty" : { $gte : 40 } }
);
# name为heige 且qty位于[40,80)区间
db.products.deleteMany( { "name":"heige","qty" : { $and:[{ $gte : 40 },{ $lt : 80 },]} }
);
# name为heige 且qty小于40或大于80
db.products.deleteMany( { "name":"heige","qty" : { $or:[{ $lt : 40 },{ $gt : 80 },]} }
);# 修改数据:item为card的第一条数据 设置qty为3
db.products.updateOne({ "item" : "card" },{ $set: { "qty" : 3 } }
);# 将所有item为card数据记录的qty设为3
db.products.updateMany({ "item" : "card" },{ $set: { "qty" : 3 } }
);# 查询所有产品数据
db.products.find();
db.products.find({});# 按条件查询:查询存货小于20的所有产品数据
db.products.find( { qty: { $lt: 20 } }
);# 每页10条 取第5页 (跳过前40条然后取10条)
db.products.find( { qty: { $lt: 20 } }
)
.skip(40)
.limit(10)

NodeJS操作MongoDB数据库

安装依赖

npm i mongodb

基本CRUD

参考 菜鸟教程

工具封装

基本配置
src/db/dbconfig.js

const url = "mongodb://localhost:27017/";
const dbname = "my_express_server"
const collections = {user:"user",
}module.exports = {url,dbname,collections
}

增删改查工具封装
src/db/operation.js

const { MongoClient, ObjectId } = require("mongodb");
const { url, dbname } = require("./config");/* 获取指定集合的连接对象 */
function getCollection(collectionName) {return new Promise((resolve, reject) => {MongoClient.connect(url, function (err, db) {// if (err) throw err;if (err) {reject(err);} else {resolve({collection: db.db(dbname).collection(collectionName),db,});}});});
}/* 通用回调:无论成败皆关闭数据库连接 */
function callback({ db, resolve, reject }, { res, err }) {console.log("db callback:res/err=",res,err);db.close();if (err) {reject(err);} else {resolve(res);}
}/* 向指定集合中添加数据 */
function execCreate(collectionName, content) {return new Promise(async (resolve, reject) => {try {const { collection, db } = await getCollection(collectionName);collection.insertOne(content, function (err, res) {callback({ db, resolve, reject }, { res, err });});} catch (err) {reject(err);}});
}/* 根据条件从指定集合查询数据 */
function execRetrieve(collectionName, whereOption = {}, { skip, limit } = {}) {console.log("db execRetrieve:whereOption", whereOption);console.log("db execRetrieve:skip/limit", skip, limit);const { MongoClient } = require("mongodb");// const url = "mongodb://localhost:27017/";return new Promise((resolve, reject) => {MongoClient.connect(url, function (err, db) {// if (err) throw err;if (err) {reject(err);} else {const dbo = db.db(dbname);let data = dbo.collection(collectionName).find(whereOption);if (skip && limit) {data = data.skip(skip).limit(limit);}data.toArray(function (err, result) {db.close();// 返回集合中所有数据// if (err) throw err;if (err) {reject(err);} else {// console.log("db execRetrieve:result=", result);resolve(result);}});}});});
}/* 更新数据 */
function execUpdate(collectionName, id, content) {console.log("db execUpdate:id/content=", id, content);return new Promise(async (resolve, reject) => {try {const { collection, db } = await getCollection(collectionName);collection.updateOne({ _id: ObjectId(id) },{$set:content},function (err, res) {callback({ db, resolve, reject }, { res, err });});} catch (err) {console.log("err=",err);reject(err);}});
}/* 删除数据 */
function execDelete(collectionName, id) {return new Promise(async (resolve, reject) => {try {const { collection, db } = await getCollection(collectionName);collection.deleteOne({ _id: ObjectId(id) }, function (err, obj) {db.close();if (err) {reject(err);} else {// console.log("文档删除成功");resolve({ msg: "删除文档成功" });}});} catch (err) {reject(err);}});
}/* 对外导出增删改查四大操作 */
module.exports = {execCreate,execRetrieve,execUpdate,execDelete,
};

MVC架构简介

  • M = model = 模型层 = 只负责数据的CRUD操作
  • V = view = 视图层 = 只负责对接用户请求
  • C = controller = 控制层 = 负责具体业务逻辑的处理,其上游是视图层,下游是模型层;
  • 视图层与模型层通常具有高度可复用性,不同工程的业务逻辑处理不同,只需修改控制层代码即可;
  • 一个用户请求的真正流转次序是:View=>Controller=>Model,即视图层=>调度控制层=>数据模型层;
  • MVC架构设计思想广泛应用于各种Web项目的前后端开发中;

实现注册

视图层实现

src/views/userRouter.js

...
const controller = require("../controllers/userController");
userRouter.post("/register", async (req, res) => {const ret = await controller.register(req.body);res.json(ret)
});
...

控制层实现

src/controllers/userController.js

const model = require("../models/userModel");/* 实际处理注册请求 */
async function register({ username, password }) {// 首先查询用户名是否存在const users = await model.getUser({ username });console.log("register:existedUsers=", users);if (!users.length) {return model.addUser({ username, password });} else {return Promise.resolve({ msg: "用户名已存在" });}
}module.exports = {register,
};

模型层实现

src/models/userModel.js

const db = require("../db/operation");
const collectionName = "user"function addUser(user) {console.log("userModel addUser");return db.execCreate(collectionName, user);
}module.exports = {addUser,
};

Token验证登录

什么是JWT登录鉴权

请参考 面试官系列

JWT-Token工具封装

src/utils/jwtUtil.js

const jsonwebtoken = require("jsonwebtoken");
// const jwtSecret = "test_key";
const { jwtSecret } = require("../config");class JWT {/* 生成token 返回token*/static generate(value, expires = "7 days") {console.log("JWT generate value",value);// value 为传入值, expires为过期时间,这两者都会在token字符串中题先try {return jsonwebtoken.sign(value, jwtSecret, { expiresIn: expires });} catch (e) {console.error("jwt sign error --->", e);return "";}}/* 校验token 返回载荷或false*/static verify(token) {try {// 如果过期将返回falsereturn jsonwebtoken.verify(token, jwtSecret);} catch (e) {console.error("jwt verify error --->", e);return false;}}
}
module.exports = JWT;/* 小案例 */
// (function () {//     /* 载荷(角色/权限描述信息) */
//     const payload = {//         // uuid: "3455445-acuya7skeasd-iue7",
//         // phone: 133409899625,
//         username:"admin",
//         password:"123456"
//     };//     // 生成token 有效时长20s
//     const token = JWT.generate(payload, "3s");
//     console.log("token", token);//     // 校验token 得到payload
//     const info = JWT.verify(token);
//     console.log("verifiedRet", info);//     /* 3秒后再次校验 */
//     setTimeout(() => {//         console.log("检验过期token");
//         const info2 = JWT.verify(token);
//         console.log("info2", info2); // false
//     }, 3000);
// })();

登录实现

视图层实现

src/views/userRouter.js

const express = require("express");
const controller = require("../controllers/userController");const userRouter = express.Router();userRouter.post("/login", async (req, res) => {const ret = await controller.login(req.body);res.json(ret)
});module.exports = userRouter;

控制层实现

src/controllers/userController.js

const model = require("../models/userModel");
const JWT = require("../utils/jwtUtil");/* 实际处理登录请求 */
async function login({ username, password }) {const users = await model.getUser({ username, password });console.log("register:existedUsers=", users);// 如果登录成功 记录之let token = null;if (users.length) {token = JWT.generate({ username, password });console.log("login:token=", token);}return Promise.resolve({code: users.length > 0 ? 1 : 0,msg: users.length > 0 ? "登录成功" : "登录失败",token,});
}module.exports = {login,
};

模型层实现

src/model/userModel.js

const db = require("../db/operation");
const collectionName = "user"function getUser(user) {console.log("userModel getUser");return db.execRetrieve(collectionName,user);
}module.exports = {getUser,
};

登录鉴权

封装登录校验中间件

src/middlewars/loginCheck.js

/* cookie校验登录 */
// const loginCheck = function (req, res, next) {//     if (!req.cookies["username"]) {//         res.send("请先登录!");
//     } else {//         next();
//     }
// };/* session校验登录 */
// const loginCheck = function (req, res, next) {//     console.log("req.session", req.session);
//     if (!req.session["username"]) {//         res.send("请先登录!");
//     } else {//         next();
//     }
// };/* token校验登录 */
const JWT = require("../utils/jwtUtil");
const regToken = /Bearer (.+)/
const loginCheck = function (req, res, next) {const token = regToken.exec(req.headers["authorization"])[1]const info = JWT.verify(token);console.log("verifiedRet", info);info ? next() : res.json({token,info,msg:"请先登录"});
};module.exports = loginCheck

为接口添加登录守卫

src/views/indexRouter.js

/* 登录校验中间件 */
const loginCheck = require("../middlewares/loginCheck");const router = express.Router();// GET /headers
router.get("/headers", (req, res, next) => {res.json({headers: req.headers,});
});/* 使用自定义中间件 */
router.get("/testtoken", loginCheck, (req, res, next) => res.end("test ok"));

提供静态资源服务

配置全局中间件

src/config.js

const path = require("path");const publicPath = path.resolve("public");
const imgPath = path.join(publicPath, "img");module.exports = {jwtSecret: "jinwandalaohu",publicPath,imgPath,
};

src/app.js

/* 引入配置项 */
const { publicPath } = require("./config");
console.log("publicPath", publicPath);app.use(express.static(publicPath));//静态文件支持

从浏览器发起测试

http://localhost:8002/img/fuckoff.jpg

上传文件

配置全局上传支持中间件

npm install multer
const multer = require("multer");
app.use(multer({ dest: "/tmp/" }).array("avitar"));//上传支持

控制层实现

src/views/fileRouter.js

const express = require("express");
const fs = require("fs");
const { imgPath } = require("../config");const fileRouter = express.Router();fileRouter.post("/upload", function (req, res) {console.log(req.files[0]); // 上传的文件信息// res.json({//     files:req.files// })const des_file = imgPath + "/" + req.files[0].originalname;fs.readFile(req.files[0].path, function (err, data) {fs.writeFile(des_file, data, function (err) {if (err) {console.log(err);res.json(err)} else {response = {username:req.body.username,message: "File uploaded successfully",filename: req.files[0].originalname,};}console.log(response);res.json(response);});});});module.exports = fileRouter;

从前端页面发起POST上传请求

public/page/file_upload.html

<h3>单文件上传</h3>
<!-- 注意:enctype  type="file" -->
<!-- action="/demo/upload" method="POST" name="avitar" 都要与服务端的配置吻合 -->
<form action="/file/upload" method="POST" enctype="multipart/form-data"><input name="username" type="text"><input type="file" name="avitar"><br><input type="submit" value="单个上传">
</form><h3>多文件上传</h3>
<!-- 注意:enctype  type="file" -->
<!-- action="/file/upload" method="POST" name="avitar" 都要与服务端的配置吻合 -->
<form action="/file/upload" method="post" enctype="multipart/form-data"><input name="username" type="text"><input type="file" name="avitar" multiple /><input type="file" name="avitar" multiple /><br /><input type="submit" value="多个上传" />
</form>

获取与修改用户信息

视图层实现

src/views/userRouter.js

const express = require("express");
const controller = require("../controllers/userController");const userRouter = express.Router();/* PUT /user/heige */
userRouter.put(/\w+/, async (req, res) => {const users = await controller.getUser({ username: req.path.slice(1) })const result = await controller.updateUser(users[0]._id,req.body)res.json(result)
});/* GET /user/heige */
userRouter.get(/\w+/, async (req, res) => {const users = await controller.getUser({ username: req.path.slice(1) })res.json(users[0])
});module.exports = userRouter;

控制层实现

src/controllers/userController.js

const model = require("../models/userModel");/* 更新用户信息 */
async function updateUser(id,user){return model.updateUser(id,user)
}/* 获取用户信息 */
async function getUser(user){return model.getUser(user)
}module.exports = {updateUser,getUser
};

模型层实现

src/models/userModel.js

const db = require("../db/operation");
const collectionName = "user"function updateUser(id, user) {console.log("userModel updateUser:id",id);return db.execUpdate(collectionName, id, user);
}function getUser(user) {console.log("userModel getUser");return db.execRetrieve(collectionName,user);
}module.exports = {updateUser,getUser,
};

“这不需要测试,肯定是好的,不必担心”

Express+MongoDB服务端开发教程相关推荐

  1. NodeJS+Express+mySQL服务端开发详解

    NodeJS+Express+mySQL服务端开发详解 随着NodeJS的发展,现在已经被很多人熟知,NodeJS已经成为了前端开发人员必备的技能.本文不会对NodeJS过多介绍 如果你感兴趣可以访问 ...

  2. github nodejs mysql_GitHub - lizhuohaicode/express: nodejs服务端开发(Express+Mysql)---小k博客...

    nodejs服务端开发(Express+Mysql) 项目展示 git clone git@github.com:htmlk/express.git 2.再导入express.sql到数据库,数据库名 ...

  3. php手游服务端开发教程,【手游服务端】梦想海贼王 卡牌系列一键端服务端游戏源码+教程...

    [手游服务端]梦想海贼王 卡牌系列一键端服务端游戏源码+教程 游戏介绍: <梦想海贼王>是一款卡牌类手游,游戏以全球第一超人气动漫<海贼王>为题材,用Q版风格配合新奇多样的玩法 ...

  4. rds基于什么开发_为什么不学基于TypeScript的Node.js服务端开发?

    为什么不学?学不动了吗?!别躺下啊,我扶你起来! 我们早就知道,如今的JavaScript已经不再是当初那个在浏览器网页中写写简单的表单验证.没事弹个alert框吓吓人的龙套角色了.借助基于v8引擎的 ...

  5. 第13章 Kotlin 集成 SpringBoot 服务端开发(1)

    第13章 Kotlin 集成 SpringBoot 服务端开发 本章介绍Kotlin服务端开发的相关内容.首先,我们简单介绍一下Spring Boot服务端开发框架,快速给出一个 Restful He ...

  6. mysql第五章项目二_Todo List:Node+Express 搭建服务端毗邻Mysql – 第五章(第1节)

    点击右上方红色按钮关注"web秀",让你真正秀起来 前言 万丈高楼平地起,我们的Todo List项目也是越来越结实了.Todo List的前面4章内容都是在为Client端开发, ...

  7. WebFlux响应式编程基础之 5 webflux服务端开发讲解

    https://blog.csdn.net/qq_27093465/article/details/64124330 debug技巧 第5章 webflux服务端开发讲解 Spring5 非组塞的开发 ...

  8. 百万在线:大型游戏服务端开发

    进入手游时代,服务端技术也在向前演进.现代游戏服务端既要承载数以万计的在线玩家,又要适应快速变化的市场需求,因此,如何设计合适的架构就成了重中之重.服务端技术并不简单,作为服务端新人,全面掌握服务端技 ...

  9. 2016届360公司PHP服务端开发笔试和面试之所得所感

    这是一篇叙述自己在360公司参加笔试和面试的过程,可能面试的职位并不是你所学的方向,但是如果你能从中学到些什么或者吸取我的教训,那么作者就非常知足了.本着"学习别人是怎么失败的,活着出来的人 ...

最新文章

  1. 2022-2028年中国汽车印制电路板(汽车PCB)产业深度调研及投资前景预测报告
  2. pytorch系列 -- 9 pytorch nn.init 中实现的初始化函数 uniform, normal, const, Xavier, He initialization...
  3. 信道检测手机软件 ios_【手机软件】云听:稀有神器,移动音频的国家队,某拉雅资源它都有!...
  4. C#心得与经验(二)
  5. 设置NPM/Electron国内源
  6. Windows Mobile 5 编程体验4
  7. Python接口测试
  8. UnityShader5:基本内置变量
  9. 打开MSDTC的方法(图解)
  10. java 调用felix_使用Eclipse启动任务将展开的软件包部署到Apache Felix
  11. 一款【免费+简单+好用+性能强大】的词云(Wordcloud)制作工具(含详细介绍)
  12. matlab数字信号处理(1)——正弦信号生成与时域分析
  13. Java深克隆和浅克隆的原理及实现
  14. oracle模板数据文件,Oracle EBS如何通过命令上传XML/BI Publisher数据定义文件和模板文件...
  15. level2买股技巧_同花顺level2使用技巧
  16. 【音视频】流媒体直播实时视频延迟时间排查和剖析:gop关键帧间隔导致延迟,流媒体和播放器缓存,B帧等导致的延迟
  17. 繁凡的对抗攻击论文精读(二)CVPR 2021 元学习训练模拟器进行超高效黑盒攻击(清华)
  18. 微信小程序宠物论坛4
  19. 点击body提示自由民主的网页代码
  20. opencv for python的图像梯度算子以及canny边缘检测

热门文章

  1. 地图上如何量方位角_地图上常用到方位角来描述目标方向,从坐标纵线顺时针量至某一直线的水平角称为()...
  2. 丢失修改与数据库的一致性
  3. C++完成星形图形的输出(对循环嵌套的应用)
  4. vulnhub靶机Tr0ll1渗透笔记
  5. 对近期自我状态的反思及总结
  6. linux版本beyond compare 4 的30天试用期已过-解决方法
  7. 理论随心记——DHCPV6基础
  8. 我们工作是为了什么!
  9. 小甲鱼Python3学习笔记之第二十八讲(仅记录学习)
  10. 零基础学习Python 作业 第28章