如何为本地JSON文件创建类Mongoose的Schema?(Node.js+EJS)
问题解答
是否需要为JSON文件创建类似MongoDB的Schema?
需要。JSON文件本身是无结构约束的纯文本格式,直接写入数据很容易出现格式不一致、字段缺失、数据类型错误等问题。创建类似Mongoose的Schema可以:
- 保证数据格式统一,和你熟悉的MongoDB数据规范对齐
- 自动处理字段校验(比如必填项、格式验证)
- 统一处理字段转换(比如自动trim字符串)
- 避免无效数据写入JSON文件
如何创建适配JSON文件的Schema?
你可以自己实现一套Schema验证逻辑,也可以用轻量的第三方库简化开发,下面结合你的Mongoose Schema示例,给出自定义实现的方案,完全贴合你的开发习惯:
1. 定义Schema验证规则
创建loginSchema.js文件,抽离字段规则和校验逻辑:
// loginSchema.js export const loginSchema = { email: { type: 'string', required: true, trim: true, validate: (value) => { if (!value.match(/^[^@ ]+@[^@ ]+\.[^@ .]{2,}$/)) { throw new Error('Email is not valid.'); } } }, password: { type: 'string', required: true, trim: true }, userCity: { type: 'string', trim: true }, userRegion: { type: 'string', trim: true }, userCountry: { type: 'string', trim: true }, userPostal: { type: 'string', trim: true }, userTimeZone: { type: 'string' }, userAgent: { type: 'string' } };
2. 实现数据校验与处理工具
创建schemaValidator.js文件,实现类似Mongoose的校验逻辑:
// schemaValidator.js export function validateAndProcessData(schema, data) { const processedData = {}; // 遍历Schema字段,逐一校验处理 for (const [field, rules] of Object.entries(schema)) { const value = data[field]; // 必填项校验 if (rules.required && (value === undefined || value === null)) { throw new Error(`Field "${field}" is required`); } // 非必填字段不存在则跳过 if (value === undefined && !rules.required) { continue; } // 数据类型校验 if (typeof value !== rules.type) { throw new Error(`Field "${field}" must be of type ${rules.type}`); } // 处理字符串trim if (rules.trim && typeof value === 'string') { processedData[field] = value.trim(); } else { processedData[field] = value; } // 自定义校验逻辑执行 if (rules.validate) { rules.validate(processedData[field]); } } // 可选:禁止Schema外的额外字段(按需开启) const extraFields = Object.keys(data).filter(key => !schema.hasOwnProperty(key)); if (extraFields.length > 0) { throw new Error(`Unexpected fields: ${extraFields.join(', ')}`); } return processedData; }
3. 在业务代码中使用
在Node.js后端处理表单提交的逻辑里,先校验数据,再写入JSON文件:
// 示例:表单提交处理路由 import { loginSchema } from './loginSchema.js'; import { validateAndProcessData } from './schemaValidator.js'; import fs from 'fs/promises'; import path from 'path'; // JSON文件存储路径 const DATA_FILE = path.join(process.cwd(), 'data', 'logins.json'); export async function handleLoginSubmit(req, res) { try { // 1. 校验并处理表单数据 const validatedData = validateAndProcessData(loginSchema, req.body); // 2. 读取现有JSON数据 let existingData = []; try { const fileContent = await fs.readFile(DATA_FILE, 'utf8'); existingData = JSON.parse(fileContent); } catch (err) { // 文件不存在则初始化空数组 if (err.code === 'ENOENT') { existingData = []; } else { throw err; } } // 3. 添加新数据并写入文件 existingData.push(validatedData); await fs.writeFile(DATA_FILE, JSON.stringify(existingData, null, 2)); // 4. 返回成功响应 res.redirect('/success'); } catch (err) { // 处理校验或文件操作错误 res.status(400).render('error', { message: err.message }); } }
可选:用第三方库简化实现
如果不想自己写校验逻辑,可以用yup(语法和Mongoose高度相似):
import * as yup from 'yup'; const loginSchema = yup.object().shape({ email: yup.string() .required('Email is required') .matches(/^[^@ ]+@[^@ ]+\.[^@ .]{2,}$/, 'Email is not valid') .trim(), password: yup.string() .required('Password is required') .trim(), userCity: yup.string().trim(), userRegion: yup.string().trim(), userCountry: yup.string().trim(), userPostal: yup.string().trim(), userTimeZone: yup.string(), userAgent: yup.string() }); // 使用方式 const validatedData = await loginSchema.validate(req.body, { abortEarly: false });
内容的提问来源于stack exchange,提问作者Change
相关产品推荐
相关产品推荐

