You need to enable JavaScript to run this app. Next.js服务端API读写JSON文件的读取异常排查与写入功能实现咨询
优惠活动
大模型
产品
解决方案
定价
更多

Next.js服务端API读写JSON文件的读取异常排查与写入功能实现咨询

现有代码的核心错误点
  • fs.readFile是异步IO方法,直接同步接收返回值永远拿不到文件内容,会得到一个Promise对象,后续JSON.parse必然报错
  • 直接在getStaticProps里import API路由函数调用的方式完全不符合设计逻辑:API路由是绑定在Next.js服务端请求处理链上的,依赖req、res上下文,脱离请求流程直接调用时上下文全为空,根本跑不通
  • 相对路径写死'list.json'的定位逻辑无效:Next.js进程的工作目录是项目根目录,不是pages/api目录,按这个路径永远找不到对应文件
  • 之前用fetch调用不生效的常见原因是请求路径错误:API路由的映射路径是/api/getlist,不能写相对路径./api/getlist,否则会和当前页面路由拼接成错误的请求地址
基础文件读取功能修复

修正API路由的读取逻辑

不管是走API接口请求数据,还是页面直连读文件,首先要解决路径定位和读写方法匹配两个问题:

  • 用process.cwd()拼接文件绝对路径,这个值永远指向你启动Next.js进程的项目根目录,不受内部工作区切换影响
  • 本地自用场景下,用同步读写的fs.readFileSync代替异步方法,代码逻辑更简单,也不会出现异步取值为空的问题

修正后的pages/api/getlist.js示例代码:

import fs from 'fs'
import path from 'path'

// 模块初始化时拼接好文件绝对路径,避免每次请求重复计算
const listFilePath = path.join(process.cwd(), 'pages', 'api', 'list.json')

export default function handler(req, res) {
  // 限制只允许GET请求读文件
  if (req.method !== 'GET') {
    return res.status(405).json({ message: '仅支持GET请求' })
  }

  try {
    // 指定utf8编码,避免读取到Buffer类型乱码
    const rawContent = fs.readFileSync(listFilePath, 'utf8')
    const list = JSON.parse(rawContent)
    res.status(200).json(list)
  } catch (err) {
    res.status(500).json({ message: '读取文件失败', error: err.message })
  }
}

页面端获取数据的两种正确方式

getStaticProps本身就在服务端运行,不需要绕API路由调用,两种可选方案:

  1. 构建/运行时直接读文件(性能最优,适合本地工具)
    在pages/index.js的getStaticProps中,用和上面一致的路径逻辑直接读取JSON文件,不需要引入API路由:
    import fs from 'fs'
    import path from 'path'
    
    export async function getStaticProps() {
      const listFilePath = path.join(process.cwd(), 'pages', 'api', 'list.json')
      const rawContent = fs.readFileSync(listFilePath, 'utf8')
      const list = JSON.parse(rawContent)
    
      return {
        props: { list }
      }
    }
    
  2. 客户端水合后调用API接口
    页面水合完成后,在客户端用正确的绝对路径fetch('/api/getlist')调用接口拿数据,不要在getStaticProps里硬拼API路由函数调用。
新增事件写入list.json的实现

在同一个getlist.js接口中扩展POST处理分支,逻辑是接收表单提交的新事件,更新JSON文件后返回最新列表,不需要额外新建接口:

// 接上面的handler函数,补充POST请求处理
export default function handler(req, res) {
  if (req.method === 'GET') {
    // 之前的读取逻辑保持不变
  }

  if (req.method === 'POST') {
    try {
      const newEvent = req.body
      // 基础字段校验,避免脏数据写入
      if (!newEvent?.date || !newEvent?.content) {
        return res.status(400).json({ message: '日期和事件描述为必填项' })
      }

      // 读取现有列表数据
      const rawContent = fs.readFileSync(listFilePath, 'utf8')
      const currentList = JSON.parse(rawContent)

      // 追加新事件,生成时间戳作为唯一id
      currentList.push({
        id: Date.now().toString(),
        date: newEvent.date,
        content: newEvent.content
      })

      // 按日期升序排序,直接适配时间线展示逻辑
      currentList.sort((a, b) => new Date(a.date) - new Date(b.date))

      // 写回文件,加2空格缩进,方便后续手动编辑JSON
      fs.writeFileSync(listFilePath, JSON.stringify(currentList, null, 2), 'utf8')

      // 返回更新后的完整列表给前端,前端直接更新状态不需要刷新页面
      res.status(200).json(currentList)
    } catch (err) {
      res.status(500).json({ message: '写入文件失败', error: err.message })
    }
  }
}

前端表单提交的对应逻辑:阻止表单默认跳转行为,收集日期、描述输入值为对象,通过fetch以POST方法发送到/api/getlist,拿到返回的最新列表后更新页面状态即可。

常见避坑点
  • 绝对不要写裸相对路径(比如fs.readFile('list.json')),永远通过process.cwd()拼路径,否则dev模式、生产构建模式下都可能出现路径找不到的问题
  • 本地自用工具的并发量为0,用同步读写方法完全没有性能损耗,比异步回调/Promise写法少很多调试成本
  • API路由本质是绑定Next.js请求生命周期的服务端处理函数,不是可以随意import调用的普通工具函数,脱离请求上下文调用必然报错

内容的提问来源于stack exchange,提问作者Daniel Guedes

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.11.02 14:15:03