node基于express+mongodb项目的整体结构搭建和逻辑抽离

一、为什么需要逻辑抽离

这是我用express实现的一个缩减版的注册功能,如下:

  • app.js
const express = require("express");
const app = express();// 连接数据库
const mongoose = require("mongoose");
// 连接数据库myTest
mongoose.connect("mongodb://localhost:27017/myTest", { useNewUrlParser: true, useUnifiedTopology: true }).then(() => {console.log("数据库连接成功");}).catch((err) => {console.log(err, "数据库连接失败");});
// 创建集合规则
const userSchema = new mongoose.Schema({userName: {type: String,required: true,},passWord: {type: String,required: true,},
});
// 创建Users集合
const User = mongoose.model("User", userSchema);const cors = require("cors");
// 解决跨域
app.use(cors());// 支持json格式的请求体数据
app.use(express.json());
// 支持urlencoded格式的请求体数据
app.use(express.urlencoded({ extended: true }));
// 注册接口
app.post("/register", async (req, res) => {const userModel = new User(req.body);// 将用户注册的信息保存到数据库中const dbBack = await userModel.save();user = dbBack.toJSON();// 以json格式返回给客户端res.status(201).json({code: 201,msg: "注册成功",user,});
});// 监听3000端口
app.listen(3000, () => {console.log("server is running at http://localhost:3000");
});

目录结构如下:
在这里插入图片描述

可以看到的是所有的逻辑,创建服务器、链接数据库、创建集合、注册都在app.js文件中,这只是一个简单的注册功能,可能看上去还不是很乱,但是当我们后面项目越来越大,所做的功能越来越多,显然,将所有的逻辑功能都集中在app.js中是不行的,这样既不利用开发,也不利于后期维护,所以对逻辑的抽离,和整体项目结构的划分是很有必要的。

二、项目结构的搭建

1. 路由模块的抽离

这里我们先不考虑注册功能是否能实现了,只是单纯的做个逻辑划分,看抽离完成之后的路由是否在客户端可以访问到就可以了。

新建router文件夹,在router文件夹下新建index.jsuser.js文件,如图:
在这里插入图片描述

  • index.js
const express = require('express');
const router = express.Router();
router.use('/user', require('./user'));
module.exports = router;
  • user.js
const express = require("express");
const router = express.Router();
router.post("/register", (req, res) => {console.log(req.body);  res.send("register");   
});
module.exports = router;

然后我们去app.js中引入router/index.js

  • app.js
const express = require("express");
const app = express();app.use(express.json());
const router = require("./router");
app.use('/api/v1', router);// 监听3000端口
app.listen(3000, () => {console.log("server is running at http://localhost:3000");
});

经过这样的抽离以后我们再访问user/register接口,就需要加上/api/v1前缀,如图:
在这里插入图片描述
到这里,我们看app.js文件中的代码逻辑是不是很清晰了,我们只是用express创建了一个web服务器,然后引入了一个路由文件,就实现了接口访问的逻辑。

那么接着看看user.js文件
在这里插入图片描述
这里的注册逻辑,我没有具体实现,但是后期我们实现的时候,这里逻辑肯定比这复杂,还有就是,user模块,肯定也不止这一个注册功能,比如还有登录、修改密码、修改头像等等,这时如果还是将接口的具体实现都集中在user.js中,最后的这个效果就和我们开始分析的app.js文件一养,逻辑太过复杂,导致user.js文件太过臃肿,不利于维护,所以,我们将user.js文件中的接口具体实现也单独抽离出来

新建一个controller文件夹,controller文件夹下新增一个userController.js文件

  • userController.js
// 用户注册
exports.register =(req, res) => {console.log(req.body);  res.send("register");   
}

修改user.js文件

const express = require("express");
const router = express.Router();
const userController = require("../controller/userController");
router.post("/register", userController.register);
module.exports = router;

这样比如我们要加个登录的功能,只需要在user中加一行代码就可以了,例如:

const express = require("express");
const router = express.Router();
const userController = require("../controller/userController");
router.post("/register", userController.register);
router.post("/login", userController.login);
module.exports = router;

而具体的登录逻辑,我们可以在userController中实现。
好啦,到这里我们的路由的抽离基本就可以了,接下来我们来具体实现一下开始的在app.js中的那个注册功能。

2. 数据库操作部分抽离

基于上面的路由抽离,要实现注册功能,我们可以在userController中来加上数据方面的操作,如下:

  • userController.js
// 连接数据库
const mongoose = require("mongoose");
// 连接数据库myTest
mongoose.connect("mongodb://localhost:27017/myTest", { useNewUrlParser: true, useUnifiedTopology: true }).then(() => {console.log("数据库连接成功");}).catch((err) => {console.log(err, "数据库连接失败");});
// 创建集合规则
const userSchema = new mongoose.Schema({userName: {type: String,required: true,},passWord: {type: String,required: true,},
});
// 创建Users集合
const User = mongoose.model("User", userSchema);// 用户注册
exports.register = async (req, res) => {const userModel = new User(req.body);// 将用户注册的信息保存到数据库中const dbBack = await userModel.save();user = dbBack.toJSON();// 以json格式返回给客户端res.status(201).json({code: 201,msg: "注册成功",user,});
};

此时我们在访问注册接口:
在这里插入图片描述
在这里插入图片描述
可以看到数据库users集合中成功添加了一条数据,说明我们的注册功能是实现了。但是,我们在回头看看userController.js,此时的数据库连接操作,创建集合都集中在了这里,就算我们创建users集合放在userController.js中合理,但是数据库连接呢?

我们的整个项目肯定不止涉及一个集合,每当新建个controller时,我们都要连接数据库,所以这个连接数据库的操作显然是一个可复用的功能,所以我们先把这一块的逻辑抽离出来。

新建一个model/index.js文件

  • model/index.js
const mongoose = require("mongoose");
mongoose.connect("mongodb://localhost:27017/myTest").then((res) => {console.log("mongo链接成功");}).catch((err) => {console.log(err);console.log("mongo链接失败");});module.exports = mongoose;

修改userController.js

const mongoose = require("../model/index");
// 创建集合规则
const userSchema = new mongoose.Schema({userName: {type: String,required: true,},passWord: {type: String,required: true,},
});
// 创建Users集合
const User = mongoose.model("User", userSchema);// 用户注册
exports.register = async (req, res) => {const userModel = new User(req.body);// 将用户注册的信息保存到数据库中const dbBack = await userModel.save();user = dbBack.toJSON();// 以json格式返回给客户端res.status(201).json({code: 201,msg: "注册成功",user,});
};

但是我们的userController中应该只注重接口的的具体实现,集合的规则和创建也应该单独抽离出来
新建model/userModel.js

  • model/userModel.js
const mongoose = require("mongoose");
// 创建集合规则
const userSchema = new mongoose.Schema({userName: {type: String,required: true,},passWord: {type: String,required: true,},
});module.exports = userSchema;

修改model/index导出

  • model/index.js
const mongoose = require("mongoose");
const { mongopath } = require("../config/config.default");
mongoose.connect(mongopath).then((res) => {console.log("mongo链接成功");}).catch((err) => {console.log(err);console.log("mongo链接失败");});module.exports = {User: mongoose.model("User", require("./userModel")),
};

修改userController.js

  • userController.js
const { User } = require("../model/index");// 用户注册
exports.register = async (req, res) => {const userModel = new User(req.body);// 将用户注册的信息保存到数据库中const dbBack = await userModel.save();user = dbBack.toJSON();// 以json格式返回给客户端res.status(201).json({code: 201,msg: "注册成功",user,});
};

至此我们实现了controller功能的单一性,也将数据操作都抽离到了model文件夹下,这时我们在用postman访问一下register接口,如下:
在这里插入图片描述
在这里插入图片描述
成功访问!

至此呢我们项目的基本架构就算完成啦。
整体目录结构如下:
在这里插入图片描述
将路由相关访问抽离到router文件夹下,接口逻辑的具体实现抽离到controller文件夹下,数据库集合相关操作抽离到model文件夹下,一些配置文件抽离到config文件夹下,我们的入口文件app.js只是创建一个服务器,具体的功能我们只需要引入对于的模块就可以了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/2122.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

关于Windows 11 docker desktop 运行doris 容器时vm.max_map_count=2000000的设置问题

需要一个简单的测试环境,于是准备用docker启动一个1fe 1be的简单玩一下 如果be容器启动后再去修改 /etc/sysctl.conf sysctl -w vm.max_map_count2000000 这个参数是没用的,be仍然会启动失败 这时可以打开cmd wsl --list C:\Users\pc>wsl --list …

ES系列--打分机制

一、文档打分机制 当你通过关键字搜索相关文档时,可能会出现多个文档,这些文档的顺序是通过一个max_score属性的大小从高到低顺序展现出来的,max_score属性就是我们所说的评分。而这个评分是通过一个文档打分机制计算出来的。 二、打分原理 …

JDK 下载 华为云镜像站 地址

通常去 Oracle 官网下载 JDK,速度很慢而且需要账号登入 Oracle 官网下载地址 https://www.oracle.com/cn/java/technologies/downloads/archive/ JDK 下载 华为云镜像站 地址 https://repo.huaweicloud.com/java/jdk/ 我们下期见,拜拜!

CentOS 8 GLIBC升级失败系统崩溃抢修实战

CentOS 8 GLIBC升级失败系统崩溃抢修实战 1. 恐怖的问题2. 参考解决方案3. 抢修实战3.1 准备工作3.2 抢修流程3.3 解决启动后Permission Denied3.3.1 参考方案3.3.2 解决 4. 总结 服务器为CentOS 8,支持glibc版本为2.28,但编译一个工具的glibc需求版本为…

MySQL-概述-数据模型SQL简介

数据库:DataBase(DB),是存储和管理数据的仓库数据库管理系统:DataBase Management System(DBMS),操作和管理数据库的大型软件。SQL:Structured Query Language&#xff0…

spring boot 多模块项目非启动模块的bean无法注入(问题记录)

之前有说我搭了一个多模块项目,往微服务升级,注入的依赖在zuodou-bean模块中,入jwt拦截, Knife4j ,分页插件等等,但是启动类在system中,看网上说在启动类上加SpringBootApplication注解默认扫描范围为自己…

物联网(IoT):连接未来的万物之网

引言: 物联网(Internet of Things,简称IoT)是指通过各种智能设备和传感器,使物体能够互联互通、收集和共享数据的网络。随着科技的不断进步和智能设备的普及,物联网的应用呈现出爆发式增长,对各…

哇~真的是你呀!今天是LINUX中的RSYNC服务

目录 前言 一、概述 二、特性 三、rsync传输模式 四、rsync应用 五、格式 六、配置文件 七、守护进程传输 八、rsyncinotfy实时同步 一、概述 rsync是linux 下一个远程数据同步工具;他可通过LAN/WAN快速同步多台主机间的文件和目录,并适当利用rsync 算法减少数据的…

微信小程序(二)

目录 1、input标签 一、表单绑定 1、数据绑定 2、输入获取 二、网络请求 1、介绍 2、注意 3、使用 4、基于Promise封装 三、自定义组件 1、创建 2、父向子组件通信 3、子向父组件通信 4、生命周期 四、vant weapp组件库 1、配置 2、使用 进入本章前的拓展&#…

Jmeter(119)-函数threadNum妙用

今天的接口场景是:有N个用户需要每隔5秒去查询一次数据,也就是说N个用户会去循环执行同一个接口。一开始的时候将用户参数化时使用了counter, 要执行2个线程3次循环,发现每次循环时,接口中用户参数的数据就会不一样&am…

【AutoGluon_01】自动机器学习框架的安装与示例

文章目录 一、安装二、示例一 AutoGluon预测目标数据1、导入数据2、训练3、预测4、评估5、小结 三、示例二 AutoGluon多模态预测(Multimodal Prediction)1、导入数据2、训练3、预测4、评估 四、示例三 AutoGluon进行时间序列预测1、导入数据2、训练3、预…

漫谈大数据时代的个人信息安全(三)——“点赞之交”

大数据时代的个人信息安全系列三:“点赞之交” 1. 点赞之交2. 点赞诈骗3. 个人信息保护小贴士 互联网就像公路,用户使用它,就会留下脚印。 每个人都在无时不刻的产生数据,在消费数据的同时,也在被数据消费。 近日&am…

AJAX异步请求JSON数据格式

目录 前言 1.AJAX的实现方式 1.1原生的js实现方式 1.2JQuery实现方式 2.1语法 3.JSON数据和Java对象的相互转换 3.1将JSON转换为Java对象 3.2将Java对象转换为JSON 前言 AJAX:ASynchronous JavaScript And XML 异步的JavaScript 和 XML。 Ajax 是一种在…

微信小程序

页面跳转&#xff1a; navigator 显示跳转 跳转普通页面跳转 tab栏页面 open-type"switchTab"回退上一个 open-type"navigateBack" <navigator url"/pages/detail/detail?usernamecc&addresschengdu">detail</navigator>js跳…

springboot sentinel 整合 规则详情和代码实现-分布式/微服务流量控制

文章目录 sentinel控制台安装目标版本说明sentinel 规则整合验证pom.xml配置注解拦截资源控制规则---内存模式测试controller客户端接入控制台 测试sentinel控制台接口调用 下一篇&#xff1a;配置持久化策略规则外传 sentinel控制台安装 下载地址&#xff1a;https://github.…

Go语言之并发编程练习,GO协程初识,互斥锁,管道:channel的读写操作,生产者消费者

GO协程初识 package mainimport ("fmt""sync""time" )func read() {defer wg.Done()fmt.Println("read start")time.Sleep(time.Second * 3)fmt.Println("read end") }func listenMusci() {defer wg.Done()fmt.Println(&qu…

vue 下拉框显示从后端请求的数据

下拉框<!-- 给产品添加推广人员弹出框 --><el-dialog :title"titlePerson" :visible.sync"openAddPerson" width"1000px" append-to-body><el-select v-model"this.bindingProductPerson.recommendId" filterable plac…

【C++】-list的具体使用

&#x1f496;作者&#xff1a;小树苗渴望变成参天大树&#x1f388; &#x1f389;作者宣言&#xff1a;认真写好每一篇博客&#x1f4a4; &#x1f38a;作者gitee:gitee✨ &#x1f49e;作者专栏&#xff1a;C语言,数据结构初阶,Linux,C 动态规划算法&#x1f384; 如 果 你 …

HTML学习 第一部分(前端学习)

参考学习网站: 网页简介 (w3schools.com) 我的学习思路是&#xff1a;网站实践视频。 视频很重要的&#xff0c;因为它会给你一种开阔思路的方式。你会想&#xff0c;噢&#xff01;原来还可以这样。这是书本或者网站教程 所不能教给你的。而且&#xff0c;对一些教程&#…

Ubuntu搭建docker+laradock

使用Ubuntu搭建dockerlaradock windows 下载Ubuntu工具二选一 链接&#xff1a;https://pan.baidu.com/s/154K6MKdFZxWqaTn2q-6MSQ 提取码&#xff1a;06lc https://www.jianshu.com/p/b7e11d0dbe8c借鉴地址&#xff1a;https://zhuanlan.zhihu.com/p/547169542 备注&#x…