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

Electron中MongoDB API向HTML传参时返回值为undefined的问题

Electron登录接口前端返回undefined问题排查

问题描述

开发个人Electron应用,采用MongoDB Atlas作为后端,搭配Node.js及Mongoose构建接口。Node.js端调用登录接口时能在日志中输出正确的用户数据,但在前端app.js中调用该接口后,获取到的response却是undefined。

相关代码

preload.js

const { contextBridge, ipcRenderer } = require('electron');

process.once("loaded", () => {
    contextBridge.exposeInMainWorld('versions', process.version);
    contextBridge.exposeInMainWorld('engineAPI', {
        userLogin: (...args) => ipcRenderer.invoke('engine:userLogin', ...args).then(result => { console.log(result); return result }),
    });
});

electron.js

// DEPENDENCIES
const { app, BrowserWindow, ipcMain } = require('electron');
const path = require('path');

// MONGOOSE ADAPATER CALL
const mongooseAdapter = require('../engine/common/mongoose.adapter');

// CONTROLLER CALLS
const userController = require('../engine/controllers/user.controller');

// Create the native Window function to be called within Electron initiation
function createWindow() {
    const mainWindow = new BrowserWindow({
        width: 1366,
        height: 768,
        // Set the path of an additional "preload" script that can be used to
        // communicate between node-land and browser-land.
        webPreferences: {
            preload: path.join(__dirname, "preload.js"),
        },
    });

    mainWindow.loadFile(path.join(__dirname, "./src/pages/index.html"));
    if (!app.isPackaged) {
        mainWindow.webContents.openDevTools();
    }
}

// This method will be called when Electron has finished its initialization and
// is ready to create the browser windows.
// Some APIs can only be used after this event occurs.
app.whenReady().then(() => {
    try {
        ipcMain.handle('engine:userLogin', userController.userLogin);
    } catch (error) {
        console.log(error);
    }

    mongooseAdapter.connect(1).then(() => {
        createWindow();
        console.log("Successfully connected to the database");
    }).catch(err => {
        console.log('Not connected. Exiting now...', err);
        process.exit();
    });

    app.on('activate', () => {
        // On macOS it's common to re-create a window in the app when the
        // dock icon is clicked and there are no other windows open.
        if (BrowserWindow.getAllWindows().length === 0) {
            createWindow();
        }
    });
});

// Quit when all windows are closed, except on macOS.
// There, it's common for applications and their menu bar to stay active until
// the user quits  explicitly with Cmd + Q.
app.on('window-all-closed', () => {
    if (process.platform !== 'darwin') {
        app.quit();
    }
});

// If your app has no need to navigate or only needs to navigate to known pages,
// it is a good idea to limit navigation outright to that known scope,
// disallowing any other kinds of navigation.
const allowedNavigationDestinations = "https://my-electron-app.com";
app.on("web-contents-created", (event, contents) => {
    contents.on("will-navigate", (event, navigationUrl) => {
        const parsedUrl = new URL(navigationUrl);
        if (!allowedNavigationDestinations.includes(parsedUrl.origin)) {
            event.preventDefault();
        }
    });
});

index.html

<form id="loginForm" onsubmit="handleFormSubmit(event)">
    <div class="form-group">
        <label for="email">Email:</label>
        <input type="email" class="form-control" id="email" name="email" required>
    </div>
    <div class="form-group">
        <label for="password">Password:</label>
        <div class="input-group">
            <input type="password" class="form-control" id="password" name="password" required>
            <div class="input-group-append">
                <button type="button" id="showPassword" class="btn btn-secondary" onclick="togglePasswordVisibility()">Show</button>
            </div>
        </div>
    </div>
    <button type="submit" class="btn btn-primary">Submit</button>
</form>

app.js

// Function to handle form submission
async function handleFormSubmit(event) {
    event.preventDefault();
    console.log("Hi");
    //Fetching input fields for login creds
    var data = {
        email: document.getElementById("email").value,
        password: document.getElementById("password").value
    };

    const response = await window.engineAPI.userLogin(data);
    console.log(response);
}

user.controller.js(问题核心代码)

const userModel = require('../models/user.model');

exports.userLogin = async (event, args) => {
    try {
        userModel.findOne({ email: args.email }).then(userData => {
            console.log(userData);
            return userData
        }).catch(error => console.log(error));
    } catch (error) {
        console.log(error)
    };
};

mongoose.adapter.js

// CONFIG CALL
const config = require('../../private/config');

// DEPENDENCIES
const mongoose = require('mongoose');

module.exports = {
    connect: function (flag) {
        switch (flag) {
            case 1:
                return new Promise(async (resolve, reject) => {
                    try {
                        const connection = await mongoose.connect(config.dbUrl).catch(error => reject(error));
                        resolve(connection);
                    } catch (error) {
                        reject(error)
                    }
                })
            default:
                break;
        }
    }
}

问题根源

你的userLogin控制器函数存在未返回异步操作结果的问题:

  • 函数标记为async,但内部使用.then()处理Promise时,没有将整个Promise链返回出去
  • 即便在.then()中return了userData,外层的async函数也没有接收这个值,最终函数默认返回undefined

Electron的ipcMain.handle会等待处理函数返回的Promise完成后,将结果传递给前端的ipcRenderer.invoke。如果处理函数没有返回有效结果,前端自然会接收到undefined。

修复方案

方案一:使用await语法(推荐,代码更简洁)

修改user.controller.js:

const userModel = require('../models/user.model');

exports.userLogin = async (event, args) => {
    try {
        // 用await直接获取查询结果
        const userData = await userModel.findOne({ email: args.email });
        console.log(userData);
        return userData; // 返回结果给前端
    } catch (error) {
        console.log(error);
        throw error; // 抛出错误,前端可通过try/catch捕获
    }
};

方案二:保留.then()写法,返回Promise链

const userModel = require('../models/user.model');

exports.userLogin = (event, args) => {
    // 直接返回整个Promise链
    return userModel.findOne({ email: args.email })
        .then(userData => {
            console.log(userData);
            return userData;
        })
        .catch(error => {
            console.log(error);
            throw error;
        });
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 01:42:49