【问题标题】:Use the Facebook API in a Google Apps Script web app?在 Google Apps Script Web 应用程序中使用 Facebook API?
【发布时间】:2014-02-22 16:00:01
【问题描述】:

我正在尝试使用 Google Apps 脚本作为 Web 应用程序创建一个 Facebook 应用程序,作为后端。唯一似乎适用的 Facebook API 是 Javascript SDK,但我什至无法让那个 API 工作。
我目前遇到的问题是 Facebook JS SDK 使用以“__”结尾的 Javascript 标识符。以双下划线结尾的 Google Apps 脚本 restricts 名称。

如果我使用名称中没有双下划线的 Facebook JS 文件的修改副本,我会收到以下错误:
Refused to display [URL] in a frame because it set 'X-Frame-Options' to 'SAMEORIGIN'

知道如何让 GAS 与 FB 配合得很好吗?

【问题讨论】:

  • Facebook 在不使用 SDK 的情况下允许 HTTP 请求。我认为任何 SDK 所做的部分工作是提供一种配置 HTTP 请求的方法。我的印象是,无论使用什么 SDK,最终所有的交互都会转换为 HTTP 请求。您可以使用 Facebook Graph API Explorer 实时查看和运行。 API Explorer 你可以使用jQuery Ajax 我正在尝试同样的事情,但不知道双下划线问题。所以我不会费心尝试加载 SDK。
  • 我在不使用 SDK 的情况下使用 Google Apps Script 和 HTTP 请求从 Facebook 检索数据并发布到 Facebook。所以我知道这是可能的。我还没有想到的是检索访问令牌。如果我使用 API Explorer 生成的访问令牌,我可以配置 HTTP 请求以访问 Facebook。但是要从登录过程中生成访问令牌,我还没有想通。 (我也没有尝试过。一直在尝试其他死路一条。)
  • 您是指应用访问令牌还是用户访问令牌?您可以通过向https://graph.facebook.com/oauth/access_token 发出不同的请求来获取它们。 developers.facebook.com/docs/facebook-login/access-tokens

标签: facebook-graph-api google-apps-script facebook-apps


【解决方案1】:

我已经知道如何使用 Apps Script UrlFetchApp.fetch 和 HTML 服务让用户通过 Facebook 登录到我的 Apps Script 应用程序。我还可以使用 Apps 脚本在 Facebook 上发帖。

  • 您不需要 Facebook Javascript SDK

一般概述

有 8 个不同的 Facebook 平台:

我与 Apps 脚本一起使用的 Facebook 平台是网站平台。该网站不嵌入在 Facebook 中运行。

你不能做什么:(据我所知)

  • 与 Facebook 交互的网站与 Facebook 页面选项卡之间存在差异。 Facebook Page Tab 嵌入在 Facebook 中运行。我的经验是,Apps 脚本 URL 会导致 Facebook Page Tab 出现问题。 Apps Script 的 URL 在末尾附加了很多 Facebook 似乎删除的内容。 (或类似的东西)我在 Facebook 上尝试了许多不同的 Google 产品变体。您可以将 Google 站点用作 Facebook 页面选项卡,但如果您将 Google Apps 脚本嵌入到站点中,即在 Facebook 页面选项卡中,则会导致跨域错误。因此,我可以开始工作的唯一选择是网站选项。

我用于解决页面选项卡问题的一种解决方法是使用 Thunderpenny 静态 HTML 作为 Facebook 页面选项卡,然后链接到我的 Apps 脚本应用程序或 GAE 应用程序。 (取决于我是否需要 HTTP S

您可以将 Google 站点用作页面选项卡,但 Thunderpenny 可以像往常一样使用 HTML、Javascript 和 CSS 设计应用程序。哦!我已经尝试在 Thunderpenny 中使用 Facebook Javascript SDK,但没有成功。此外,Thunderpenny 应用程序没有后端,Apps Script 有一个后端(.gs 代码),您可以在其中隐藏您的 Facebook 应用程序令牌。

基本步骤是:

  • 从您的应用中触发指向 Facebook oauth 的链接。
  • href="https://www.facebook.com/dialog/oauth?client_id=yourIDhereNoQuotes&redirect_uri=https://script.google.com/macros/s
  • 使用 OnLoad 函数从 URL 捕获返回的 Facebook 令牌
  • 您无法使用doGet(e) 将 Facebook 重定向 URL 处理回您的应用程序。这就是为什么。 Apps 脚本需要在 URL 中看到问号才能解析 URL。 Facebook 返回具有不同配置的 URL。
  • 在您的应用加载时运行脚本以处理 Facebook 令牌
  • window.onload=function(){};
  • onload 函数运行 .gs 函数来验证令牌
  • 使用UrlFetchApp.fetchdebug 令牌
  • 在私有缓存中缓存登录状态
  • var cache = CacheService.getPrivateCache();
  • 在需要时检查登录状态。

请注意,Facebook 会独立跟踪您的应用登录状态。但是,当 Facebook 令牌过期时,您的应用程序的登录状态可能仍然处于活动状态。所以你需要一种方法来检查。

使用官方 Facebook 图形

Brand Resources - Facebook

下载 Facebook 图形。阅读注意事项

创建 Facebook 登录按钮

<div id="FbLog">
    <a href="https://www.facebook.com/dialog/oauth?client_id=YourClientID&redirect_uri=https://script.google.com/macros/s/YourAppsScript/exec?&response_type=token&scope=publish_stream"><img src="https://FacebookGraphic.png" height="95%" width="95%"></a>
</div>

为 Facebook 登录按钮设置样式

#FbLog {
    padding: 10px;
    background-color: white;
    margin-left:auto;
    margin-right:auto;
    cursor: pointer;
}

窗口加载代码

这是我用来捕获 Facebook 令牌的 window.onload 客户端代码。这仅用于捕获 Facebook 令牌,而不用于验证令牌。我的应用程序允许 Facebook 登录和常规登录。 Facebook 令牌的验证在另一个代码中完成。

window.onload=function(){
  //console.log("This onload did run");

  //get the URL out of the browsers address bar
  //the URL can have multiple different strings attached depending upon the situation.
  //1)Sign in with Facebook. 2) Someone wanting to buy an item 3) Someone wanting to input an item for sale.
  //Any situation other than the FB log in is initiated in the back end.
  window.daURL = window.location;
  window.urlStrng = daURL.toString();

  //console.log("url: " + urlStrng);
  //If there was a FaceBook login, there will be a hashtag in the url string
  window.hashPos = urlStrng.indexOf("#");
  if (window.hashPos > 0) {
    mainStart('InputBlock', 'InputForm');
    window.urlEnd = urlStrng.split("#", 2);
    window.urlTkn = urlEnd[1].split("&", 2);
    window.fbAcsTkn = urlTkn[0].split("=", 2);
    window.finalTkn = fbAcsTkn[1];

    window.scndExpire = urlStrng.substring(urlStrng.indexOf("_in=")+4, urlStrng.length);
    console.log("scndExpire: " + scndExpire);

    google.script.run.withFailureHandler(onFailure)
    .withSuccessHandler(showFBsuccess)
    .processFB_LogIn(window.finalTkn, scndExpire)
  }
  else {
    //If it's not a Facebook log in, go to next two choices
    //If the URL string has &L in it, then item listing info is being passed because someone clicked 'Buy'
    window.whatToLoad = urlStrng.indexOf("&L");
    console.log("Second option of onload ran");
    if (window.whatToLoad > 0) {
      google.script.run.withFailureHandler(onFailure)
        .withSuccessHandler(injectBuyForm)
        .include('MutualCmit');
     } else {
     google.script.run.withFailureHandler(onFailure)
       .withSuccessHandler(injectSignInForm)
       .include('SignIn');
     };
  };
};

请注意,即使 Facebook 登录是在前端触发的,验证也会在 .gs 代码中进行。有人可以注入虚假的 Facebook 令牌,但它不会通过服务器端代码的检查。

这是处理 Facebook 登录的.gs 代码:

验证 Facebook Token GS 代码

//I put this cache line at the very top of the `.gs` file. The other code
// can be put somewhere lower.

var cache = CacheService.getPrivateCache();

function processFB_LogIn(argFB_Tkn, expTime) {
    cache.put('fbTkn', argFB_Tkn, 4000);
    cache.put('fbExpr', expTime, 4000);

    var meFBtkn = cache.get('fbTkn');

    Logger.log("FaceBook Token: " + meFBtkn);

     //This section is for verifying (debug) the user actually signed in through Facebook
    //The first FB token is passed in from the URL right after the user signs in, and when this apps Script loads.
    //IMPORTANT!!!    IMPORTANT!!!   You MUST escape the | character with code %7C

    var AppAccssTkn = 'YourAppID%7YourAppToken'; //This never changes unless app secret changes
    var optnGetTkn = {"method" : "get", "muteHttpExceptions" : true};
      //This 'Debugs' the token returned in the URL after the user signed in with Facebook.  You "should" verify that the token is real.
      var rsltDebug = UrlFetchApp.fetch("https://graph.facebook.com/debug_token?input_token="  + meFBtkn  + "&access_token=" + AppAccssTkn, optnGetTkn);
      var debugTxt = rsltDebug.getContentText();
      Logger.log("debugTxt: " + debugTxt);

      var jsonObj = JSON.parse(debugTxt);
      Logger.log("jsonObj: " + jsonObj);
      //This is the FB user ID
      var useIdTxt = jsonObj.data.user_id;
      cache.put('pubIDcache', useIdTxt, 4000);

      var tknValid = jsonObj.data.is_valid;

      Logger.log("reslt of the debug: " + useIdTxt);
      Logger.log("tknValid: " + tknValid);

      var getFbUseName = UrlFetchApp.fetch("https://graph.facebook.com/" + useIdTxt + "/?fields=first_name&access_token=" + AppAccssTkn, optnGetTkn);

      var objUseName = JSON.parse(getFbUseName);
      var arryFirstName = objUseName.first_name;
      Logger.log("user name: " + arryFirstName);

      cache.put('fbFrstName', arryFirstName, 4000);

   if (tknValid === false) {
     return 'notValid';
   }
   else if (arryFirstName != null) {
     //This is how it's determined if someone is logged in or not.
     cache.put('imin', '9847594ujglfugfjogj', 4000);
     return arryFirstName;
  };
};

您需要一个不会更改的 one time 应用令牌,除非您更改应用机密。您必须使用一次性运行的代码生成它。

使用此代码获取您的应用访问令牌:

//A Facebook App Token never changes unless you go to the Facebook Developers Console, and you
//change the App Secret.  So, do NOT keep requesting a new App Token.  Just get it once, then
//hard code it into a backend secret function.
// The App Token can be used to modify your App, but you can just do that 'Manually'
function getOneTimeFB_AppToken() {
  Logger.log("getOneTimeFB_AppToken ran");
  //keep this info secret
  //Generate an App Access Token
  var ysshAppID = 'Your App ID';
  var ysshAppSecret = 'Your App Secret';
  var optnAppTkn = {"method" : "get"};
  var getAppTknURL = "https://graph.facebook.com/oauth/access_token?client_id=" + ysshAppID + "&client_secret=" + ysshAppSecret + "&grant_type=client_credentials"
  var getAppTkn = UrlFetchApp.fetch(getAppTknURL, optnAppTkn);
  Logger.log("Object returned from GET: " + getAppTkn)
  var myAppTkn = getAppTkn.getContentText();
  Logger.log("myAppTkn: " + myAppTkn);
};

发布到 Facebook GS 代码

function fncPostItemFB(arg1ToPost, arg2ToPost, arg3ToPost, argEtcToPost) {
  var fbCacheTkn = cache.get('fbTkn');
  Logger.log("fbCacheTkn: " + fbCacheTkn);

  if (fbCacheTkn === null) {
    return false;
  };
  Logger.log("fncPostItemFB ran: " + fbCacheTkn);
  return fncPostSecurly_(arg1ToPost, arg2ToPost, arg3ToPost, argEtcToPost);
};

function fncPostSecurly_(arg1ToPost, arg2ToPost, arg3ToPost, argEtcToPost) {
  Logger.log("fncPostSecurly ran");

  var sttsUpdate = argToPost + "your text to post here" + argToPost;
  var fromLogInTkn = cache.get('fbTkn');

  Logger.log("cache FB token: " + fromLogInTkn);

  //This is added security https://developers.facebook.com/docs/graph-api/securing-requests/
  var appsecret_sig = Utilities.computeHmacSha256Signature(fromLogInTkn, "YourAppSecret");
  var optnPostFB = {"method" : "post"};  //
  var PostToFB_URL = "https://graph.facebook.com/FacebookPageOrGroupID/feed?message=" + sttsUpdate + "&access_token=" 
    + fromLogInTkn; // + "&appsecret_proof=" + appsecret_sig;


    //Make a post to the Page
    var whatHappened = UrlFetchApp.fetch(PostToFB_URL, optnPostFB );
    //The return from facebook is an object.  Has to be converted to a string.
    var strFrmFbObj = whatHappened.getContentText();
    Logger.log("Return value of Post: " + strFrmFbObj);

    //When a post is successfully made to Facebook, a return object is passed back with an id value.

    var rtrnVerify = strFrmFbObj.indexOf('{\"id\":\"');
    Logger.log("rtrnVerify: " + rtrnVerify);

    if (rtrnVerify != -1) {
      return true;
    } else {
      return false;
    };
 };

发布到 Facebook 前端 Javascript 代码

<script>
window.WriteInput = function(whereToPost) {

    window.strngCtgry = document.getElementById('id_Category').value;
    window.strngMaker = document.getElementById('id_Maker').value;
    window.strngAskingPrice = document.getElementById('id_AskingPrice').value;
    window.strngType = document.getElementById('id_ShrtDesc').value;
    window.strngFunction = document.getElementById('id_Function').value;
    window.strngCosmetic = document.getElementById('id_Cosmetic').value;  
    window.strngDescription = document.getElementById('id_Description').value;
    window.strngUserID = document.getElementById('pubID_Holder').textContent;
    window.addrIP = document.getElementById('IP_Holder').textContent;

  if (whereToPost === 'fb') {
    console.log("fncPostToFB ran" + strngDescription);
    if (strngDescription === "" || strngAskingPrice === "") {alert("Missing Input"); return false;};
    google.script.run.withFailureHandler(postFbFail)
    .withSuccessHandler(savedToFB)
    .fncPostItemFB(strngCtgry, strngMaker, strngAskingPrice, strngType, strngDescription, strngFunction, strngCosmetic, addrIP);
  } else {
    google.script.run.withFailureHandler(onFailure)
      .withSuccessHandler(savedLst)
      .fncSaveItem(strngCtgry, strngMaker, strngAskingPrice, strngType, strngDescription, strngFunction, strngCosmetic, addrIP);
      };
    };

window.savedLst = function(rtrnInput) {
   if (rtrnInput === false) {
     alert("Failed to Save Data");
   }
   else if (rtrnInput === "NotLogged") {
     alert("You are not logged in!");
     mainStart('SignInBody', 'SignIn');
   }
   else if (rtrnInput === "noItemForPic") {
     alert("You Need to Save an Item to attach the Picture to");
   }
   else {
  alert("Your Data Was Saved!");
  //Show the listing that was just saved next to the upload Pics button
  document.getElementById('listToPic').innerHTML = document.getElementById('id_ShrtDesc').value +
    ", " + document.getElementById('id_Description').value +
    ", - Made By: " + document.getElementById('id_Maker').value +
    ", Price: $" + document.getElementById('id_AskingPrice').value;

    };
};

window.postFbFail = function() {
  alert("Failed to Post to Facebook!  Try Signing In Again.");
  unsignFB();
};

window.savedToFB = function(pstFbStat) {
  if (pstFbStat === false) {
    alert("You are Not Signed in to Facebook!");
    unsignFB();
    google.script.run.withFailureHandler(onFailure)
      .signOutFB();
  } else {
    alert("Your Item was Posted to Facebook!");
  };
};

</script>

【讨论】:

    【解决方案2】:

    您可以通过 Google App 脚本的 oAuth2 库连接到 Facebook API 这是 oAuth2 库代码 1B7FSrk5Zi6L1rSxxTDgDEUsPzlukDsi4KGuTMorsTQHhGBzBkMun4iDF

    和facebook.gs文件

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-20
      • 2012-07-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多