【问题标题】:Facebook Graph API v2.0+ - /me/friends returns empty, or only friends who also use my applicationFacebook Graph API v2.0+ - /me/friends 返回空,或仅返回也使用我的应用程序的朋友
【发布时间】:2014-06-18 12:02:03
【问题描述】:

我正在尝试使用 Graph API v2.0 获取我的朋友姓名和 ID,但数据返回空:

{
  "data": [
  ]
}

当我使用 v1.0 时,以下请求一切正常:

FBRequest* friendsRequest = [FBRequest requestForMyFriends];
[friendsRequest startWithCompletionHandler: ^(FBRequestConnection *connection,
                                              NSDictionary* result,
                                              NSError *error) {
    NSArray* friends = [result objectForKey:@"data"];
    NSLog(@"Found: %i friends", friends.count);
    for (NSDictionary<FBGraphUser>* friend in friends) {
        NSLog(@"I have a friend named %@ with id %@", friend.name, friend.id);
    }
}];

但现在我交不到朋友!

【问题讨论】:

标签: facebook facebook-graph-api facebook-ios-sdk facebook-graph-api-v2.0


【解决方案1】:

在 v2.0 的 Graph API 中,调用 /me/friends 会返回此人的也在使用该应用的朋友。

此外,在 v2.0 中,您必须向每个用户请求 user_friends 权限。 user_friends 不再默认包含在每次登录中。每个用户必须授予user_friends 权限才能出现在对/me/friends 的响应中。有关更多详细信息,请参阅the Facebook upgrade guide,或查看下面的摘要。

如果您想访问不使用应用的朋友列表,有两种选择:

  1. If you want to let your people tag their friends 在他们使用您的应用发布到 Facebook 的故事中,您可以使用 /me/taggable_friends API。 Use of this endpoint requires review by Facebook 并且应该只用于您呈现朋友列表以便让用户在帖子中标记他们的情况。

  2. If your App is a Game AND your Game supports Facebook Canvas,您可以使用/me/invitable_friends端点来渲染a custom invite dialog,然后将此API返回的令牌传递给the standard Requests Dialog.

在其他情况下,应用不再能够检索用户朋友的完整列表(仅限于使用user_friends 权限专门授权您的应用的那些朋友)。 This has been confirmed by Facebook as 'by design'.

对于希望允许人们邀请朋友使用应用的应用,您仍然可以使用Send Dialog on Web 或新的Message Dialog on iOSAndroid

更新:Facebook 已在此处发布了有关这些更改的常见问题解答:https://developers.facebook.com/docs/apps/faq,其中解释了开发人员可用于邀请朋友等的所有选项。

【讨论】:

  • 自您上次编辑或我们在同一页面后有任何消息吗?
  • 在提交审核的项目中看不到taggable_friends
  • 我不明白他们为什么要限制这么多。我的意思是旧系统太容易被滥用了,但这个系统几乎没用。查看所有朋友的公开资料肯定不会是隐私问题。除非您认为友谊的联系本身是私人的(但为什么不是在可标记朋友的情况下)。这并不是限制隐私滥用的举措,这是一种副作用,他们希望关闭普通(非 Facebook 附属)开发人员使用 Facebook 数据发展业务的能力。
  • Facebook 最近发布了可用数据的重大更改:请参阅changelogtaggable_friends 现在什么都不返回。另外,需要登录审核才能获得user_friends 权限。
  • 如果 API 不能让您完成列出朋友等基本任务,那么提供 API 的意义何在?
【解决方案2】:

虽然 Simon Cross 的 答案已被接受且正确,但我想我会通过一个示例(Android)来说明需要做什么。我会尽量保持一般性,只关注这个问题。就我个人而言,我最终将东西存储在数据库中,因此加载很顺利,但这需要一个 CursorAdapter 和 ContentProvider ,这有点超出了这里的范围。

我自己来到这里,然后想,现在怎么办?!

问题

就像 user3594351 一样,我注意到朋友数据是空白的。我通过使用 FriendPickerFragment 发现了这一点。三个月前有效的方法不再有效。甚至 Facebook 的例子也被打破了。所以我的问题是'如何手动创建 FriendPickerFragment?

什么不起作用

Simon Cross 中的

选项 #1 不足以邀请朋友加入该应用。 Simon Cross 还推荐了请求对话框,但它一次只允许五个请求。在任何给定的 Facebook 登录会话期间,请求对话框也会显示相同的朋友。没用。

什么有效(摘要)

选项 #2 需要一些努力。您必须确保满足 Facebook 的新规则:1.) 您是游戏 2.) 您有 Canvas 应用程序(Web Presence) 3.) 您的应用程序已在 Facebook 注册。这一切都在 Facebook 开发者网站的 Settings 下完成。

为了在我的应用中手动模拟好友选择器,我执行了以下操作:

  1. 创建一个显示两个片段的选项卡活动。每个片段显示一个列表。一个片段用于可用朋友 (/me/friends),另一个片段用于可邀请朋友 (/me/invitable_friends)。使用相同的片段代码来呈现两个选项卡。
  2. 创建一个从 Facebook 获取好友数据的 AsyncTask。加载该数据后,将其扔给适配器,该适配器会将值呈现到屏幕上。

详情

异步任务

private class DownloadFacebookFriendsTask extends AsyncTask<FacebookFriend.Type, Boolean, Boolean> {

    private final String TAG = DownloadFacebookFriendsTask.class.getSimpleName();
    GraphObject graphObject;
    ArrayList<FacebookFriend> myList = new ArrayList<FacebookFriend>();

    @Override
    protected Boolean doInBackground(FacebookFriend.Type... pickType) {
        //
        // Determine Type
        //
        String facebookRequest;
        if (pickType[0] == FacebookFriend.Type.AVAILABLE) {
            facebookRequest = "/me/friends";
        } else {
            facebookRequest = "/me/invitable_friends";
        }

        //
        // Launch Facebook request and WAIT.
        //
        new Request(
            Session.getActiveSession(),
            facebookRequest,
            null,
            HttpMethod.GET,
            new Request.Callback() {
                public void onCompleted(Response response) {
                    FacebookRequestError error = response.getError();
                    if (error != null && response != null) {
                        Log.e(TAG, error.toString());
                    } else {
                        graphObject = response.getGraphObject();
                    }
                }
            }
        ).executeAndWait();

        //
        // Process Facebook response
        //
        //
        if (graphObject == null) {
            return false;
        }

        int numberOfRecords = 0;
        JSONArray dataArray = (JSONArray) graphObject.getProperty("data");
        if (dataArray.length() > 0) {

            // Ensure the user has at least one friend ...
            for (int i = 0; i < dataArray.length(); i++) {

                JSONObject jsonObject = dataArray.optJSONObject(i);
                FacebookFriend facebookFriend = new FacebookFriend(jsonObject, pickType[0]);

                if (facebookFriend.isValid()) {
                    numberOfRecords++;

                    myList.add(facebookFriend);
                }
            }
        }

        // Make sure there are records to process
        if (numberOfRecords > 0){
            return true;
        } else {
            return false;
        }
    }

    @Override
    protected void onProgressUpdate(Boolean... booleans) {
        // No need to update this, wait until the whole thread finishes.
    }

    @Override
    protected void onPostExecute(Boolean result) {
        if (result) {
            /*
            User the array "myList" to create the adapter which will control showing items in the list.
             */

        } else {
            Log.i(TAG, "Facebook Thread unable to Get/Parse friend data. Type = " + pickType);
        }
    }
}

我创建的 FacebookFriend 类

public class FacebookFriend {

    String facebookId;
    String name;
    String pictureUrl;
    boolean invitable;
    boolean available;
    boolean isValid;
    public enum Type {AVAILABLE, INVITABLE};

    public FacebookFriend(JSONObject jsonObject, Type type) {
        //
        //Parse the Facebook Data from the JSON object.
        //
        try {
            if (type == Type.INVITABLE) {
                //parse /me/invitable_friend
                this.facebookId =  jsonObject.getString("id");
                this.name = jsonObject.getString("name");

                // Handle the picture data.
                JSONObject pictureJsonObject = jsonObject.getJSONObject("picture").getJSONObject("data");
                boolean isSilhouette = pictureJsonObject.getBoolean("is_silhouette");
                if (!isSilhouette) {
                    this.pictureUrl = pictureJsonObject.getString("url");

                } else {
                    this.pictureUrl = "";
                }

                this.invitable = true;
            } else {
                // Parse /me/friends
                this.facebookId =  jsonObject.getString("id");
                this.name = jsonObject.getString("name");
                this.available = true;
                this.pictureUrl = "";
            }

            isValid = true;
        } catch (JSONException e) {
            Log.w("#", "Warnings - unable to process Facebook JSON: " + e.getLocalizedMessage());
        }
    }
}

【讨论】:

  • 非游戏应用怎么样?一个约会网站......一个寻找人的游戏,一个寻找餐馆的应用程序......一个寻找餐馆的游戏等等。你明白了。那么这真的很接近游戏吗,或者这只是迫使你在 Facebook 中放置应用程序并在他们的广告平台上花钱的一种方式?谁来决定什么是游戏?谁决定您的应用实际上是否是游戏?不要误会我的意思,但对我来说,阻止非游戏应用程序是没有意义的,除非你只是想强迫它们使用 facebook 生态系统。
  • @Mário 这就是 Facebook 背后的意图。您可以放置​​任何内容并将其标记为游戏,因为他们不在乎它是否真的是游戏。他们只关心从你的口袋里掏钱。
【解决方案3】:

Facebook 现在已经修改了他们的政策。如果您的应用没有 Canvas 实现并且您的应用不是游戏,那么无论如何您都无法获得整个好友列表。当然也有 taggable_friends,不过那是个标签而已。

您将能够拉出仅授权该应用的朋友列表。

使用 Graph API 1.0 的应用程序将在 2015 年 4 月 30 日之前运行,之后将被弃用。

请参阅以下内容以获取更多详细信息:

【讨论】:

    【解决方案4】:

    Swift 4.2 和Xcode 10.1 中:

    如果您想从 Facebook 获取好友列表,您需要在 Facebook 中提交您的应用以供审核。查看一些登录权限:

    Login Permissions

    这里有两个步骤:

    1) 首先你的应用状态必须是 Live

    2) 从 Facebook 获取所需权限

    1) 实时启用我们的应用状态:

    1. 转到应用页面并选择您的应用

      https://developers.facebook.com/apps/

    2. 仪表板的右上角选择状态。

    3. 提交隐私政策网址

    4. 选择类别

    5. 现在我们的应用处于Live状态。

    一步完成。

    2) 提交我们的应用以供审核:

    1. 首先发送所需的请求。

      示例:user_friends、user_videos、user_posts 等

    2. 其次,进入当前请求页面

      示例:user_events

    3. 提交所有详细信息

    4. 像这样提交所有请求(user_friends、user_events、user_videos、user_posts 等)。

    5. 最后提交您的应用以供审核。

      如果您的评论被 Facebook 接受,您现在就有资格阅读联系人等。

    【讨论】:

    • user_friends 权限非常有限。来自 Facebook 的文档(2019 年 4 月):[仅限此] 授予应用访问也使用所述应用的朋友列表的权限。此权限仅限于有限的合作伙伴,使用需要事先获得 Facebook 的批准。为了让一个人出现在另一个人的朋友列表中,两个人都必须与应用共享他们的朋友列表,并且在登录期间没有禁用此权限。
    • 测试者账户呢? user_friends 是我的应用程序,我使用朋友的帐户作为测试员,他们接受了请求,现在他们的帐户显示为测试员,但在查询朋友时仍然没有显示?
    【解决方案5】:

    正如 Simon 所说,这在新的 Facebook API 中是不可能的。 纯粹从技术上讲您可以通过浏览器自动化来做到这一点。

    • 这违反了 Facebook 政策,因此根据您居住的国家/地区,这可能不合法
    • 您必须使用您的凭据/向用户询问凭据并可能存储它们(存储密码,即使是对称加密也不是一个好主意)
    • 当 Facebook 更改其 API 时,您还必须更新浏览器自动化代码(如果您不能强制更新您的应用程序,您应该将浏览器自动化部分作为 Web 服务)
    • 这是绕过 OAuth 概念
    • 另一方面,我的感觉是我拥有我的数据,包括我的朋友列表,Facebook 不应限制我通过 API 访问这些数据

    使用WatiN的示例实现:

    class FacebookUser
    {
      public string Name { get; set; }
      public long Id { get; set; }
    }
    
    public IList<FacebookUser> GetFacebookFriends(string email, string password, int? maxTimeoutInMilliseconds)
    {
      var users = new List<FacebookUser>();
      Settings.Instance.MakeNewIeInstanceVisible = false;
      using (var browser = new IE("https://www.facebook.com"))
      {
        try
        {
          browser.TextField(Find.ByName("email")).Value = email;
          browser.TextField(Find.ByName("pass")).Value = password;
          browser.Form(Find.ById("login_form")).Submit();
          browser.WaitForComplete();
        }
        catch (ElementNotFoundException)
        {
          // We're already logged in
        }
        browser.GoTo("https://www.facebook.com/friends");
        var watch = new Stopwatch();
        watch.Start();
    
        Link previousLastLink = null;
        while (maxTimeoutInMilliseconds.HasValue && watch.Elapsed.TotalMilliseconds < maxTimeoutInMilliseconds.Value)
        {
          var lastLink = browser.Links.Where(l => l.GetAttributeValue("data-hovercard") != null
                           && l.GetAttributeValue("data-hovercard").Contains("user.php")
                           && l.Text != null
                         ).LastOrDefault();
    
          if (lastLink == null || previousLastLink == lastLink)
          {
            break;
          }
    
          var ieElement = lastLink.NativeElement as IEElement;
          if (ieElement != null)
          {
            var htmlElement = ieElement.AsHtmlElement;
            htmlElement.scrollIntoView();
            browser.WaitForComplete();
          }
    
          previousLastLink = lastLink;
        }
    
        var links = browser.Links.Where(l => l.GetAttributeValue("data-hovercard") != null
          && l.GetAttributeValue("data-hovercard").Contains("user.php")
          && l.Text != null
        ).ToList();
    
        var idRegex = new Regex("id=(?<id>([0-9]+))");
        foreach (var link in links)
        {
          string hovercard = link.GetAttributeValue("data-hovercard");
          var match = idRegex.Match(hovercard);
          long id = 0;
          if (match.Success)
          {
            id = long.Parse(match.Groups["id"].Value);
          }
          users.Add(new FacebookUser
          {
            Name = link.Text,
            Id = id
          });
        }
      }
      return users;
    }
    

    实现此方法的原型(使用 C#/WatiN)请参阅 https://github.com/svejdo1/ShadowApi。它还允许动态更新正在检索您的联系人列表的 Facebook 连接器。

    【讨论】:

    • 您的代码假定应用可以访问用户的用户名和密码。这不是 OAuth 的工作方式,大多数用户不会将他们的 FB 用户名和密码提供给应用程序。这将是一个巨大的安全风险。
    • 这违反了 Facebook 的政策。不应遵循此建议。
    • 另一方面,在黑客攻击中,您甚至无法安全地保护用户的凭据。万一有人决定使用您的代码,请通过将 uri 更改为 https 来确保它使用 SSL。
    • 加一个,让他们把它塞到脸上!
    • 你们听起来好像 Facebook 政策是某种国际法,如果违反它,你就是罪犯。你还认为 Facebook 只为用户考虑最好的,我认为这是错误的,就像@OndrejSvejdar 所说的那样,因为那样他们只会让你选择是否想在其他应用程序中看到。我认为这只是 Facebook 通过免费平台扰乱他人成长的举措。相反,人们应该直接在 Facebook 上开始开发他们的应用程序、服务、游戏、新闻等。如果他们不愿意,请为 Facebook 广告付费。
    【解决方案6】:

    使用您的 JavaScript 代码尝试/me/taggable_friends?limit=5000

    或者

    试试Graph API

    https://graph.facebook.com/v2.3/user_id_here/taggable_friends?access_token=

    【讨论】:

    • 我认为 - 否决票是因为“使用此端点需要 Facebook 审核,并且仅应用于您呈现朋友列表以便让用户标记他们的情况在一个帖子中。”您可以在选定的答案中阅读更多相关信息。
    • { "data": [ ], "summary": { "total_count": 414 } } 我得到了这个 json 数据块 null 没有得到朋友。我能做什么?
    【解决方案7】:

    在 Facebook SDK Graph API v2.0 或更高版本中,您必须在 Facebook 登录时向每个用户请求 user_friends 权限,因为不再包含 user_friends默认情况下,每次登录;我们必须添加它。

    每个用户必须授予 user_friends 权限才能出现在对 /me/friends 的响应中。

    let fbLoginManager : FBSDKLoginManager = FBSDKLoginManager()
    fbLoginManager.loginBehavior = FBSDKLoginBehavior.web
    fbLoginManager.logIn(withReadPermissions: ["email","user_friends","public_profile"], from: self) { (result, error) in
        if (error == nil) {
    
            let fbloginresult : FBSDKLoginManagerLoginResult = result!
            if fbloginresult.grantedPermissions != nil {
                if (fbloginresult.grantedPermissions.contains("email")) {
                    // Do the stuff
                }
                else {
                }
            }
            else {
            }
        }
    }
    

    所以在 Facebook 登录时,它会提示一个包含所有权限的屏幕:

    如果用户按下继续按钮,权限将被设置。当您使用 Graph API 访问好友列表时,将列出您按上述方式登录应用程序的好友

    if ((FBSDKAccessToken.current()) != nil) {
        FBSDKGraphRequest(graphPath: "/me/friends", parameters: ["fields" : "id,name"]).start(completionHandler: { (connection, result, error) -> Void in
            if (error == nil) {
    
                print(result!)
            }
        })
    }
    

    输出将包含在通过 Facebook 登录您的应用程序时授予 user_friends 权限的用户。

    {
        data = (
                 {
                     id = xxxxxxxxxx;
                     name = "xxxxxxxx";
                 }
               );
        paging = {
            cursors = {
                after = xxxxxx;
                before = xxxxxxx;
            };
        };
        summary = {
            "total_count" = 8;
        };
    }
    

    【讨论】:

      猜你喜欢
      • 2016-04-13
      • 2015-07-24
      • 1970-01-01
      • 1970-01-01
      • 2014-08-21
      • 2018-10-02
      • 1970-01-01
      相关资源
      最近更新 更多