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
相关产品推荐
相关产品推荐

