Facebook Graph API v2.0 +-/ me / friends返回空,或者仅返回也使用我的应用程序的朋友


366

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

但是现在我无法交朋友!


解决这个问题的可能方法 stackoverflow.com/a/25584013/2529087
java.quaso 2014年

Answers:


627

在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,其中解释了开发人员可以用来邀请朋友等的所有选项。


自您上次编辑以来或我们在同一页面上有任何新闻吗?
伊利亚·加兹曼

taggable_friends在要提交的评论中看不到。
AlikElzin-kilaka

7
我不明白他们为什么要限制这么多。我的意思是旧系统太过滥用了,但是这个几乎没有用。看到您所有朋友的公开档案肯定不是隐私问题。除非您认为友谊的联系本身就是私人的(但再加上为什么在可标记的朋友中不是如此)。这不是限制隐私滥用的举动,这是一个副作用,他们希望关闭普通(非Facebook关联)开发人员使用facebook数据发展业务的能力。
丹尼尔·夏普

3
Facebook最近发布了可用数据的重大更改:请参阅changelog。在taggable_friends现在没有返回。此外,必须先进行登录审查,然后才能被授予user_friends权限。
Duncan Jones

如果提供的API不允许您执行诸如列出好友之类的基本任务,那么提供API有什么意义呢?
6infinity8 '18年

17

尽管西蒙·克罗斯(Simon Cross)的答案是正确的,但我认为我会举例说明(Android)需要完成的工作。我会尽可能保持一般性,只关注问题。就我个人而言,我忙于将事情存储在数据库中,这样加载就很顺利,但是这需要CursorAdapter和ContentProvider,这在这里有点超出范围。

我自己来到这里,然后想,现在呢?

问题

就像user3594351一样,我注意到朋友数据为空。我通过使用FriendPickerFragment找到了这一点。三个月前起作用的东西不再起作用。甚至Facebook的例子也坏了。所以我的问题是“如何手动创建FriendPickerFragment?

什么没有奏效

Simon Cross的选项#1 不够强大,无法邀请朋友使用该应用程序。西蒙·克罗斯(Simon Cross)还推荐了“请求对话框”,但是一次只能允许五个请求。在任何给定的Facebook登录会话期间,请求对话框还显示了相同的朋友。没用。

工作原理(摘要)

选项#2有点辛苦。您必须确保满足Facebook的新规则:1.)您是游戏2.)您有Canvas应用程序(网络存在)3.)您的应用程序已在Facebook中注册。所有这些都在Facebook开发者网站的“ 设置”下完成。

为了在我的应用程序中手动模拟朋友选择器,我做了以下工作:

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

细节

AsynchTask

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());
        }
    }
}

20
非游戏应用怎么样?约会网站...找人的游戏,找餐厅的应用程序...找餐厅的游戏,等等。那么,这真的接近游戏吗?或者这仅仅是一种迫使您在Facebook上放置应用程序并在其广告平台上花钱的方式?谁来决定什么是游戏?谁决定您的应用实际上是游戏?别误会我的意思,但对我而言,除非您只想强迫非游戏应用使用Facebook生态系统,否则阻止非游戏应用是没有意义的。
2014年

@Mário这是Facebook的意图。您可以放置​​任何东西并将其标记为游戏,因为他们不在乎它是否真的是游戏。他们所关心的只是从您的口袋里赚钱。
sandalone '17

12

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

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

使用Graph API 1.0的应用将一直运行到2015年4月30日,之后将不推荐使用。

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


3

Swift 4.2和Xcode 10.1中:

如果要从Facebook获取好友列表,则需要将应用提交到Facebook中进行审核。查看一些登录权限:

登录权限

这是两个步骤:

1)首先,您的应用状态必须为实时

2)从Facebook 获得所需的权限

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

  1. 转到应用页面,然后选择您的应用

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

  2. 资讯主页右上方选择状态。

    在此处输入图片说明

  3. 提交隐私政策网址

    在此处输入图片说明

  4. 选择类别

    在此处输入图片说明

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

    在此处输入图片说明

第一步完成。

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

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

    例如:user_friends,user_videos,user_posts等。

    在此处输入图片说明

  2. 其次,转到“当前请求”页面

    在此处输入图片说明

    示例:user_events

  3. 提交所有详细信息

    在此处输入图片说明

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

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

    如果您的评论已被Facebook接受,则您现在可以阅读联系人等信息。


3
user_friends权限非常有限。来自Facebook的文档(2019年4月):[仅此]授予应用程序访问同样使用该应用程序的朋友列表的权限。此权限仅限于有限的合作伙伴,使用需要获得Facebook的事先批准。为了使一个人出现在另一个人的朋友列表中,两个人都必须与该应用共享他们的朋友列表,并且在登录期间未禁用此权限。
dearsina '19

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连接器,以检索您的联系人列表。


2
您的代码假定该应用有权访问用户的用户名和密码。这不是OAuth的工作方式,并且大多数用户不会向应用程序提供其FB用户名和密码。那将是巨大的安全风险。
jbustamovej 2014年

32
这违反了Facebook的政策。不应遵循此建议。
西蒙·克罗斯

3
另一方面,您甚至无法安全地保护黑客中用户的凭据。万一有人决定使用您的代码,请通过将uri更改为https来确保它使用SSL。
Rogala

8
加一把它们塞在脸上!
天网

12
大家听起来都像Facebook的政策是某种国际法,而违反它就是您是犯罪分子。另外,您认为Facebook只考虑了最适合用户的内容,就像@OndrejSvejdar所说的那样,我认为这是错误的,因为那样的话,它们会让您选择是否希望在其他应用程序中显示。我认为这只是Facebook通过免费免费平台破坏他人成长的一种举措。相反,人们应该开始直接在Facebook上开发其应用,服务,游戏,新闻等。如果不愿意,请为Facebook广告付费。
JustGoscha 2015年

2

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

要么

尝试Graph API

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


2
我认为-此处的不赞成票是因为“使用此终结点需要Facebook进行审查,并且仅应在您呈现朋友列表以使用户在帖子中标记他们的情况下使用。” 您可以在所选答案中了解更多有关此内容的信息。
moonvader

{“ data”:[],“ summary”:{“ total_count”:414}}我得到了这个json数据块,为null,没有得到朋友。我能做什么?
Makvin

-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;
    };
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.