# express服务基础版 **Repository Path**: action_erlang/express_server ## Basic Information - **Project Name**: express服务基础版 - **Description**: 一个使用express开发的后端服务 1、mongodb的操作 2、中间件的处理 3、包含el-form的验证处理 4、邮件发送功能 5、基础的用户接口 6、邮箱验证码 7、接口登录验证 - **Primary Language**: NodeJS - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-02-24 - **Last Updated**: 2025-02-28 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 项目介绍 ## 项目文档目录 - apidoc: 接口文档目录,会自动生成 - logs: 日志文件目录,需要根据项目配置手动创建 - upload: 上传文件目录,需要根据项目需要手动创建 - docs: 存放文档文件 - image: 存放图片文件 - video: 存放视频文件 - music: 存放音频文件 - uploads: 上传大文件暂存目录,需要根据项目需要手动创建 - public:存放网页静态资源 - request: 存放请求测试文件 - src:源代码 - app.js:入口文件 - config:配置文件 - middlerware:中间件 - schedule:定时任务文件 - store: mongodb 数据库连接文件 - utils: 工具类文件 - router:路由 - services:接口逻辑 ## 项目环境 - node 版本:v20.18.0 - apidoc 版本:v1.2.0 - mongodb 版本:v4.2.5 - window 系统: win11 ## 项目依赖 - dependencies - apicache: v1.6.3 - connect-history-api-fallback: v2.0.0 - cookie-parser: v1.4.7 - cors: v2.8.5 - cross-env: v7.0.3 - express: v4.21.2 - http-errors: v2.0.0 - js-yaml: v4.1.0 - jsonwebtoken: v9.0.2 - log4js: v6.13.1 - mongodb: v6.13.1 - node-schedule: v2.1.1 - nodemailer: v6.10.0 - svg-captcha: v1.4.0 - devDependencies ## 启动命令 - start: node 启动项目 - dev: apidoc + nodemon 启动项目 - apidoc: 生成 apidoc 文档 - test: 运行简单的单元测试 ## src 目录说明 ### src/config.js 1. port: 端口号 2. logPath: 日志文件路径 3. whilelistIps: ip 白名单 4. whiteReferers: 防盗链白名单 5. db: 数据库配置 1. type: 数据库类型 2. host: 数据库地址 3. port: 数据库端口 4. user: 数据库用户名 5. password: 数据库密码 6. database: 数据库名 6. baseUrl: 接口基础路径 7. withoutToken:路由白名单 8. jwt: jwt 密钥 9. tokenExpire: token 过期时间 10. mailExpire:验证码过期时间 11. captcha: 验证码配置 1. size: 验证码大小 2. width: 验证码宽度 3. height: 验证码高度 4. fontSize: 验证码字体大小 5. noise: 验证码干扰线数量 6. color: 验证码颜色 7. background: 验证码背景颜色 8. ignoreChars: 验证码忽略字符 ### src/app.js 1. 程序入口文件 2. 实例化 express 3. 挂载中间件总文件 4. 挂载总路由文件 5. 挂载静态资源 6. 挂载 api 接口文档 7. 挂载 connect-history-api-fallback 中间件,解决前端路由刷新 404 问题 8. 处理 404 9. 捕获异常错误,防止服务崩溃 10. 根据配置文件启动 node 服务 ### src/store 1. index.js: - 数据操作类 - 自动连接数据库 - 30s 无操作断开数据库连接 - 封装数据集合操作方法 - 导入数据库格式校验文件 - 实例化数据集合操作对象 - 导出数据操作对象 2. 其他.js: 数据集合格式校验文件 ### src/scheduled 1. index.js - 定时任务管理类 - 导入定时任务文件 - 创建定时任务 - 导出定时任务管理对象 2. 其他.js - 导出 core: 定时任务 core - 导出 fn: 定时任务执行函数 ### src/utils 1. index.js: 会被继承的工具类 - objMenuKeys(data, keys): 根据 keys 枚举对象 2. utils.js: 工具类文件, 方法都是静态方法,不需要实例化 - DataType: 数据类型校验类 - getType(data): 获取数据类型 - checkType(data, type): 校验数据类型 - isEmpty(data): 校验数据是否为空 - isEqual(data1, data2): 校验数据是否相等 - MathUtils: 数学工具类 - round(num, n=0): 数字四舍五入, 保留 n 位小数 - ceil(num, n=0): 数字向上取整, 保留 n 位小数 - floor(num, n=0): 数字向下取整, 保留 n 位小数 - random(min, max): 生成随机数 - decimalize(num): 取小数部分 - StrUtils: 字符串工具类 - titleCase(str): 字符串首字母大写 - DeepUtils: 深拷贝工具类 - deepCopy(data, isNull=true): 深拷贝, 默认允许 null 值复制 - deepGet(data, path): 深读取, path 路径格式`info.name.age` - deepSet(data, path, value): 深设置, path 路径格式`info.name.age` - deepMerge(target, source, isNull=true): 深合并, 默认允许 null 值合并 - DictUtils: 字典工具类, 继承自 DeepUtils - Iterate(data): 迭代器, 返回一个迭代器对象 - ListUtils: 列表工具类, 继承自 DeepUtils - TimeUtils: 时间工具类 - format(date, format='YYYY-MM-DD HH:mm:ss'): 时间格式化 - FuncUtils: 高阶函数工具类, 返回一个函数 - curry(fn): 柯里化函数 - debounce(fn, delay=0): 防抖函数 - debounceImmediate(fn, delay=0): 防抖函数,立即执行版本 - throttle(fn, delay=0): 节流函数 - throttleImmediate(fn, delay=0): 节流函数,立即执行版本 3. email.js: 邮件工具类 - sendMail(options): 发送邮件 - to: 接收者邮箱 - subject: 邮件主题 - text: 邮件正文, 可选 - html: 邮件正文 html, 可选 - attachments: 附件, 可选 4. fs-extend.js: 文件系统增强工具类 - rmNoEmptyDir(path): 删除非空目录 - emptyDir(path): 清空目录 - readDeepDir(path): 读取目录下所有文件和子目录 - DirName: 读取结果类 - path: 目录路径 - type: 目录类型, dir 或 file - children: 子目录或文件列表 5. verify.js: 校验工具类, 继承自 index.js 的 Utils 类 - constructor(options): 构造函数 - validator: 校验规则, 可选 - intergrity: 校验完整性, 可选, 默认 false - 校验完整性: 数据超出校验规则数量时, 校验失败 - toVerify(data, isquired=true): 校验数据 - data: 待校验数据 - isquired: 是否校验必填项, 默认 true - toVerifyArray(datalist, isquired=true): 校验数据列表 - datalist: 待校验数据列表 - isquired: 是否校验必填项, 默认 true - selValidator(keys): 选择校验规则, 返回校验规则对象 - 设计目的: 接口类校验规则复用时,可给每个接口选择校验规则 6. zlib.js: 压缩工具类, 目前都是静态方法 - Compress: 压缩类 - do_gzip(input, output): gzip 压缩数据 - input: 目标文件地址 - output: 压缩文件地址 - do_deflate(input, output): deflate 压缩数据 - Decompress: 解压类 - do_ungzip(input, output): unzip 解压数据 - input: 目标文件地址 - output: 解压文件地址 - do_inflate(input, output): inflate 解压数据 7. download.js: 下载工具类 - download(res, path, filename): 下载文件 - res: 响应对象 - path: 文件路径 - filename: 文件名 - streamload(req, res, path): 音/视频传输 - req: 请求对象 - res: 响应对象 - path: 文件路径 ### src/middleware 1. index.js: 挂载中间件, 导出给 app.js - 挂载 cors(): 根据配置运行访问控制 - 挂载 express.json(): 解析请求体 - 挂载 express.urlencoded(): 解析请求体 - 挂载 cookieParser(): 解析 cookie - 挂载 log4js 中间件: 日志记录 - 挂载 firewall 中间件: 防盗链 2. firewall.js: 防盗链中间件 3. log4js.js: 日志记录中间件 4. corsMiddle.js: 跨域中间件 5. upload.js: 文件上传中间件 - uploadDocs(req, res, next): 文档上传中间件 - uploadImage(req, res, next): 图片上传中间件 - uploadMulic(req, res, next): 文件上传中间件 - uploadVideo(req, res, next): 视频上传中间件 - uploadBigFile(req, res, next): 大文件上传中间件 - mergeFile(from, to): 合并文件 - from: 合并目标目录地址 - to: 输出文件地址 ### src/router 1. index.js: 挂载路由, 导出给 app.js - 动态导入其他路由文件 - 挂载路由 - 复用 登录验证接口 逻辑,做接口登录验证中间件 2. 其他.js: 路由文件 1. 引入 express 2. 引入接口逻辑文件 3. 实例化路由对象 4. 编写接口 5. 接口模板:`router.get("/login", user.P('login'))` 6. P: 看到停车场的标志 P 而设计,把接口比作停车位,接口逻辑比作车辆。 ### src/serivces 1. template.js: 接口模板 1. 设计接口返回结果状态:200 成功, 400 失败, 401 未登录 2. 用于规范接口响应模板, 便于维护和扩展 3. 设计 P 方法, 处理接口函数 this 指向问题 4. P 方法自动捕获接口错误,并统一返回接口错误信息 5. P 方法可以获取接口处理后的接口,统一返回接口结果 2. verify.js: 校验模板, 用于复制代码,快速写校验规则 3. 接口文件夹 1. index.js: 1. 定义接口逻辑类 2. 编写接口函数 3. 接口函数都是异步的 2. verify.js: 校验规则 4. 接口设计规范 1. 接口统一使用 async/await 修饰为异步函数 2. 接口接受两个参数: - req: 请求对象 - res: 响应对象, 可选 3. 接口处理异常逻辑可直接抛出 - 统一抛出异常会被捕获,并统一返回接口错误信息 - 案例:`throw new Error('邮箱已注册')` 4. 接口某些逻辑必须使用 res 时, 在使用第二个参数,其他情况不需要 5. 接口处理成功逻辑可直接返回,方便进行函数的单元测试编写 ## test 测试文件夹 1. 根据 src/serivces 文件夹结构编写测试用例文件 2. 文件建议为`.test.js`类型 3. 使用 nodejs 内置的 assert 模块进行断言 4. 如何编写测试: 1. 引入 node:test 模块 2. 引入 node:assert 模块 3. 引入需要测试接口 4. 根据接口逻辑编写测试函数,调用接口 5. 获取接口返回值,然后使用断言判断 5. 断言方法 - 断言真假:`assert.ok(value[, msg])` - 判断空值:`assert.ifError(value[, msg])` - 判断相等:`assert.equal(actual, expected[, msg])` - 判断不相等:`assert.notEqual(actual, expected[, msg])` - 判断严格相等:`assert.strictEqual(actual, expected[, msg])` - 判断严格不相等:`assert.notStrictEqual(actual, expected[, msg])` - 判断深度相等:`assert.deepEqual(actual, expected[, msg])` - 判断深度不相等:`assert.notDeepEqual(actual, expected[, msg])` - 判断深度严格相等:`assert.deepStrictEqual(actual, expected[, msg])` - 判断深度严格不相等:`assert.notDeepStrictEqual(actual, expected[, msg])` - 判断类型:`assert.typeOf(value, name[, msg])` - 判断异常:`assert.throws(fn[, error][, msg])` - fn: 测试函数 - error: 预期的错误类型,可选 - msg: 错误信息,可选 - 判断不抛出异常:`assert.doesNotThrow(fn[, msg])` - 抛出错误:`assert.fail([msg|error])` - 如果断言逻辑无法满足测试,可以自己写 js 测试逻辑。 - 如果出现错误就使用 assert.fail 抛出自定义错误 - 没有错误就使用 assert.ok(true) 表示通过测试 6. 测试文件模板 ```js import test from "node:test"; import assert from "node:assert"; import mail from "../src/services/mail/index.js"; test("/mail/mailCanuse", async (t) => { await t.test("格式错误", async (t) => { try { await mail.mailCanuse({ query: { email: "1224710680@qq" } }); } catch (error) { assert.strictEqual(error.message, "邮箱格式不正确"); } }); await t.test("邮箱已被注册", async (t) => { try { await mail.mailCanuse({ query: { email: "1224710680@qq.com" } }); } catch (error) { assert.strictEqual(error.message, "邮箱已被注册"); } }); await t.test("正常", async (t) => { let data = await mail.mailCanuse({ query: { email: "1224710681@qq.com" }, }); assert.strictEqual(data.msg, "邮箱可用"); }); }); ``` ## requret 请求文件夹 1. vscode 需要安装插件:REST Client 2. 编写请求文件 文件名.http 3. 编写请求模板 ```http ### GET请求模板 GET http://localhost:3000/api/v1/user/logout HTTP/1.1 ### POST请求模板 POST http://localhost:3000/api/v1/user/login HTTP/1.1 Content-Type: application/json Cookie: user_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY { "email": "123@qq.com", "password": "123456" } ``` 4. 测试文件会自动处理 Set-Cookie,不需要手动复制粘贴 ## public 静态资源文件夹 - 存放静态资源 - 存放打包后的 vue 或 react 等项目文件 ## upload 上传文件夹 - docs 存放上传文档 - images 存放上传图片 - videos 存放上传视频 - music 存放上传音乐 ## uploads 上传文件夹 > 用于大文件上传时,存放临时文件 > 上传成功后,读取临时目录里的文件,进行合并 > 合并导出到upload对应文件夹内 > 删除临时目录文件