Google Calendar API JavaScript事件冲突查询报错排查求助
问题分析与修复方案
1. 403权限错误原因及修复
问题:表单提交时,isFreeDuringEvent函数在用户完成认证前就被调用,此时没有有效访问令牌,导致API请求被拒绝。
修复:调整流程,确保先完成用户认证,再执行冲突检查和事件创建操作。在表单提交处理器中,将冲突检查和事件创建逻辑移到认证回调内部,或先验证令牌有效性再执行后续操作。
2. 类型错误(Cannot read 'result' of undefined)原因及修复
问题:当API请求失败(如403错误)时,response变量未被赋值,后续代码直接访问response.result导致类型错误。
修复:在访问response.result前添加存在性检查,并为events设置默认空数组;同时在isFreeDuringEvent函数中捕获错误后返回明确的冲突状态。
3. 时区处理错误原因及修复
问题:查询事件时未指定时区,API将timeMin和timeMax解析为UTC时间,与用户事件使用的America/Los_Angeles时区不匹配,导致冲突检查结果不准确。
修复:在事件列表请求中添加timeZone参数,明确指定时区为America/Los_Angeles,确保时间范围解析正确。
4. 异步逻辑混合问题修复
问题:createEvent函数同时使用await和回调函数,导致逻辑混乱。
修复:统一使用async/await处理异步操作,移除冗余回调。
完整修正代码
<!DOCTYPE html> <html> <head> <title>Google Calendar Event Scheduling</title> <meta charset="utf-8" /> </head> <body> <p>Google Calendar Event Scheduling</p> <button id="authorize_button" onclick="handleAuthClick()">Authorize</button> <br> <button hidden id="event_button" onclick="handleEventClick()">Create Default Event</button> <button id="signout_button" onclick="handleSignoutClick()">Sign Out</button> <br> <form name="event_form" id="event_form"> <label for="event_summary">Event Summary:</label> <br> <input name='event_summary' id="event_summary" type="text"> <br> <br> <label for="event_location">Event Location:</label> <br> <input name="event_location" id="event_location" type="text"> <br> <br> <label for="event_description">Event Description:</label> <br> <textarea name="event_description" id="event_description" type="text"></textarea> <br> <br> <label for="start_time">Start Time:</label> <br> <input name="start_time" id="start_time" type="datetime-local"> <br> <br> <label for="end_time">End Time:</label> <br> <input name="end_time" id="end_time" type="datetime-local"> <br> <br> <button type="submit">Submit</button> </form> <pre id="content" style="white-space: pre-wrap;"></pre> <script type="text/javascript"> /* exported gapiLoaded */ /* exported gisLoaded */ /* exported handleAuthClick */ /* exported handleSignoutClick */ const CLIENT_ID = 'YOUR_ACTUAL_CLIENT_ID'; // 替换为你的Google Cloud客户端ID const API_KEY = null; const DISCOVERY_DOC = 'https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest'; const SCOPES = 'https://www.googleapis.com/auth/calendar'; let tokenClient; let gapiInited = false; let gisInited = false; document.getElementById('authorize_button').style.visibility = 'hidden'; document.getElementById('signout_button').style.visibility = 'hidden'; document.getElementById('event_button').style.visibility = 'hidden'; function gapiLoaded() { gapi.load('client', initializeGapiClient); } async function initializeGapiClient() { await gapi.client.init({ apiKey: API_KEY, discoveryDocs: [DISCOVERY_DOC], }); gapiInited = true; maybeEnableButtons(); } function gisLoaded() { tokenClient = google.accounts.oauth2.initTokenClient({ client_id: CLIENT_ID, scope: SCOPES, callback: '', }); gisInited = true; maybeEnableButtons(); } function maybeEnableButtons() { if (gapiInited && gisInited) { document.getElementById('authorize_button').style.visibility = 'visible'; document.getElementById('event_button').style.visibility = 'visible'; } } async function isFreeDuringEvent(startTime, endTime) { console.log(startTime); console.log(endTime); let response; try { const request = { 'calendarId': 'primary', 'timeMin': startTime, 'timeMax': endTime, 'singleEvents': true, 'maxResults': 10, 'orderBy': 'startTime', 'timeZone': 'America/Los_Angeles' // 指定时区,确保时间解析正确 }; console.log(request); response = await gapi.client.calendar.events.list(request); console.log(response); } catch (err) { console.error('Error checking for conflicts:', err.message); return true; // 发生错误时默认视为存在冲突 } // 检查response及result是否存在 if (!response || !response.result) { console.error('Invalid response from Calendar API'); return true; } const events = response.result.items || []; // 默认空数组避免undefined错误 let hasEventDuringTimeRange = false; const targetStart = new Date(startTime); const targetEnd = new Date(endTime); for (const event of events) { // 处理全天事件和定时事件的时间字段 const eventStart = event.start.dateTime ? new Date(event.start.dateTime) : new Date(event.start.date); const eventEnd = event.end.dateTime ? new Date(event.end.dateTime) : new Date(event.end.date); // 标准时间重叠检查逻辑:目标开始时间 < 事件结束时间 且 目标结束时间 > 事件开始时间 if (targetStart < eventEnd && targetEnd > eventStart) { hasEventDuringTimeRange = true; console.log(`Conflict found: ${event.summary}`); } } if (!hasEventDuringTimeRange) { console.log('No conflicts found in the specified time range.'); } return hasEventDuringTimeRange; } function handleAuthClick() { tokenClient.callback = async (resp) => { if (resp.error !== undefined) { throw (resp); } document.getElementById('signout_button').style.visibility = 'visible'; document.getElementById('authorize_button').hidden = true; await listUpcomingEvents(); }; if (gapi.client.getToken() === null) { tokenClient.requestAccessToken({ prompt: 'consent' }); } else { tokenClient.requestAccessToken({ prompt: '' }); } } const formElement = document.querySelector('form#event_form') const getFormJSON = (form) => { const data = new FormData(form); return Array.from(data.keys()).reduce((result, key) => { result[key] = data.get(key); return result; }, {}); }; let submittedEventObj; const handler = (event) => { event.preventDefault(); const valid = formElement.reportValidity(); if (valid) { const result = getFormJSON(formElement); result.start_time = result.start_time + ':00'; result.end_time = result.end_time + ':00'; console.log(result); submittedEventObj = { 'summary': result.event_summary, 'location': result.event_location, 'description': result.event_description, 'start': { 'dateTime': result.start_time, 'timeZone': 'America/Los_Angeles', }, 'end': { 'dateTime': result.end_time, 'timeZone': 'America/Los_Angeles', } }; console.log(submittedEventObj); // 统一处理认证后的逻辑 async function handleAfterAuth() { const hasConflict = await isFreeDuringEvent(result.start_time, result.end_time); if (!hasConflict) { await createEvent(submittedEventObj); } else { console.log('Event creation canceled due to conflicts.'); } } // 检查是否已认证,未认证则先请求令牌 if (gapi.client.getToken() !== null) { tokenClient.callback = async (resp) => { if (resp.error !== undefined) { throw (resp); } await handleAfterAuth(); }; tokenClient.requestAccessToken({ prompt: '' }); } else { tokenClient.callback = async (resp) => { if (resp.error !== undefined) { throw (resp); } document.getElementById('signout_button').style.visibility = 'visible'; document.getElementById('authorize_button').hidden = true; await handleAfterAuth(); }; tokenClient.requestAccessToken({ prompt: 'consent' }); } } }; formElement.addEventListener("submit", handler); function handleEventClick() { tokenClient.callback = async (resp) => { const event = { 'summary': 'Google I/O 2015', 'location': '800 Howard St., San Francisco, CA 94103', 'description': 'A chance to hear more about Google\'s developer products.', 'start': { 'dateTime': '2023-06-30T09:00:00', 'timeZone': 'America/Los_Angeles', }, 'end': { 'dateTime': '2023-06-30T17:00:00', 'timeZone': 'America/Los_Angeles', } }; if (resp.error !== undefined) { throw (resp); } await createEvent(event); }; if (gapi.client.getToken() === null) { tokenClient.requestAccessToken({ prompt: 'consent' }); } else { tokenClient.requestAccessToken({ prompt: '' }); } } async function createEvent(submittedEvent) { try { const response = await gapi.client.calendar.events.insert({ calendarId: 'primary', resource: submittedEvent, }); console.log('Event created successfully:', response.result.htmlLink); } catch (err) { console.error('Error creating event:', err.message); } } function handleSignoutClick() { const token = gapi.client.getToken(); if (token !== null) { google.accounts.oauth2.revoke(token.access_token); gapi.client.setToken(''); document.getElementById('content').innerText = ''; document.getElementById('authorize_button').innerText = 'Authorize'; document.getElementById('signout_button').style.visibility = 'hidden'; } } async function listUpcomingEvents() { let response; try { const request = { 'calendarId': 'primary', 'timeMin': (new Date()).toISOString(), 'showDeleted': false, 'singleEvents': true, 'maxResults': 10, 'orderBy': 'startTime', }; response = await gapi.client.calendar.events.list(request); } catch (err) { document.getElementById('content').innerText = err.message; return; } const events = response.result.items; if (!events || events.length == 0) { document.getElementById('content').innerText = 'No events found.'; return; } const output = events.reduce( (str, event) => `${str}${event.summary} (${event.start.dateTime || event.start.date})\n`, 'Events:\n'); document.getElementById('content').innerText = output; } </script> <script async defer src="https://apis.google.com/js/api.js" onload="gapiLoaded()"></script> <script async defer src="https://accounts.google.com/gsi/client" onload="gisLoaded()"></script> </body> </html>
内容的提问来源于stack exchange,提问作者displayName is undefined
相关产品推荐
相关产品推荐

