首页 > web前端 > js教程 > 正文

Node.js与PostgreSQL集成:解决路由处理函数参数传递错误

花韻仙語
发布: 2025-10-22 11:17:27
原创
298人浏览过

node.js与postgresql集成:解决路由处理函数参数传递错误

本文旨在解决Node.js Express应用中集成PostgreSQL时常见的参数传递错误。当数据库操作函数期望接收`req`和`res`对象,但在Express路由中以不正确的方式调用时,会导致`TypeError: Cannot read properties of undefined (reading 'json')`。我们将详细解析问题根源,并提供正确的函数引用传递方法,以及更优化的代码结构,确保前后端数据交互顺畅。

理解Node.js与PostgreSQL的集成架构

在构建基于Node.js和Express的后端应用时,通常会将路由处理逻辑与数据库操作逻辑进行分离。例如,index.js文件负责定义API路由、中间件和服务器启动,而expense_model.js则专注于与PostgreSQL数据库进行交互,执行CRUD(创建、读取、更新、删除)操作。

index.js (Express应用入口):

const express = require('express');
const app = express();
const port = 8000;
const expense_model = require('./expense_model'); // 引入数据库模型

app.use(express.json()); // 解析JSON请求体
// CORS配置
app.use(function (req, res, next) {
    res.setHeader('Access-Control-Allow-Origin', 'http://localhost:8000');
    res.setHeader('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE,OPTIONS');
    res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Access-Control-Allow-Headers');
    next();
});

// 错误的GET路由示例
app.get('/', (req, res) => {
    expense_model.getExpenses() // ⚠️ 问题所在:这里直接调用了函数
        .then(expenses => {
            res.status(200).json(expenses);
        })
        .catch(error => {
            console.error('Failed to retrieve expenses:', error);
            res.status(500).json({ error: 'Failed to retrieve expenses' });
        });
});

// 其他路由...
app.post('/expenses', (req, res) => {
    expense_model.createExpense(req.body) // 这里也存在类似问题,但因为createExpense直接接收req.body,暂时未暴露
    .then(response => {
      res.status(200).send(response);
    })
    .catch(error => {
      res.status(500).send(error);
    })
  })
app.delete('/expenses/:id', (req, res) => {
    expense_model.deleteExpense(req.params.id) // 同样,这里也存在类似问题
    .then(response => {
      res.status(200).send(response);
    })
    .catch(error => {
      res.status(500).send(error);
    })
  })

app.listen(port, () => console.log(`Example app listening on port ${port}!`));
登录后复制

expense_model.js (数据库操作模块):

const Pool = require('pg').Pool;
const pool = new Pool({
    user: 'my_user',
    host: 'localhost',
    database: 'my_database',
    password: 'root',
    port: 5432,
});

// 期望接收req和res的函数
async function getExpenses(req, res) {
    try {
        const query = 'SELECT * FROM expenses';
        const result = await pool.query(query);
        const expenses = result.rows;
        res.status(200).json(expenses); // 在这里直接发送响应
        console.log('Response:', res);
    } catch (err) {
        console.error('Failed to retrieve expenses:', err);
        res.status(500).json({ error: 'Failed to retrieve expenses' });
    }
}

async function createExpense(req, res) {
    const { expense, amount, description, tag } = req.body;
    const query = 'INSERT INTO expenses (expense, amount, description, tag) VALUES ($1, $2, $3, $4) RETURNING *';
    const values = [expense, amount, description, tag];
    try {
        const result = await pool.query(query, values);
        res.status(201).json(result.rows[0]);
    } catch (err) {
        console.error('Failed to create an expense:', err);
        res.status(500).json({ error: 'Failed to create an expense' });
    }
}

async function deleteExpense(req, res) {
    const id = parseInt(req.params.id);
    const query = 'DELETE FROM expenses WHERE id = $1';
    const values = [id];
    try {
        const result = await pool.query(query, values);
        if (result.rowCount === 0) {
            res.status(404).json({ error: 'Expense not found' });
        } else {
            res.json({ message: 'Expense deleted successfully' });
        }
    } catch (err) {
        console.error('Failed to delete an expense:', err);
        res.status(500).json({ error: 'Failed to delete an expense' });
    }
}

// 导出所有函数
module.exports = {
    getExpenses,
    createExpense,
    deleteExpense,
    // updateExpense 函数在原问题中未被使用,但通常也在此模块中
};
登录后复制

问题分析:TypeError: Cannot read properties of undefined (reading 'json')

在上述代码中,当访问根路径 / 时,index.js中的路由处理函数尝试调用expense_model.getExpenses()。

错误信息 TypeError: Cannot read properties of undefined (reading 'json') at Object.getExpenses 明确指出,在expense_model.getExpenses函数内部,尝试访问一个undefined对象的json属性。这通常发生在res对象为undefined的情况下,因为res.status(200).json(expenses)这一行需要res对象是有效的Express响应对象。

根本原因: Express的路由处理函数通常接收req (请求对象) 和 res (响应对象) 作为参数。当我们将一个函数作为路由处理程序传递给app.get()、app.post()等方法时,Express会自动将req和res对象作为参数传递给该函数。

在原始的index.js中,app.get('/', (req, res) => { expense_model.getExpenses().then(...) }) 这段代码中,expense_model.getExpenses() 被立即调用了。这意味着它在被传递给then()方法之前就已经执行。然而,expense_model.getExpenses函数定义时期望接收req和res参数:async function getExpenses(req, res)。由于在index.js中调用时没有传入任何参数,expense_model.getExpenses内部的req和res变量就变成了undefined。当它尝试执行res.status(200).json(expenses)时,由于res是undefined,便会抛出TypeError。

解决方案:正确传递函数引用

解决此问题的关键在于理解如何将函数作为回调或引用传递。Express期望路由处理程序是一个函数引用,而不是函数调用的结果。

即构数智人
即构数智人

即构数智人是由即构科技推出的AI虚拟数字人视频创作平台,支持数字人形象定制、短视频创作、数字人直播等。

即构数智人 36
查看详情 即构数智人

修正后的 index.js (GET路由):

// ... (其他代码保持不变)

// 正确的GET路由示例
app.get('/', expense_model.getExpenses); // ⚠️ 直接传递函数引用

// ... (其他路由)
app.post('/expenses', expense_model.createExpense); // 同样,直接传递函数引用
app.delete('/expenses/:id', expense_model.deleteExpense); // 同样,直接传递函数引用

// ... (其他代码保持不变)
登录后复制

通过将expense_model.getExpenses(不带括号)直接作为app.get的第二个参数,我们实际上是向Express提供了一个函数引用。当有请求到达/路径时,Express会自动调用expense_model.getExpenses函数,并将当前的req和res对象作为参数传递给它。这样,expense_model.getExpenses内部就能正确地访问和使用res对象来发送响应。

优化建议:更好的职责分离

虽然上述解决方案可以修复错误,但将res对象直接传递给数据库模型函数(如getExpenses)并不是最佳实践。更好的做法是让数据库模型函数专注于数据查询和返回结果,而让Express路由处理函数负责接收这些结果并构造HTTP响应。这增强了模块的独立性和可测试性。

优化后的 expense_model.js:

const Pool = require('pg').Pool;
const pool = new Pool({
    user: 'my_user',
    host: 'localhost',
    database: 'my_database',
    password: 'root',
    port: 5432,
});

// 优化后的getExpenses:只负责查询并返回数据
async function getExpensesFromDB() {
    try {
        const query = 'SELECT * FROM expenses';
        const result = await pool.query(query);
        return result.rows; // 返回查询到的数据
    } catch (err) {
        console.error('Failed to retrieve expenses from DB:', err);
        throw new Error('Database query failed'); // 抛出错误,由调用者处理
    }
}

// 优化后的createExpense:只负责创建并返回新数据
async function createExpenseInDB({ expense, amount, description, tag }) {
    const query = 'INSERT INTO expenses (expense, amount, description, tag) VALUES ($1, $2, $3, $4) RETURNING *';
    const values = [expense, amount, description, tag];
    try {
        const result = await pool.query(query, values);
        return result.rows[0];
    } catch (err) {
        console.error('Failed to create an expense in DB:', err);
        throw new Error('Database insert failed');
    }
}

async function deleteExpenseInDB(id) {
    const query = 'DELETE FROM expenses WHERE id = $1';
    const values = [id];
    try {
        const result = await pool.query(query, values);
        if (result.rowCount === 0) {
            return false; // 表示未找到要删除的记录
        }
        return true; // 表示删除成功
    } catch (err) {
        console.error('Failed to delete an expense from DB:', err);
        throw new Error('Database delete failed');
    }
}

module.exports = {
    getExpensesFromDB,
    createExpenseInDB,
    deleteExpenseInDB,
    // updateExpenseInDB
};
登录后复制

相应优化后的 index.js:

const express = require('express');
const app = express();
const port = 8000;
const expense_model = require('./expense_model');

app.use(express.json());
app.use(function (req, res, next) {
    res.setHeader('Access-Control-Allow-Origin', 'http://localhost:8000');
    res.setHeader('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE,OPTIONS');
    res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Access-Control-Allow-Headers');
    next();
});

// 优化后的GET路由
app.get('/', async (req, res) => {
    try {
        const expenses = await expense_model.getExpensesFromDB(); // 调用数据库模型函数获取数据
        res.status(200).json(expenses); // 在路由处理函数中发送响应
    } catch (error) {
        console.error('Failed to retrieve expenses:', error);
        res.status(500).json({ error: 'Failed to retrieve expenses' });
    }
});

// 优化后的POST路由
app.post('/expenses', async (req, res) => {
    try {
        const newExpense = await expense_model.createExpenseInDB(req.body);
        res.status(201).json(newExpense);
    } catch (error) {
        console.error('Failed to create expense:', error);
        res.status(500).json({ error: 'Failed to create expense' });
    }
});

// 优化后的DELETE路由
app.delete('/expenses/:id', async (req, res) => {
    const id = parseInt(req.params.id);
    try {
        const deleted = await expense_model.deleteExpenseInDB(id);
        if (deleted) {
            res.status(200).json({ message: 'Expense deleted successfully' });
        } else {
            res.status(404).json({ error: 'Expense not found' });
        }
    } catch (error) {
        console.error('Failed to delete expense:', error);
        res.status(500).json({ error: 'Failed to delete expense' });
    }
});


app.listen(port, () => console.log(`Example app listening on port ${port}!`));
登录后复制

注意事项与总结

  1. 函数引用与函数调用: 在Express路由中,当你希望Express自动传递req、res等参数时,必须传递函数本身的引用(如expense_model.getExpenses),而不是函数调用的结果(如expense_model.getExpenses())。
  2. 职责分离: 数据库模型层应专注于数据持久化逻辑,返回纯数据或操作结果;API路由层则负责处理HTTP请求/响应的细节,如解析请求体、设置状态码、发送JSON响应等。这种分离使得代码更易于维护、测试和理解。
  3. 错误处理: 无论是数据库层还是路由层,都应有健壮的错误处理机制。数据库操作可能因连接问题、SQL语法错误等失败,路由层应捕获这些错误并向客户端返回适当的HTTP状态码和错误信息。
  4. 异步操作: Node.js中大量使用异步操作(如数据库查询)。使用async/await可以使异步代码看起来更像同步代码,提高可读性,但务必确保在适当的地方使用try...catch来处理可能发生的错误。

通过遵循这些原则,可以有效地避免常见的集成问题,构建出更健壮、可维护的Node.js与PostgreSQL应用。

以上就是Node.js与PostgreSQL集成:解决路由处理函数参数传递错误的详细内容,更多请关注php中文网其它相关文章!

路由优化大师
路由优化大师

路由优化大师是一款及简单的路由器设置管理软件,其主要功能是一键设置优化路由、屏广告、防蹭网、路由器全面检测及高级设置等,有需要的小伙伴快来保存下载体验吧!

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
热门推荐
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号