370

我正在尝试使用 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);
    }
}];

但是现在我交不到朋友!

4

7 回答 7

633

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

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

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

  1. 如果您想让您的人在他们使用您的应用程序发布到 Facebook 的故事中标记他们的朋友,您可以使用/me/taggable_friendsAPI。使用此端点需要 Facebook 审核,并且仅应用于您呈现朋友列表以便让用户在帖子中标记他们的情况。

  2. 如果您的应用是游戏并且您的游戏支持 Facebook Canvas,您可以使用/me/invitable_friends端点来呈现自定义邀请对话框,然后将此 API 返回的令牌传递给标准请求对话框。

在其他情况下,应用程序不再能够检索用户朋友的完整列表(只有那些专门授权您的应用程序使用user_friends权限的朋友)。这已被 Facebook 确认为“设计使然”。

对于希望允许人们邀请朋友使用应用程序的应用程序,您仍然可以使用Web 上的发送对话框iOSAndroid上的新消息对话框。

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

于 2014-05-01T22:21:26.277 回答
17

尽管Simon Cross 的回答被接受并且是正确的,但我想我会用一个需要做的例子(Android)来加强它。我会尽量保持一般性,只关注这个问题。就我个人而言,我最终将东西存储在数据库中,因此加载很顺利,但这需要一个 CursorAdapter 和 ContentProvider ,这有点超出了这里的范围。

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

问题

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

什么没有奏效

来自Simon Cross的选项 #1不足以邀请朋友使用该应用程序。Simon Cross还推荐了 Requests Dialog,但它一次只允许五个请求。在任何给定的 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());
        }
    }
}
于 2014-07-30T02:19:20.790 回答
12

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

您将能够提取仅授权该应用程序的朋友列表。

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

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

于 2015-03-21T11:54:54.240 回答
3

Swift 4.2 和Xcode 10.1 中:

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

登录权限

这是两个步骤:

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

2)从 Facebook获取所需的权限。

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

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

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

  2. 在Dashboard的右上角选择状态。

    在此处输入图像描述

  3. 提交隐私政策网址

    在此处输入图像描述

  4. 选择类别

    在此处输入图像描述

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

    在此处输入图像描述

一步完成。

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

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

    示例:user_friends、user_videos、user_posts 等。

    在此处输入图像描述

  2. 二、进入Current Request页面

    在此处输入图像描述

    示例:用户事件

  3. 提交所有详细信息

    在此处输入图像描述

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

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

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

于 2019-02-20T09:19:30.447 回答
2

正如 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 连接器。

于 2014-07-24T21:34:52.183 回答
2

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

或者

尝试图形 API

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

于 2015-05-22T14:53:43.533 回答
-1

在 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;
    };
}
于 2017-04-27T07:20:42.563 回答