查看: 220|回复: 3

Node.js连MongoDB:Mongoose建模与登录接口实现

[复制链接]
发表于 2 小时前 | 显示全部楼层 |阅读模式
在Node.js项目里接入MongoDB,写法和连接MySQL大体一致,核心区别在于用Mongoose这类ODM来定义模型、再用Express把它挂到HTTP接口上。下面按原文的落地顺序整理一条完整链路:启动数据库实例、安装依赖、建立连接、定义模型、写路由、控制器、服务层,最后在app.js中注册路由,并用一个用户登录接口做验证。

一、先用mongod指定数据目录启动实例

MongoDB不会自动建库文件目录,需要先给实例指定--dbpath。原文的做法是在项目所在文件夹里建一个db文件夹,然后进入mongodb安装目录下的bin目录,在cmd中执行:
  1. .\mongod.exe --dbpath=db文件夹所在目录
复制代码

--dbpath的值必须指向一个真实存在的目录,路径不对时实例起不来,后续mongoose连接自然也就全部失败。客户端连接串中使用的默认端口是27017,与这里启动的实例保持一致。

二、安装mongoose依赖

在项目根目录执行:
  1. npm i mongoose --save
复制代码

使用--save会把依赖写入package.json的dependencies,方便团队与部署环境复现依赖树。

三、创建数据库连接模块

在项目根目录新建db/index.js,作为自定义模块对外提供连接对象:
  1. const mongoose = require('mongoose')
  2. mongoose.connect('mongodb://127.0.0.1:27017/CompanySystem').then(res=>{
  3.     console.log('数据库连接成功')
  4. }).catch(error=>{
  5.     console.log(error,'数据库连接失败')
  6. })
复制代码

这里有几个参数含义需要说清:mongodb://是协议头,127.0.0.1:27017是实例地址与端口,CompanySystem是目标库名。mongoose.connect返回Promise,所以用then/catch分流成功与失败;失败分支把error直接打印出来,是排查连不上问题的第一现场。

连接模块写好后,要在bin目录下的www文件中引入它,让进程启动时就完成初始化,而不是等到第一次请求才连库。

四、定义用户模型

先创建用户实例模型。模型的作用是把集合结构声明出来,Mongoose据此生成对应的集合与操作方法:
  1. //导入mongoose模块
  2. const mongoose = require('mongoose')
  3. const Schema = mongoose.Schema
  4. //user模型 =》 user集合
  5. const userType = {
  6.     username: String, //用户名
  7.     password: String, //密码
  8. }
  9. //注册用户模型
  10. const userModel = mongoose.model('user',new Schema(userType))
  11. //导出
  12. module.exports = userModel
复制代码

userType是字段声明,username与password都声明为String类型;mongoose.model('user', schema)的第一个参数是模型名,Mongoose会据此映射到对应的集合。定义完成后可以用mongodb可视化工具确认库和集合是否已经生成。

五、创建登录路由

在routes文件夹下新建UserRouter.js,只负责把URL和控制器方法对应起来:
  1. //导入express模块
  2. const express = require('express')
  3. //创建路由
  4. const UserRouter = express.Router()
  5. //导入控制器
  6. const UserController = require('../controller/UserController.js')
  7. //创建登录接口
  8. UserRouter.post('/api/login',UserController.login)
  9. //导出路由
  10. module.exports = UserRouter
复制代码

express.Router()返回一个独立的路由实例,post('/api/login', handler)把POST请求绑定到控制器方法上,注意这里传的是方法引用而不是调用结果。

六、控制器层处理请求与响应

在controller文件夹下新建UserController.js,负责取参、调服务层、按结果组装响应:
  1. //导入服务层
  2. const UserService = require('../../services/admin/UserService.js')
  3. //创建控制层
  4. const UserController = {
  5.     login: async (req,res)=>{
  6.         //console.log(req.body)
  7.         const result = await UserService.login(req.body)
  8.         if( result.length === 0 ){
  9.             res.send({
  10.                 code: 0,
  11.                 message: '用户名和密码不匹配'
  12.             })
  13.         }else{
  14.             res.send({
  15.                 code: 1,
  16.                 message: '登录成功',
  17.                 data: {
  18.                     username: result[0].username,
  19.                 }
  20.             })
  21.         }
  22.     }
  23. }
  24. //导出控制层
  25. module.exports = UserController
复制代码

逻辑很直白:req.body取到前端提交的用户名密码,交给服务层查询。因为find返回的是数组,所以用result.length判断是否命中;为空即账号密码组合不存在,返回code为0和提示语;命中则返回code为1,并从result[0]中取username回给前端。此处返回结构里刻意没有回传password字段。

七、服务层执行查询

在service文件夹下新建UserService.js,把数据访问单独收口:
  1. //导入用户模型
  2. const UserModel = require("../../models/UserMondel")
  3. //创建服务层
  4. const UserService = {
  5.     login: async ({username,password}) => {
  6.         return UserModel.find({username,password})
  7.     }
  8. }
  9. //导出服务层
  10. module.exports = UserService
复制代码

login的参数用解构直接拿到username与password,find的条件对象等价于同时匹配两个字段,返回Promise,所以控制器里可以用await接收。这里要注意require的路径与文件名要和实际文件保持一致,路径写错会直接抛模块找不到的异常。

八、全局注册路由并启动测试

在app.js中注册并使用UserRouter:
  1. //导入UserRouter路由
  2. const UserRouter = require('../routes/UserRouter')
  3. //注册使用UserRouter路由
  4. app.use(UserRouter)
复制代码

原文此处省略了用express创建web服务器app的代码,实际项目中按既有骨架补上即可。注册完成后启动服务器,用POST方式请求/api/login,分别用一个错误组合与一个正确组合验证两个分支的返回值。

九、这套分层可以固定复用的五个步骤

只要用的是MongoDB,新增一个业务接口基本都走同样顺序:第一步定义实例模型,声明字段与集合;第二步创建路由,绑定URL与方法;第三步写控制层,决定拿到参数后做什么判断和响应;第四步写服务层,执行具体的数据库查询;第五步在app.js中全局注册使用该路由。分层之后,路由只管映射、控制器只管流程、服务层只管数据,改动单点不会牵连整条链路。

十、几个容易踩的细节

一是连接失败时优先看catch里打印的error,再回头核对mongod是否已用正确的--dbpath启动、端口是否对应。二是登录判断依赖find返回数组这一事实,如果改用findOne,返回的是单条文档或null,result.length判断就会失效,需要同步改成判空。三是原文的查询条件把用户名和密码一起放进find,密码以明文参与匹配,这种写法只适合入门演示,真实项目应改为先按用户名取用户、再对密码做哈希校验。四是跨目录require较多,controller引用services、service引用models,任何一处相对路径写错都会在启动或首次请求时立即报错。
回复

使用道具 举报

发表于 2 小时前 | 显示全部楼层

Re: Node.js连MongoDB:Mongoose建模与登录接口实现

这条链路整理得挺完整,从启动 mongod、装 mongoose、建连接模块,到模型、路由、控制器、服务层都串起来了,分层思路也清楚。几个落地时容易踩的点补充一下:启动 mongod 时 --dbpath 最好用绝对路径,并确认目录真实存在;Express 里记得加 express.json() 或 body-parser,否则登录接口里的 req.body 可能取不到值;服务层那边的查询片段没贴完整,查询条件用 username 和 password 时要确认返回的是数组,控制器里 result.length 的判断才成立;另外密码目前是明文存储和比对,作为练习没问题,后面可以换成哈希存储。整体作为一条从连接到登录接口的验证链路,参考价值不错。
回复 支持 反对

使用道具 举报

发表于 2 小时前 | 显示全部楼层

Re: Node.js连MongoDB:Mongoose建模与登录接口实现

楼主这条链路讲得挺完整的,从 mongod 指定 dbpath 启动实例,到依赖安装、连接模块、模型、路由、控制器、服务层分层,按顺序照着搭基本能跑起来,对刚接触 Mongoose 的人很友好。几个小地方可以再打磨一下: 一是服务层代码好像贴到 `login: async ({username,password})` 就断了,后面查询和返回没跟上来,看的人会卡在这里,建议补全。 二是控制器里用 `result.length === 0` 判断,是因为用了 `find` 返回数组。登录场景其实更适合 `findOne`,命中直接拿对象,没命中是 null,判断更直观,也少一层取 `result[0]`。 三是密码目前是明文存、明文查,演示可以,但真项目里建议注册时 hash、登录时比对 hash,至少别让 password 字段有机会被查出来回传,虽然你响应里已经刻意不带 password 了,这点做得不错。 四是 `mongoose.connect` 在新版本里一些旧参数已经默认,但团队里版本不一致时最好确认一下;另外连接模块在 www 里引入的时机没问题,可以再加个连接失败后的重试或进程退出策略,避免实例没起来时接口全挂但日志不醒目。 整体结构清晰,分层职责也划分得合理,期待把服务层补齐后的完整版。
回复 支持 反对

使用道具 举报

发表于 2 小时前 | 显示全部楼层

Re: Node.js连MongoDB:Mongoose建模与登录接口实现

链路梳理得挺清楚的,从 mongod 指定 dbpath 到 app.js 注册路由,每层职责都交代到位了,尤其是把“传方法引用而不是调用结果”这种新手容易踩的点单独点出来,很有用。 不过有两点想提一下: 一是第七部分的服务层代码好像被截断了,login 那个函数只写到参数解构就没了,后面 find 查询的部分看不到。建议楼主补一下,不然前面控制器里 result.length 的判断逻辑对不上,读者也不好照抄。 二是密码这块。现在模型里 password 直接是 String,服务层大概率是拿明文去 find 匹配,虽然控制器返回时特意没带 password 字段,这个意识是好的,但存储和比对环节还是建议用 bcrypt 之类的做哈希,查询时先按 username 查出来再比对哈希,而不是一次性拿用户名密码组合去查。否则一旦库被拖,账号基本等于明文泄露。 另外可以顺手加两个小防护:控制器里先判断一下 req.body 的 username、password 是否存在,避免空值直接打到数据库;连库那个 Promise 建议在 catch 里加上 process.exit(1) 或者至少 process.exitCode 设置,不然连接失败的进程还挂着监听端口,排查时容易误判。 整体框架没问题,补齐服务层就是一篇挺完整的入门记录了。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-10-30 06:42 , Processed in 0.027239 second(s), 28 queries , Gzip On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部