Google Apps Script触发器调用外部API返回401,手动运行正常
问题:Google Apps Script触发器调用Entire Recruit API返回401错误
我正在用Google Apps Script访问Entire Recruit API。手动运行pullTrigger函数时,getTokenFromAPI和后续的getShiftsBear、getMembersBear、getClientsBear接口都返回200;但使用时间触发器、onFormSubmit/OnEdit触发器运行时,只有getTokenFromAPI返回200,其余接口全部返回401。
想请教:
- 为什么触发器执行会出现这个问题?
- 是否需要使用异步函数?
- API文档提到要注册IP,但开发者表示未启用该限制,那是否需要借助Google Cloud才能带认证访问外部API?
触发器调用的函数代码
function pullTrigger(){ SpreadsheetApp.openByUrl(dataURL); let token = getTokenFromAPI(); Logger.log("Starting"); SpreadsheetApp.flush(); Utilities.sleep(3000); getShiftsBear(token); SpreadsheetApp.flush(); Utilities.sleep(3000); getMembersBear(token); SpreadsheetApp.flush(); Utilities.sleep(3000); getClientsBear(token); SpreadsheetApp.flush(); Logger.log("Concluded"); }
接口函数示例(getShiftsBear)
function getShiftsBear(token){ Logger.log('Bearer Auth in header'); //var url3 = 'http://developers.entirerecruit.com/recruit-out/v1.0/GetShiftsByShiftDate?ShiftFromDate=2022-08-01&ShiftToDate=2022-08-07&Show_TimesheetVerified=false&$count=true'; //all shifts let url3 = urlBase; let fromDate = getTodayDate(); let toDate = getTomorrowDate(); //need a way to get dates in let para = 'GetShiftsByShiftDate?ShiftFromDate='+ fromDate +'&ShiftToDate=' + toDate + '&Show_TimesheetVerified=false&$count=true' //combines date with URL - all shifts url3 = url3 + para; console.log("the url is - " + url3); var auth = 'Bearer ' + token; console.log(auth); var response = UrlFetchApp.fetch(url3, { method: 'GET', headers: { 'Authorization': auth }, muteHttpExceptions: true }); Logger.log('Response Code: ' + response.getResponseCode()); if (responseCodeEscape(response.getResponseCode()) == 0){ Logger.log("fail " + response.getResponseCode()); return; } var content = response.getContentText(); //console.log(content); var json = JSON.parse(content); //console.log(json["value"]); json = json["value"]; var keys = [] for(var key in json){ var arr = [key , json[key]["ShiftCtrlNumber"],json[key]["ServiceId"],json[key]["DeliveryId"],json[key]["ServiceName"],json[key]["DeliveryName"],json[key]["ShiftOrderedDate"],json[key]["ShiftDate"],json[key]["ShiftDay"],json[key]["ShiftType"],json[key]["Start"],json[key]["End"],json[key]["QualificationCode"],json[key]["ExpertiseCode"],json[key]["EmployeeId"],json[key]["OfficeName"],json[key]["PostCode"],json[key]["PriorityID"],json[key]["PriorityName"],json[key]["QualificationCode"],json[key]["State"],json[key]["StatusCode"],json[key]["StatusDescription"],json[key]["FirstName"],json[key]["LastName"],json[key]["IsBooked"],json[key]["IsVerified"],json[key]["Break"],json[key]["WkdHrs"],json[key]["StatusCode"],json[key]["StatusDescription"],json[key]["OfficeID"],json[key]["OfficeName"],json[key]["ProfessionalCode"],json[key]["ProfessionalName"],json[key]["AuthorizedPersonName"],json[key]["BookingRatio"],json[key]["OrderNo"],json[key]["BookedBy"],json[key]["LastUpdatedOn"]] keys.push(arr); } var ss = SpreadsheetApp.openByUrl(dataURL).getSheetByName("shifts_dump"); var headers =ss.getRange('1:2').getValues(); ss.clear(); ss.getRange('1:2').setValues(headers); console.log(keys.length); if (keys.length > 0){ ss.getRange(2,2,keys.length,40).setValues(keys); } }
问题分析与解答
1. 触发器执行返回401的核心原因
手动运行和触发器运行的执行上下文完全不同:
- 手动运行时,脚本以你的个人账号权限执行,
UrlFetchApp请求的IP是你本地设备的IP,或是Google为个人会话分配的专属IP。 - 触发器(时间/表单/编辑触发器)运行时,脚本在Google服务器上执行,使用的是Google云IP池中的IP。
虽然开发者声称未启用IP限制,但很多API的token会隐性绑定请求IP(用于防盗用)。你手动获取的token基于本地IP生成,触发器用Google服务器IP请求时,token的IP校验失败,直接返回401。
2. 是否需要异步函数?
不需要。当前的同步调用逻辑没有问题,Utilities.sleep的间隔设置也合理,401错误和异步调用无关,核心是token的有效性问题。
3. 是否需要借助Google Cloud?
不需要额外依赖Google Cloud,但需要调整token的获取逻辑:
- 不要在函数开头只获取一次token,而是在每个接口请求前重新获取token。因为触发器运行的IP和手动运行不同,之前获取的token在新IP环境下无效。
- 检查
getTokenFromAPI的实现,确保它在触发器上下文里能正常获取适配当前IP的有效token。
额外排查点
- 打印触发器运行时获取的token和手动运行的token,对比是否一致。如果一致,说明API确实存在隐性IP校验;如果不一致,检查
getTokenFromAPI是否依赖了只有手动运行时才有的缓存或属性。 - 在
getShiftsBear中打印完整请求头,确认Authorization头正确携带了token,没有拼写错误(比如Bearer后面的空格是否存在)。
内容的提问来源于stack exchange,提问作者Simon Murray
相关产品推荐
相关产品推荐

