如何将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
相关产品推荐
相关产品推荐

