Sequelize findOne返回值及Pokemon创建存在性验证故障排查
问题:新增Pokemon时重复数据处理异常
问题现象
我要做一个新增Pokemon的控制器,逻辑是先检查PostgreSQL数据库里有没有同名Pokemon,有就返回错误,没有就创建。现在用Insomnia发POST请求时,Pokemon不存在能正常创建并返回提示;但如果已存在,客户端会收到“创建成功”的提示,数据库没新增数据,而且服务器直接崩溃。我需要实现:存在同名Pokemon时,客户端能看到错误提示,同时服务器不崩溃。怀疑是不是该在路由层做验证,或者不能用throw new Error。
控制器代码
const { Pokemon } = require('../db') const postPokemon = async (name, health, attak, def, speed, height, weight) => { const search = await Pokemon.findOne({ where: { name }}) if(search !== null){ throw new Error(`Ya existe un pokemon con el nombre ${name}`) } else{ const createPokemon = await Pokemon.create({name, health, attak, def, speed, height, weight}) return createPokemon } } module.exports = { postPokemon }
路由代码
pokemonsRouter.post('/', async (req, res) => { try { const { name, health, attak, def, speed, height, weight } = req.body if(!name) throw new Error(`El campo nombre no puede estar vacio`) postPokemon(name, health, attak, def, speed, height, weight) res.status(200).send(`El pokemon ${name} fue creado correctamente`) } catch (error) { res.status(400).send(error.message) } })
错误日志
pokemon-main\api\src\controllers\postPokemons.js:6 throw new Error(`Ya existe un pokemon con el nombre ${name}`) ^ Error: Ya existe un pokemon con el nombre Charmander at postPokemon (C:\Users\crist\OneDrive\Escritorio\hc\PIs\PI-Pokemon-main\PI-Pokemon-main\api\src\controllers\postPokemons.js:6:15) at processTicksAndRejections (node:internal/process/task_queues:96:5) [nodemon] app crashed - waiting for file changes before starting...
Sequelize官方findOne示例(中文翻译)
const project = await Project.findOne({ where: { title: 'My Title' } }); if (project === null) { console.log('未找到!'); } else { console.log(project instanceof Project); // true console.log(project.title); // 'My Title' }
问题原因及修复方案
核心问题
- 路由未等待异步函数执行:
postPokemon是异步函数,但路由调用时没加await,导致控制器逻辑还没跑完,就直接返回了“创建成功”的响应。 - 异步错误未被捕获:因为没加
await,控制器抛出的错误会变成未捕获的Promise拒绝,直接导致服务器崩溃。
修复步骤
1. 路由层添加await,等待控制器执行结果
修改路由代码,确保等待控制器完成后再处理响应:
pokemonsRouter.post('/', async (req, res) => { try { const { name, health, attak, def, speed, height, weight } = req.body if(!name) throw new Error(`El campo nombre no puede estar vacio`) // 加上await,等待控制器执行完成 await postPokemon(name, health, attak, def, speed, height, weight) // 创建成功用201状态码更符合REST规范 res.status(201).send(`El pokemon ${name} fue creado correctamente`) } catch (error) { res.status(400).send(error.message) } })
2. 控制器错误处理可选方案
方案一:保持抛出错误的方式
控制器代码不变,因为路由已经加了await,抛出的错误会被try/catch捕获,返回给客户端错误提示,服务器不会崩溃。
方案二:返回错误字符串替代抛出
如果不想用throw,可以让控制器返回错误字符串,路由根据返回值判断:
// 控制器代码修改 const postPokemon = async (name, health, attak, def, speed, height, weight) => { const search = await Pokemon.findOne({ where: { name }}) if(search !== null){ return `Ya existe un pokemon con el nombre ${name}` } else{ return await Pokemon.create({name, health, attak, def, speed, height, weight}) } } // 路由对应修改 pokemonsRouter.post('/', async (req, res) => { try { const { name, health, attak, def, speed, height, weight } = req.body if(!name) throw new Error(`El campo nombre no puede estar vacio`) const result = await postPokemon(name, health, attak, def, speed, height, weight) if (typeof result === 'string') { res.status(400).send(result) } else { res.status(201).send(`El pokemon ${name} fue creado correctamente`) } } catch (error) { res.status(400).send(error.message) } })
额外优化建议
给Pokemon模型的name字段添加唯一约束,双重保障避免重复数据:
// 在Pokemon模型定义中修改name字段 name: { type: DataTypes.STRING, allowNull: false, unique: true // 添加唯一约束 }
这样即使代码层面验证失效,数据库也会拒绝插入重复数据,此时可以捕获Sequelize的唯一约束错误,返回更友好的提示。
内容的提问来源于stack exchange,提问作者Frank
相关产品推荐
相关产品推荐

