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

Node.js+Express从SQL Server取数渲染至EJS模板问题求助

解决Node.js+Express+Tedious获取SQL Server数据并渲染到EJS的问题

嘿,我看你遇到的问题很典型——异步代码没处理好,导致拿不到数据库返回的结果。别担心,我一步步帮你理清楚,修改代码让它正常工作:

问题分析

你的代码里有几个关键问题:

  • 异步流程没处理:executeStatement是异步执行SQL查询的,但你没通过回调、Promise或者async/await来获取它的结果,所以路由函数里根本拿不到column.value
  • 结果没保存:在row事件里你访问了column.value,但没把它存到变量里,用完就丢了
  • 连接关闭时机错误:你在Request的回调里直接关闭了连接,这会导致后续请求无法使用这个连接,而且可能在数据还没完全返回时就断开了

修改后的完整代码

var express = require('express');
var tediousExpress = require('express4-tedious');
var app = express();
var Connection = require('tedious').Connection;
var Request = require('tedious').Request;

app.set('view engine', 'ejs');
app.set('views', './views');

// 数据库配置(记得替换成你的真实凭据)
var config = {
  server: '你的SQL Server地址',
  authentication: {
    type: 'default',
    options: {
      userName: '用户名',
      password: '密码'
    }
  },
  options: {
    database: '数据库名',
    encrypt: true // 如果是Azure SQL需要开启,本地可能不需要,根据情况调整
  }
};

var connection = new Connection(config);

// 监听连接成功事件
connection.on('connect', function(err) {
  if (err) {
    console.error('连接数据库失败:', err);
  } else {
    console.log('成功连接到SQL Server');
  }
});

app.use(function (req, res, next) {
  req.sql = tediousExpress(connection);
  next();
});

// 封装数据库查询为Promise,方便用async/await处理
function executeStatement() {
  return new Promise((resolve, reject) => {
    let resultData = []; // 用来保存查询结果

    const request = new Request("SELECT * FROM table WHERE ArticleId = 24588 FOR JSON PATH", function(err, rowCount) {
      if (err) {
        console.error('查询出错:', err);
        reject(err); // 出错时拒绝Promise
      } else {
        console.log(`总共获取了 ${rowCount} 行数据`);
        // 注意:FOR JSON PATH返回的是JSON字符串,需要解析成对象
        const parsedData = resultData.length ? JSON.parse(resultData[0]) : [];
        resolve(parsedData); // 成功时返回解析后的数组
      }
    });

    // 每一行数据触发的事件
    request.on('row', function(columns) {
      columns.forEach(function(column) {
        if (column.value !== null) {
          resultData.push(column.value); // 把拿到的value存到数组里
        }
      });
    });

    // 执行SQL请求
    connection.execSql(request);
  });
}

app.get('/products', async function (req, res) {
  try {
    // 等待数据库查询完成,拿到结果
    const products = await executeStatement();
    // 渲染EJS模板,把products数据传给前端
    res.render('products', { products: products });
    // 如果想先测试返回JSON,可以用这个:res.json({ products: products });
  } catch (err) {
    console.error('路由处理出错:', err);
    res.status(500).send('服务器内部错误');
  }
});

// 启动服务器,监听端口
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`服务器运行在 http://localhost:${PORT}`);
});

关键修改点说明

  1. 用Promise封装异步查询:
    把executeStatement改成返回Promise的函数,这样就能用async/await来处理异步操作,让代码像同步一样好理解,新手更容易上手。

  2. 保存查询结果:
    声明了resultData数组,在row事件里把每个column.value存进去,最后在Request的回调里解析JSON字符串(因为你用了FOR JSON PATH,返回的是JSON格式的字符串),再返回给路由。

  3. 正确处理异步流程:
    路由函数改成async函数,用await等待executeStatement的结果,拿到后再用res.render把数据传给EJS模板。

  4. 调整连接关闭逻辑:
    去掉了每次请求后关闭连接的代码,因为建立数据库连接开销很大,应该复用这个连接。如果需要关闭连接,可以在应用停止的时候(比如收到SIGINT信号)再关闭。

EJS模板示例(views/products.ejs)

假设你的模板是这样的,就可以直接用传过来的products数组:

<!DOCTYPE html>
<html>
<head>
  <title>产品列表</title>
</head>
<body>
  <h1>产品信息</h1>
  <% if (products.length > 0) { %>
    <ul>
      <% products.forEach(product => { %>
        <li>产品ID: <%= product.ArticleId %>,名称: <%= product.ProductName %></li>
        <!-- 根据你的实际字段调整 -->
      <% }) %>
    </ul>
  <% } else { %>
    <p>没有找到相关产品</p>
  <% } %>
</body>
</html>

额外提示

  • 记得把config里的数据库凭据替换成你自己的,还有SELECT语句里的表名和字段也要对应你的数据库结构。
  • 如果不需要FOR JSON PATH,可以去掉,直接返回行数据,那resultData里就是每一行的对象,不用再解析JSON了。
  • 生产环境建议用连接池(Tedious的ConnectionPool)来管理数据库连接,比单连接更稳定高效。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 05:09:12