You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将NodeJS CRUD应用的表单数据保存到JSON文件?

如何将表单提交的数据保存到JSON文件(Node.js + Express)

1. 初始化数据存储文件

在项目根目录创建 sellers.json 文件,初始内容为一个空数组:

[]

2. 修改后端 index.js

添加表单数据解析中间件,编写接口处理数据的读写操作:

'use strict'

const fs = require("fs");
const express = require("express");
const app = express();
const path = require("path");

// 解析表单提交的数据
app.use(express.urlencoded({ extended: true }));
app.use(express.static('public'))
app.set("view engine", "ejs");
app.set("views", path.join(__dirname, "views"));

// 封装读取JSON文件的函数
const readSellers = () => {
  try {
    const data = fs.readFileSync(path.join(__dirname, 'sellers.json'), 'utf8');
    return JSON.parse(data);
  } catch (err) {
    console.error('读取文件失败:', err);
    return [];
  }
};

// 封装写入JSON文件的函数
const writeSellers = (sellers) => {
  try {
    fs.writeFileSync(path.join(__dirname, 'sellers.json'), JSON.stringify(sellers, null, 2));
    return true;
  } catch (err) {
    console.error('写入文件失败:', err);
    return false;
  }
};

// 获取所有卖家数据的接口
app.get("/get-sellers", (req, res) => {
  const sellers = readSellers();
  res.json(sellers);
});

// 保存新卖家数据的接口
app.post("/add-seller", (req, res) => {
  const { name, domain, type, id } = req.body;
  const sellers = readSellers();
  
  // 检查ID是否唯一(可选,避免重复)
  const exists = sellers.some(seller => seller.id === id);
  if (exists) {
    return res.status(400).json({ success: false, message: 'ID已存在' });
  }

  sellers.push({ name, domain, type, id });
  const success = writeSellers(sellers);
  
  if (success) {
    res.json({ success: true, message: '保存成功' });
  } else {
    res.status(500).json({ success: false, message: '保存失败' });
  }
});

// 编辑卖家数据的接口(可选)
app.post("/edit-seller", (req, res) => {
  const { name, domain, type, id } = req.body;
  const sellers = readSellers();
  const index = sellers.findIndex(seller => seller.id === id);
  
  if (index === -1) {
    return res.status(404).json({ success: false, message: '卖家不存在' });
  }

  sellers[index] = { name, domain, type, id };
  const success = writeSellers(sellers);
  
  if (success) {
    res.json({ success: true, message: '编辑成功' });
  } else {
    res.status(500).json({ success: false, message: '编辑失败' });
  }
});

// 删除卖家数据的接口(可选)
app.post("/delete-seller", (req, res) => {
  const { id } = req.body;
  const sellers = readSellers();
  const filteredSellers = sellers.filter(seller => seller.id !== id);
  
  const success = writeSellers(filteredSellers);
  
  if (success) {
    res.json({ success: true, message: '删除成功' });
  } else {
    res.status(500).json({ success: false, message: '删除失败' });
  }
});

app.get("/", ( req, res) => {
    res.render("home.ejs");
})

app.listen(3000, ()=>{
    console.log("listening on port 3000..");
})

3. 修改前端 script.js

更新表单提交逻辑,通过接口与后端交互,并在页面加载时读取已有数据:

let selectedRow = null; 

class Seller {
  constructor(name, domain, type, id) {
    this.name = name;
    this.domain = domain;
    this.type = type;
    this.id = id;
  }
}

// UI Class: Handle UI Tasks
class UI {
  // 添加卖家到列表
  static addSellerToList(seller) {
    const list = document.querySelector('#seller-list');
    const row = document.createElement('tr');
    row.dataset.id = seller.id; // 存储ID到行元素,方便后续编辑/删除

    row.innerHTML = `
      <td>${seller.name}</td>
      <td>${seller.domain}</td>
      <td>${seller.type}</td>
      <td>${seller.id}</td>
      <td><a href="#" class="btn btn-success btn-sm edit">Edit</a></td>
      <td><a href="#" class="btn btn-danger btn-sm delete">X</a></td>
    `;

    list.appendChild(row);
  }

  // 编辑列表中的卖家信息
  static editSellerToList(seller) {
    selectedRow.children[0].textContent = seller.name;
    selectedRow.children[1].textContent = seller.domain;
    selectedRow.children[2].textContent = seller.type;
  }

  // 删除列表中的卖家
  static deleteSeller(el) {
    if(el.classList.contains('delete')) {
      el.parentElement.parentElement.remove();
      UI.showAlert('Seller Deleted', 'danger');
    }
  }

  // 填充表单用于编辑
  static editSeller(el) {
    if(el.classList.contains('edit')) {
      selectedRow = el.parentElement.parentElement;
      document.querySelector('#name').value = selectedRow.children[0].textContent;
      document.querySelector('#domain').value = selectedRow.children[1].textContent;
      document.querySelector('#type').value = selectedRow.children[2].textContent;
    }
  }

  static showAlert(message, className) {
    const div = document.createElement('div');
    div.className = `alert alert-${className}`;
    
    div.appendChild(document.createTextNode(message));
    const container = document.querySelector('.container');
    const main = document.querySelector('.main');
    container.insertBefore(div, main);
    setTimeout(() => document.querySelector('.alert')?.remove(), 2000);
  }

  static clearFields() {
    document.querySelector('#name').value = '';
    document.querySelector('#domain').value = '';
    document.querySelector('#type').value = '';
  }

  // 渲染所有卖家数据
  static renderSellers(sellers) {
    const list = document.querySelector('#seller-list');
    list.innerHTML = ''; // 清空现有列表
    sellers.forEach(seller => UI.addSellerToList(seller));
  }
}

// 页面加载时获取并渲染卖家数据
document.addEventListener('DOMContentLoaded', async () => {
  try {
    const response = await fetch('/get-sellers');
    const sellers = await response.json();
    UI.renderSellers(sellers);
  } catch (err) {
    UI.showAlert('加载数据失败', 'danger');
    console.error(err);
  }
});

// 提交表单:添加/编辑卖家
document.querySelector('#seller-form').addEventListener('submit', async (e) => {
  e.preventDefault();

  const name = document.querySelector('#name').value;
  const domain = document.querySelector('#domain').value;
  const type = document.querySelector('#type').value;
  let id = GenerateUniqueID();

  // 编辑模式下使用现有ID
  if (selectedRow) {
    id = selectedRow.dataset.id;
  }

  if(name === '' || domain === '' || type === '') {
    UI.showAlert('Please fill in all fields', 'danger');
    return;
  }

  const seller = new Seller(name, domain, type, id);
  let success = false;

  try {
    if (selectedRow == null) {
      // 添加新卖家
      const response = await fetch('/add-seller', {
        method: 'POST',
        headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
        body: `name=${encodeURIComponent(name)}&domain=${encodeURIComponent(domain)}&type=${encodeURIComponent(type)}&id=${encodeURIComponent(id)}`
      });
      const result = await response.json();
      success = result.success;
      if (success) {
        UI.addSellerToList(seller);
        UI.showAlert('Seller Added', 'success');
      } else {
        UI.showAlert(result.message, 'danger');
      }
    } else {
      // 编辑现有卖家
      const response = await fetch('/edit-seller', {
        method: 'POST',
        headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
        body: `name=${encodeURIComponent(name)}&domain=${encodeURIComponent(domain)}&type=${encodeURIComponent(type)}&id=${encodeURIComponent(id)}`
      });
      const result = await response.json();
      success = result.success;
      if (success) {
        UI.editSellerToList(seller);
        UI.showAlert('Seller Info Edited', 'info');
      } else {
        UI.showAlert(result.message, 'danger');
      }
    }
  } catch (err) {
    UI.showAlert('网络请求失败', 'danger');
    console.error(err);
  }

  if (success) {
    selectedRow = null;
    UI.clearFields();
  }
});

// 删除卖家
document.querySelector('#seller-list').addEventListener('click', async (e) => {
  if(e.target.classList.contains('delete')) {
    const row = e.target.parentElement.parentElement;
    const id = row.dataset.id;
    
    try {
      const response = await fetch('/delete-seller', {
        method: 'POST',
        headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
        body: `id=${encodeURIComponent(id)}`
      });
      const result = await response.json();
      if (result.success) {
        UI.deleteSeller(e.target);
        UI.showAlert('Seller Removed', 'success');
      } else {
        UI.showAlert(result.message, 'danger');
      }
    } catch (err) {
      UI.showAlert('网络请求失败', 'danger');
      console.error(err);
    }
  } else if(e.target.classList.contains('edit')) {
    UI.editSeller(e.target);
  }
});

function GenerateUniqueID() {
  return ('0000'+(Math.random() * (100000 - 101) + 101)|0).toString().slice(-5);
}

关键说明

  • 使用fs.readFileSync和fs.writeFileSync处理文件读写(如果需要异步操作可以改用fs.promises.readFile/writeFile)
  • 接口中添加了错误处理,避免因文件读写失败导致服务崩溃
  • 前端使用fetch发送表单数据,注意对参数进行encodeURIComponent编码,避免特殊字符问题
  • 行元素添加data-id属性,方便编辑/删除时获取卖家ID

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 01:05:19