我正在尝试使用 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);
}
}];
但是现在我交不到朋友!
在 Graph API 的 v2.0 中,调用 /me/friends
会返回此人也在使用该应用程序的朋友。
此外,在 v2.0 中,您必须向每个用户请求 user_friends
权限。 user_friends
不再默认包含在每次登录中。每个用户必须授予 user_friends
权限才能出现在对 /me/friends
的响应中。有关更多详细信息,请参阅 the Facebook upgrade guide,或查看下面的摘要。
如果您想访问不使用应用程序的朋友列表,有两种选择:
如果您想让您的人使用您的应用在他们发布到 Facebook 的故事中标记他们的朋友,您可以使用 /me/taggable_friends API。使用此端点需要 Facebook 审核,并且仅应用于您呈现朋友列表以便让用户在帖子中标记他们的情况。如果您的应用是游戏并且您的游戏支持 Facebook Canvas,您可以使用 /me/invitable_friends 端点来呈现自定义邀请对话框,然后将此 API 返回的令牌传递给标准请求对话框。
在其他情况下,应用程序不再能够检索用户朋友的完整列表(只有那些使用 user_friends
权限专门授权您的应用程序的朋友)。 This has been confirmed by Facebook as 'by design'.
对于希望允许人们邀请朋友使用应用程序的应用程序,您仍然可以使用 Send Dialog on Web 或新的 Message Dialog on iOS 和 Android。
更新:Facebook 已在此处发布了有关这些更改的常见问题解答:https://developers.facebook.com/docs/apps/faq,其中解释了开发人员可用于邀请朋友等的所有选项。
尽管 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 开发者网站的“设置”下完成。
为了在我的应用程序中手动模拟好友选择器,我执行了以下操作:
创建一个显示两个片段的选项卡活动。每个片段显示一个列表。一个片段用于可用朋友 (/me/friends),另一个片段用于可邀请朋友 (/me/invitable_friends)。使用相同的片段代码来呈现两个选项卡。创建一个从 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现在已经修改了他们的政策。如果您的应用没有 Canvas 实现并且您的应用不是游戏,则无论如何您都无法获得整个好友列表。当然也有 taggable_friends,但那只是用来标记的。
您将能够提取仅授权该应用程序的朋友列表。
使用 Graph API 1.0 的应用程序将在 2015 年 4 月 30 日之前运行,之后将被弃用。
请参阅以下内容以获取更多详细信息:
用户好友
Facebook 应用程序开发常见问题解答
如果您想从 Facebook 获取好友列表,您需要在 Facebook 中提交您的应用以供审核。查看一些登录权限:
这是两个步骤:
1) 首先你的应用状态必须是 Live
2) 从 Facebook 获取所需的权限。
1) 实时启用我们的应用状态:
转到应用程序页面并选择您的应用程序 https://developers.facebook.com/apps/ 在仪表板的右上角选择状态。提交隐私政策 URL 选择类别 现在我们的应用程序处于 Live 状态。
一步完成。
2) 提交我们的应用以供审核:
首先发送所需的请求。示例:user_friends、user_videos、user_posts 等。其次,转到当前请求页面 示例:user_events 提交所有详细信息 像这样提交所有请求(user_friends、user_events、user_videos、user_posts 等)。最后提交您的应用以供审核。如果您的评论被 Facebook 接受,您现在有资格阅读联系人等。
user_friends
权限非常有限。来自 Facebook 的文档(2019 年 4 月):[仅限此] 授予应用访问也使用所述应用的朋友列表的权限。此权限仅限于有限的合作伙伴,使用需要事先获得 Facebook 的批准。为了让一个人出现在另一个人的朋友列表中,两个人都必须与应用共享他们的朋友列表,并且在登录期间没有禁用此权限。
正如 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 连接器。
使用您的 JavaScript 代码尝试 /me/taggable_friends?limit=5000
或者
试试 Graph API:
https://graph.facebook.com/v2.3/user_id_here/taggable_friends?access_token=
在 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 登录时,它会提示一个包含所有权限的屏幕:
https://i.stack.imgur.com/9adBL.png
如果用户按下继续按钮,将设置权限。当您使用 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;
};
}
taggable_friends
。taggable_friends
现在什么都不返回。此外,在您被授予user_friends
权限之前,需要进行登录审核。