ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Express.js中使用fs.readFile读取JSON文件返回用户接口

Express.js中使用fs.readFile读取JSON文件返回用户接口 这次处理一个很小但很典型的 Express.js 场景后端没有数据库只有一个 JSON 文件需要用户信息时用 Node.js 自带的 readFile 把它读出来再通过 Express 路由返回给前端。很多人刚接触 Express 时会把所有数据塞进内存变量里或者一上来就接 MongoDB/MySQL。实际上对学习阶段、原型验证、内部工具来说直接用 fs.readFile 读一个本地 JSON 文件就够了代码量少也不依赖额外数据库服务。这里说的 readFile是 Node.js 内置 fs 模块的fs.readFile不是任何第三方库。核心流程是浏览器请求/users或/users/:idExpress 收到请求后在路由处理函数里调用fs.readFile读取data/users.json拿到文件内容后通过JSON.parse转成对象数组再决定返回全部用户还是只返回匹配 id 的用户。整个服务仅依赖 express 一个 npm 包其余都用 Node.js 原生能力完成非常适合用来理解“Express 路由 异步文件读取 JSON 接口”这条链路。本期内容是 Echo 系列“5分钟学编程 · Express.js 篇”的第 12 篇定位是能直接上手跑通的最小示例。文章会先带你搭一个最小 Express 服务准备一份可用的 users.json 用户数据然后分两种风格实现 readFile 读取一种是传统的 err-first 回调写法另一种是fs.promises.readFile配合 async/await 的写法。最后用 curl 做接口验证并讨论文件路径、JSON 解析、批量读取和后续扩展方向。适合正在学 Node.js/Express 接口开发的读者也适合需要快速给前端人物 mock 用户数据但不想引入数据库的人。1. 核心知识点速览在开始敲代码之前先把本期的关键信息列出来。下面的表格可以帮助你快速判断这个教程需要安装什么、能学到什么以及适合用在什么场景。项目本期内容教程主题Express.js 中使用 Node.js fs.readFile 读取用户信息核心能力通过 HTTP 接口读取本地 JSON 文件中的用户数据主要依赖express文件读取使用 Node.js 内置 fs 模块数据存储本地 JSON 文件 data/users.json适合学习和原型验证接口设计GET /users 返回全部用户GET /users/:id 返回单个用户阅读方式fs.readFile 回调风格 fs.promises.readFile async/await 风格前置条件已安装 Node.js 和 npm建议使用 LTS 版本服务端口示例统一使用 3000端口冲突可自行修改生产建议文件存储只用于学习和小型实验生产环境建议接数据库如果你关注的是“这个例子里有没有数据库、要不要装 Redis、要不要写 Docker”答案是都不需要。你只需要一个能运行 Node.js 的电脑、一个编辑器以及能执行 npm install 的网络环境。跟着文章往下走跑通一个只读用户接口的成本很低。这里也要理解边界本教程不是讲如何设计一个用户系统也不是讲如何用 Express ORM 数据库做生产级 CRUD。它更像是一块“积木”帮你把“接口请求”和“文件读取”这两个知识点拼起来。后续如果要增加 POST、PUT、DELETE、登录鉴权、字段权限完全可以在此基础上继续延伸。2. 适用场景与学习边界2.1 这个 Demo 能解决什么问题从实际使用角度看这个 Demo 至少有四类用途。第一适合刚接触 Express 的初学者。你不需要理解数据库连接池、ORM 映射、事务这些概念只需要知道“readFile 是异步的读取完成后要处理回调或 await”就能写一个可以运行的用户查询接口。第二适合项目前期的 mock 接口阶段。前端希望先拿到用户列表数据结构后端暂时不想搭建数据库可以直接在项目里放一个 users.json通过 Express 暴露接口。这样前端联调时不需要等真实数据库。第三适合本地工具或内部管理后台。用户量不大、访问量也不高希望用最少的依赖从文件里读取配置项或人员名单。readFile 直接从文件系统读取即可。第四适合想拆解 fs 模块用法的读者。你会发现文件读取成功不代表数据格式正确格式正确不代表一定能找到目标用户三层判断本身就是很好的练习。2.2 不建议在哪些场景使用文件型存储有它的适用边界。用户量变大后每个请求都完整读取一个 JSON 文件并 JSON.parse会造成不必要的磁盘 IO 和 CPU 开销并且文件内数据会随着用户增长越来越大读取时间也会变长。高并发场景下多个请求同时读同一个文件也不是问题因为读取不会产生写污染但如果你未来要往文件里写用户数据就要考虑并发写、文件锁、数据一致性问题。如果你的应用需要做频繁增删改查需要按用户名模糊搜索需要给用户字段加索引需要不同服务实例共享同一份用户数据那就不适合继续用 JSON 文件。相对稳妥的方案是换成 SQLite、PostgreSQL 或 MySQL。SQLite 对本地单机项目非常友好PostgreSQL 和 MySQL 则更适合正式服务端部署。另外要提醒一点用户信息属于敏感数据。教程示例中使用的是张三、李四这类演示数据不涉及真实隐私但如果你把真实姓名、手机号、身份证号放在 JSON 文件里又没有接口权限控制一旦服务被访问到就可能引发数据风险。生产环境使用用户数据必须做脱敏、授权、审计并避免把静态数据目录直接暴露给浏览器。3. 环境准备与前置条件本教程不涉及 GPU 或模型部署环境要求非常简单。你需要准备以下内容。操作系统方面Windows、macOS、Linux 都可以。Node.js 是必须的建议安装 LTS 版本。如果不确定本机是否安装了 Node.js可以先打开终端执行下面两条命令正常输出版本号表示环境可用。node -v npm -v如果命令找不到需要先到 Node.js 官网下载对应系统的 LTS 安装包或者使用 nvm 这类版本管理工具安装。没有 Node.js后面的 Express 项目完全跑不起来。编辑器方面VS Code 或 WebStorm 都可以能编辑 JavaScript 文件即可。终端工具方面Windows 可使用 PowerShell 或 CMDmacOS/Linux 使用 Terminal。后续请求接口时curl 是自带工具不需要额外安装。目录结构上建议新建一个专门的项目目录避免在系统盘随机位置创建文件。整篇文章推荐的目录是这样的express-readfile-demo/ ├── data/ │ └── users.json ├── src/ │ └── server.js └── package.json为什么把数据文件和代码分开因为 data 是数据资源src 是服务端代码。如果后续要加配置、日志、静态资源都可以按目录继续扩展。把 users.json 放在 data 目录下也能避免不小心把它放到 public 静态目录里导致被前端直接下载。如果你的电脑上已经安装过 npm 镜像源或者有离线环境安装依赖会更快。互联网正常的情况下直接使用 npm 官方源就好不需要额外配置。4. 项目初始化与 Express 服务启动4.1 初始化项目并安装 express第一步进入终端执行下面一组命令。它会创建项目目录、进入目录、初始化 package.json并安装 express 依赖。mkdir express-readfile-demo cd express-readfile-demo npm init -y npm install expressnpm init -y会生成一个默认的 package.json里面的 main 字段默认是 index.js。后续文章推荐把入口文件放在 src/server.js所以可以手动修改 package.json或者不强求保持一致。只要启动命令里的路径正确package.json 里的入口字段不会影响手动执行node src/server.js。安装成功后项目里会出现 node_modules 目录package.json 的 dependencies 中也会出现 express 版本记录。如果你在别的电脑上重新部署只要保留 package.json执行npm install就能安装回依赖不需要复制 node_modules。4.2 准备用户数据文件接下来创建 data 目录和用户数据文件。数据内容可以根据自己的需求调整但结构最好是有 id 的对象数组。这里用一个简单示例[ { id: 1, name: 张三, email: zhangsanexample.com, city: 北京 }, { id: 2, name: 李四, email: lisiexample.com, city: 上海 }, { id: 3, name: 王五, email: wangwuexample.com, city: 广州 } ]注意这里必须保证 JSON 语法正确。很多初学者明明代码逻辑没问题但 readFile 之后 JSON.parse 一直报错检查半天才发现是 users.json 里多了一个逗号或者使用了注释。JSON 文件不能写 JavaScript 注释也不能在最后一个元素后面加多余的逗号。4.3 编写基础 Express 服务在 src 目录下创建 server.js。先用不到完整业务逻辑只搭一个能访问根路径的服务确认 express 能正常启动。const express require(express); const app express(); const PORT 3000; app.get(/, (req, res) { res.send(Express readFile demo); }); app.listen(PORT, () { console.log(Server is running at http://localhost:${PORT}); });启动命令如下node src/server.js终端显示Server is running at http://localhost:3000就说明基础服务已经启动。此时用浏览器打开http://localhost:3000页面会显示Express readFile demo。这里先别急着继续先确认端口是否能被访问。如果终端报错Error: listen EADDRINUSE: address already in use :::3000说明 3000 端口已经被占用需要把代码里的 PORT 改成 3001 或其他端口或者在启动命令里通过环境变量动态指定。5. readFile 读取用户信息接口实现这一章是核心内容。我们要实现两个接口GET /users返回全部用户列表GET /users/:id根据 id 返回单个用户。两个接口都会用到 readFile 去读取 data/users.json。5.1 理解 fs.readFile 的异步回调机制fs.readFile是异步读取文件的函数。它不会阻塞 Node.js 事件循环读取完成后会执行传入的回调函数。回调函数第一个参数是错误对象 err第二个参数是文件内容 data。如果不指定编码data 默认是 Buffer如果指定utf8data 就是字符串。因为 Node.js 的回调风格是错误优先所以写代码时第一个判断总是if (err)。拿到 data 之后的下一步不是直接返回给客户端而是判断数据是否能被 JSON.parse 解析。parse 过程中也可能抛出异常因此要包一层 try catch。5.2 使用回调风格读取全部用户先用一个简化的全部用户接口说明整体逻辑const express require(express); const fs require(fs); const path require(path); const app express(); const PORT 3000; const usersFilePath path.join(__dirname, .., data, users.json); app.get(/users, (req, res) { fs.readFile(usersFilePath, utf8, (err, data) { if (err) { console.error(err); return res.status(500).json({ code: 500, message: 读取用户文件失败 }); } try { const users JSON.parse(data); res.json({ code: 0, data: users }); } catch (parseErr) { console.error(parseErr); res.status(500).json({ code: 500, message: 用户文件格式错误 }); } }); }); app.listen(PORT, () { console.log(Server is running at http://localhost:${PORT}); });这个版本关注三件事readFile 读取文件JSON.parse 转换数据res.json 返回 JSON。读取成功后的响应结构使用了{ code: 0, data: users }code 表示业务状态码便于前端统一判断。这里有一个容易忽略的点当文件读取失败时如果直接使用res.json而没有加 return后续代码仍然可能继续执行导致 Express 出现无法发送响应头的问题。所以错误分支一定要写 return或者将 if else 逻辑写完整。上面代码中 return 的作用就是中断当前函数执行。5.3 引入路由参数并返回单个用户接下来在相同文件里增加/users/:id路由。这里的:id是路径参数拿到之后通过req.params.id获取。由于 URL 里的参数是字符串而 users.json 里的 id 是数字直接比较会永远找不到因此需要用Number(req.params.id)做一次转换。app.get(/users/:id, (req, res) { const userId Number(req.params.id); fs.readFile(usersFilePath, utf8, (err, data) { if (err) { console.error(err); return res.status(500).json({ code: 500, message: 读取用户文件失败 }); } try { const users JSON.parse(data); const user users.find((item) item.id userId); if (!user) { return res.status(404).json({ code: 404, message: 未找到 id 为 ${userId} 的用户 }); } res.json({ code: 0, data: user }); } catch (parseErr) { console.error(parseErr); res.status(500).json({ code: 500, message: 用户文件格式错误 }); } }); });整个逻辑是读取文件 - 解析 JSON - 在数组中 find 目标用户 - 找不到就返回 404找得到就返回用户对象。这个链路覆盖了 readFile、JSON.parse、Array.find 和 Express 状态码组合使用是本期最值得吸收的部分。5.4 改造成 fs.promises.readFile async/await回调风格写两层嵌套还能接受但一旦后续增加更多判断回调嵌套会变得很难维护。Node.js 从较新版本开始提供了fs.promises模块我们可以用require(fs).promises拿到 Promise 风格的 readFile再通过 async/await 顺序写出同步代码的观感。改造后的完整入口文件如下const express require(express); const fs require(fs); const path require(path); const app express(); const PORT 3000; const usersFilePath path.join(__dirname, .., data, users.json); const fsp fs.promises; app.get(/users, async (req, res) { try { const data await fsp.readFile(usersFilePath, utf8); const users JSON.parse(data); res.json({ code: 0, data: users }); } catch (err) { console.error(err); res.status(500).json({ code: 500, message: 服务器读取用户文件失败 }); } }); app.get(/users/:id, async (req, res) { const userId Number(req.params.id); try { const data await fsp.readFile(usersFilePath, utf8); const users JSON.parse(data); const user users.find((item) item.id userId); if (!user) { return res.status(404).json({ code: 404, message: 未找到 id 为 ${userId} 的用户 }); } res.json({ code: 0, data: user }); } catch (err) { console.error(err); res.status(500).json({ code: 500, message: 服务器内部错误 }); } }); app.listen(PORT, () { console.log(Server is running at http://localhost:${PORT}); });与回调风格相比async/await 版本不再需要手动处理第二层回调。readFile 的异常会直接进入 catch 块JSON.parse 抛出的异常也可以被同一个 catch 捕获。代码结构从“嵌套”变成“顺序”更适合新手理解也更适合后续维护。需要区分的是这里用 try catch 包住 await并不会让所有文件读取都变成同步操作。await 只是让异步代码的书写方式更接近同步底层仍然是异步执行。两个/users和/users/:id请求同时到达时Node.js 依然能处理其他事件不会因为一个文件读取请求而阻塞全部请求。5.5 文件路径为什么建议使用 __dirname path.join文章中的代码使用了path.join(__dirname, .., data, users.json)而不是直接写./data/users.json。原因在于__dirname始终指向当前模块文件所在目录。在 src/server.js 里__dirname 指向 src 目录所以..回到项目根目录再拼接 data/users.json 就能稳定定位到数据文件。如果直接写相对路径它依赖的是进程启动时的当前工作目录。当你在项目根目录执行node src/server.js时没问题但如果某天你换到其他目录启动这个脚本相对路径就可能指向不存在的文件readFile 就会报 ENOENT 错误。使用 path.join 也比直接写字符串拼接__dirname /../data/users.json更稳因为 path.join 会按当前操作系统的路径分隔符处理。Windows 下不用太担心路径反斜杠和正斜杠的差异。如果你把 users.json 和 server.js 放在同一个目录可以简化成path.join(__dirname, users.json)。但既然项目分层是 src 和 data保留..是正确做法。6. 功能测试与效果验证完成代码后最重要的事情是验证接口是否符合预期。本节给出完整的启动、请求和判断方法。6.1 启动 Express 服务在项目根目录执行node src/server.js看到终端输出Server is running at http://localhost:3000就说明服务启动成功。如果修改了代码需要先按 Ctrl C 停掉当前进程再重新执行启动命令。不要开着旧进程测新代码。6.2 使用 curl 测试接口打开一个新终端分别执行下面三条命令curl http://localhost:3000/users curl http://localhost:3000/users/1 curl http://localhost:3000/users/999第一条命令预期返回全部用户数组{code:0,data:[{id:1,name:张三,email:zhangsanexample.com,city:北京},{id:2,name:李四,email:lisiexample.com,city:上海},{id:3,name:王五,email:wangwuexample.com,city:广州}]}第二条命令预期返回 id 为 1 的用户对象{code:0,data:{id:1,name:张三,email:zhangsanexample.com,city:北京}}第三条命令因为 users.json 中没有 id 为 999 的用户预期返回 404{code:404,message:未找到 id 为 999 的用户}6.3 判断成功的标准接口是否正常可以从三个层面判断。HTTP 状态码层面/users 应返回 200/users 下面不存在的路径应返回 404文件读取失败时应返回 500。业务状态码层面code 字段为 0 表示查询成功code 为 404 或 500 表示失败。数据内容层面/users 的 data 字段应该是数组/users/:id 的 data 字段应该是用户对象而不是套了一层多余数组。前端在调用时通常会先判断response.ok再判断 body.code。如果后端没有统一返回结构前端就要额外处理字符串、对象、错误文本等多种格式。本教程在示例中返回 code 字段也是一种工程化习惯方便前端统一拦截错误。6.4 验证失败时的排查方向如果访问 /users 时终端报错优先查看终端里是否打印了错误堆栈。常见的失败原因有三类。第一文件路径不对。报错信息通常是ENOENT: no such file or directory。检查 src/server.js 里 usersFilePath 是否指向了真实存在的 data/users.json。如果项目根目录没有 data 目录需要先创建。第二JSON 解析失败。常见错误是Unexpected token } in JSON。用编辑器打开 users.json 检查是否存在多余逗号、中英文引号混用、文件尾部缺少右中括号等问题。JSON 没有注释不要用//注释。第三路由顺序问题。/users/:id应该放在/users后面并没有严格顺序要求因为 Express 可以使用精确路径匹配不会把/users和/users/:id混淆。但如果你以后增加/users/me这类固定路径一定要把它放在/users/:id之前否则/users/me会被当成 id 为 me 的动态参数处理。7. 接口 API 与批量任务扩展严格来说上一章实现的/users和/users/:id已经是一个可用的 HTTP API。前端可以直接用 fetch 调用也可以被其他后端服务通过 curl 调用。这一节继续讨论 API 参数、统一返回和批量读取扩展。7.1 前端如何调用这个接口假设你的前端页面运行在 http://localhost:5173后端接口位于 http://localhost:3000可以使用下面这段 fetch 读取全部用户fetch(http://localhost:3000/users) .then((res) res.json()) .then((body) { if (body.code 0) { console.log(body.data); } });读取单个用户时把 id 放到路径里const userId 1; fetch(http://localhost:3000/users/${userId}) .then((res) res.json()) .then((body) { if (body.code 0) { console.log(body.data); } });这里需要注意跨域问题。如果你直接从浏览器里的另一个端口访问 Express 接口浏览器会拦截跨域请求。本教程不讨论跨域但你在真正开发前后端分离项目时可能需要为 Express 增加 cors 中间件或手动设置响应头。这个问题常见于本地联调建议提前了解。7.2 统一返回格式与错误处理上一章接口返回的格式并不完全相同。成功时返回{ code: 0, data: ... }404 时返回{ code: 404, message: ... }500 时也返回 message。如果前端希望在出错时也拿到 code这个结构是一致的但如果你希望错误时也必须有 data 字段可以进一步约定所有响应都带 code、message、data 三个字段即使错误时 data 为 null。建议在后续开发中封装一个统一响应函数而不是在每个路由里重复写 status 和 json。这样能显著减少出错概率。比如抽成一个sendSuccess(res, data)和sendError(res, status, message)路由内部只关心业务逻辑。7.3 单个用户一个文件Promise.all 批量读取之前的设计是一个 users.json 里放所有用户。实际工作里有时会遇到另一种设计每个用户一个 JSON 文件文件放在 data/users 目录下。这时候要返回全部用户就不能只 readFile 一次需要读取目录下的多个文件并汇总结果。这里给出一个批量读取多个用户文件的示例。先把代码需要的目录结构调整成express-readfile-demo/ ├── data/ │ └── users/ │ ├── 1.json │ ├── 2.json │ └── 3.json然后修改路由逻辑使用fs.promises.readdir读取目录再通过Promise.all并发读取所有文件。const usersDir path.join(__dirname, .., data, users); app.get(/users, async (req, res) { try { const files await fsp.readdir(usersDir); const jsonFiles files.filter((file) file.endsWith(.json)); const readPromises jsonFiles.map(async (file) { const content await fsp.readFile(path.join(usersDir, file), utf8); return JSON.parse(content); }); const users await Promise.all(readPromises); res.json({ code: 0, data: users }); } catch (err) { console.error(err); res.status(500).json({ code: 500, message: 批量读取用户文件失败 }); } });从“readFile 读取用户信息”的角度看这个批量读取扩展依然以单个 readFile 为核心只是通过 readdir 拿到文件列表再用 Promise.all 并行执行多个 readFile。文件数量不多时这种写法清晰高效。如果文件数量很大比如到上千个一次性并发读取所有文件可能造成文件描述符压力需要引入分批读取或并发限制机制。7.4 什么情况需要引入正式任务队列如果读取单个文件本身已经足够快不需要额外设计任务队列。但如果你在扩展场景中不只是 readFile而是要在读取每个用户文件后做 OCR、图像处理、模型推理或第三方 API 调用单次请求就会变成耗时任务。这种情况下建议把任务放到异步队列中处理而不是让 HTTP 请求一直等待所有文件处理完成。比较好的做法是先接收任务并返回任务 id后台通过队列逐个处理前端再通过轮询或 WebSocket 获取处理结果。Node.js 社区常见的方案包括 BullMQ、Redis 队列和更轻量的内存任务列表。这些不是本期重点只在遇到真实耗时场景时再做迁移。8. readFile 性能观察与注意事项虽然 Express readFile 不需要 GPU 或显存但文件读取依然有性能边界需要说清楚。8.1 readFile、readFileSync 与 fs.promises.readFile 的差异很多新手会混淆 Node.js 里三种读取文件的方式。这里用一张表说明。读取方式是否阻塞代码风格适用场景fs.readFileSync同步阻塞try/catch 同步启动时加载配置、工具脚本fs.readFile异步不阻塞回调函数路由内读取请求数据fs.promises.readFile异步不阻塞async/await try/catch路由内读取推荐优先使用在 Express 路由处理函数里不建议使用 readFileSync。因为 readFileSync 会阻塞事件循环在读写较大的文件时其他请求会被卡住影响服务吞吐量。启动时读取少量配置readFileSync 是合理的但请求处理链路中异步读取是默认标准。8.2 每次请求都重复读文件的问题当前实现的/users和/users/:id会在每次请求时都调用一次 readFile。对于一个只有几条用户数据的 JSON 文件这个成本可以忽略。但如果你把 users.json 一直增长到几万条用户每次请求都要读取整个几 MB 文件并解析 JSON响应延迟会明显增加同时磁盘 IO 也浪费在“其实只需要一个用户”的场景上。更合理的做法分几层。如果用户数据变化不频繁可以在服务启动时读文件并缓存到内存后续请求直接返回缓存数据在用户修改后手动刷新缓存或设置过期时间。如果文件变得很大可以使用数据库替换 JSON 文件数据库天然支持按 id 索引不需要每次全量加载。如果数据从远程接口获取再考虑缓存层。8.3 多实例部署时文件存储的注意点假设同一个服务部署了两份实例分别运行在不同的进程或机器上它们各自读取同一份文件。如果文件在某台机器本地另一个实例读不到同样的数据就会出现用户信息不一致。解决思路是把数据存储到共享数据库或者将 users.json 放到共享存储上并保证所有实例都能访问。不过对于学习项目本地单实例跑一个 Node 进程已经足够演示 readFile 的使用方法不需要过早考虑多实例一致性。8.4 文件编码与中文乱码问题读取文件时一定要指定编码utf8否则返回的是 Buffer。Buffer 在 JavaScript 里会被自动转成类数组对象直接 JSON.parse 会报错。即使你稍后手动调用Buffer.toString()如果文件本身是用 GBK 保存的中文仍可能乱码。最好的做法是统一把 data/users.json 保存为 UTF-8并在 readFile 的第二个参数传utf8。编辑器右下角一般会显示文件编码注意确保是 UTF-8。9. 常见问题与排查方法Express readFile 这类入门项目排查难度其实不高。下面这张表把最容易遇到的问题集中列出。问题现象可能原因排查方式解决方案启动报 EADDRINUSE3000 端口被占用终端查看报错运行 lsof -i :3000更换端口或关闭占用进程提示 Cannot find module express未安装 express 依赖查看 package.json 是否包含 express执行 npm installreadFile 报 ENOENT文件路径错误打印 usersFilePath 拼出的路径使用 __dirname path.joinJSON.parse 报 unexpected tokenusers.json 格式错误打开文件检查逗号和引号修正 JSON 文件访问接口返回空对象或乱码readFile 没指定 utf8检查第二个参数改为 fs.readFile(file, utf8)/users/1 一直查不到用户id 类型是字符串console.log 打印 req.params.id使用 Number(req.params.id)修改代码后接口不变旧进程没退出查看终端进程重启 node 进程或用 nodemon前端浏览器跨域不同端口互相请求查看浏览器 Network 报错引入 cors 或设置响应头找不到 id 时也返回 200路由里没判断 user 是否存在检查 find 后的 iffind 不到时 return res.status(404)其中端口占用和文件路径错误是出现频率最高的两个问题。端口占用可以换一个端口继续测试比如把 PORT 改成 3001。文件路径问题可以在路由处理函数里临时执行console.log(usersFilePath)然后去系统里确认这个路径到底是否存在。依赖安装失败也比较常见。如果 npm install 因为网络问题失败可以优先检查 npm 源配置或使用其他镜像源但注意不要为了安装依赖而使用非常规代理工具。安装成功后node_modules 目录会存在package-lock.json 也会生成。如果你使用的是旧版本 Express启动方式和代码略有差异。当前常规稳定版本是 Express 4.x文章中的代码基于常见稳定写法使用 Express 5 时存在部分异步错误处理差异建议遇到版本相关问题以你实际安装版本的官方文档为准。10. 最佳实践与安全建议到这里核心代码已经可以跑通。为了让你在实际项目中不只是“能用”而是“用得稳”下面给出几条工程化建议。10.1 路径尽量用绝对路径始终使用path.join(__dirname, ...)解析文件路径。这个做法可以避免“启动目录变了导致文件找不到”的问题。如果要支持外部配置可以用环境变量传入数据目录路径而不是把路径写死散落在多个路由中。10.2 优先选择异步方式并在路由内做好错误处理路由处理函数中使用fs.promises.readFile搭配 async/await能让代码顺序更清晰。每次读取文件都要处理失败分支不要让“文件不存在”“JSON 格式错误”“目标用户不存在”三个错误都裸奔给前端。建议将三个状态分别映射为 500、500、404避免前端把错误解析成成功。10.3 控制返回字段范围当前 users.json 中如果有内部字段比如 password、phone、internalRemark不应该原样返回给所有客户端。可以在返回前做一次字段过滤。最简单的方式是解构出需要的字段const safeUser { id: user.id, name: user.name, email: user.email }; res.json({ code: 0, data: safeUser });更复杂的项目可以封装 pick 函数或使用序列化工具。原则是“最小返回字段”不要把不必要的内部数据暴露给前端。10.4 不要把用户 JSON 放进 public 静态目录如果 Express 配置了express.static(public)并且你把 users.json 误放到 public 目录那么任何人都可以直接通过http://localhost:3000/users.json下载整个文件绕过你的路由逻辑。用户数据文件应该放在服务端可读、但不能被静态资源服务直接访问的位置例如 data 目录并确保没有把 data 目录作为静态资源根目录。10.5 真实用户数据必须做合规处理本教程使用的用户信息完全是演示数据。如果你拿它处理真实用户信息需要考虑数据来源的合法授权、最小化采集、脱敏展示、访问日志审计。即使只是本地测试也不要随意下载和存储真实用户的敏感信息。涉及第三方平台用户数据时还要遵守对应平台的使用条款和隐私合规要求。10.6 文件存储只是过渡方案JSON 文件存储适合在用户量小、结构简单、读取频率不高的情况下使用。当你发现需要写操作、需要并发控制、需要按字段查询、需要保证多实例数据一致时应尽早切换到 SQLite、PostgreSQL、MySQL 等数据库。不要把 JSON 文件当成长期数据库除非你能接受它的边界和风险。10.7 开发阶段推荐使用 nodemon 自动重启每次修改 server.js 都要手动重启服务开发体验一般。可以安装 nodemon 作为开发依赖然后使用npx nodemon src/server.js启动。这样修改代码后服务会自动重启提升调试效率。生产环境尽量使用 node 直接启动或配合进程管理工具不要依赖 nodemon。11. 总结与下一步这一篇解决的核心问题是在 Express.js 中如何用 Node.js 的 readFile 读取用户信息并暴露为 HTTP 接口。你已经看到不需要引入数据库借助 fs 模块和 express 就能完成一个最小但完整的读取链路。重点知识包括readFile 的异步回调机制、JSON.parse 的必要性、路由参数的类型转换、错误状态码的区分以及使用 __dirname path.join 解决路径问题。建议你先不要急着跳到数据库或大型框架。先把上面最小的服务跑通手动执行一遍 curl体会从“浏览器发起请求”到“Express 接收请求”再到“readFile 读取文件”这三个环节。最容易踩的坑有两个一个是路径没写对导致 ENOENT另一个是 req.params.id 与 JSON id 类型不一致导致查询不到用户。这两个坑以后写 Node.js 服务时会反复遇到尽早踩清楚更划算。下一步可以在这个 Demo 基础上做几个方向的扩展。第一个方向是给 /users 增加 POST 接口用 fs.writeFile 或 fs.promises.writeFile 写入新的用户第二个方向是把用户数据从 JSON 文件迁移到 SQLite体会文件存储与关系型数据库的差异第三个方向是为 /users/:id 增加鉴权中间件只有携带有效 token 的请求才允许读取用户信息。每一步都不需要替换全部代码而是继续加深对 Express 路由和 Node.js 文件模块的理解。如果这篇内容对你有帮助可以先收藏备用。等真正需要写一个文件读接口时直接翻到第 5 章的完整代码复制后改成自己的数据目录即可。
返回列表