blacklotus1998/laravel-facebook-sdk

Laravel 的 Facebook SDK 集成

4.0 2020-09-25 15:37 UTC

README

Build Status Latest Stable Version Total Downloads

一个完全单元测试的包,用于轻松地将 Facebook SDK v5 集成到 Laravel 6.0、7.0、8.0。

我应该直接使用 Laravel Socialite 吗?

Laravel 5 内置了对 Socialite 的支持,允许您使用 OAuth 2.0 提供商进行身份验证。Facebook 登录使用 OAuth 2.0,因此 Socialite 支持 Facebook 登录。

如果您只需要对应用程序进行身份验证并获取用户的访问令牌以获取用户的基本数据,那么 Socialite 或 The PHP League 的 Facebook OAuth Client 应该能满足您的需求。

但如果您需要以下任何功能,您可能需要将 Facebook PHP SDK 与此包结合使用

安装

将 Laravel Facebook SDK 包添加到您的 composer.json 文件中。

composer require sammyk/laravel-facebook-sdk

自动发现: 自版本 3.5.0 以来,Laravel Facebook SDK 支持 Laravel 5.5 及更高版本的 自动发现

服务提供者

在您的应用配置中,将 LaravelFacebookSdkServiceProvider 添加到提供者数组中。

'providers' => [
    SammyK\LaravelFacebookSdk\LaravelFacebookSdkServiceProvider::class,
    ];

对于 Lumen,将提供者添加到您的 bootstrap/app.php 文件中。

$app->register(SammyK\LaravelFacebookSdk\LaravelFacebookSdkServiceProvider::class);

外观(可选)

如果您想使用外观,请将其添加到您的应用配置中的别名数组中。

'aliases' => [
    'Facebook' => SammyK\LaravelFacebookSdk\FacebookFacade::class,
    ];

但还有 更好的方法 来使用此包,那就是 不要使用外观

IoC 容器

IoC 容器将自动为您解决 LaravelFacebookSdk 依赖。您可以通过多种方式从 IoC 容器中获取 LaravelFacebookSdk 实例。

// Directly from the IoC
$fb = App::make('SammyK\LaravelFacebookSdk\LaravelFacebookSdk');
// Or in PHP >= 5.5
$fb = app(SammyK\LaravelFacebookSdk\LaravelFacebookSdk::class);

// From a constructor
class FooClass {
    public function __construct(SammyK\LaravelFacebookSdk\LaravelFacebookSdk $fb) {
       // . . .
    }
}

// From a method
class BarClass {
    public function barMethod(SammyK\LaravelFacebookSdk\LaravelFacebookSdk $fb) {
       // . . .
    }
}

// Or even a closure
Route::get('/facebook/login', function(SammyK\LaravelFacebookSdk\LaravelFacebookSdk $fb) {
    // . . .
});

配置文件

注意: 自版本 3.4.0 以来,只要设置了您的 必需配置值,发布配置文件是可选的。

另外注意: 配置文件包含一个默认的 Graph API 版本,该版本会定期更新到最新稳定版本,这可能会在您更新此包的小版本或补丁版本时造成破坏性更改。建议您仍然发布配置文件,并自行在适当的时候更新 Graph API 版本,以防止破坏事物。

在 Facebook 中创建应用后,您需要提供应用 ID 和密钥。在 Laravel 中,您可以使用 artisan 命令发布配置文件。

$ php artisan vendor:publish --provider="SammyK\LaravelFacebookSdk\LaravelFacebookSdkServiceProvider" --tag="config"

配置文件在哪里? Laravel 5 将配置文件发布到 /config/laravel-facebook-sdk.php

Lumen 中,您需要手动将配置文件从 /src/config/laravel-facebook-sdk.php 复制到您基础项目目录中的配置文件夹。由于 Lumen 默认没有 /config 文件夹,因此如果您还没有创建它,则需要创建。

所需的配置值

您需要将配置文件中的 app_idapp_secret 值更新为 您的应用 ID 和密钥

默认情况下,配置文件将查找环境变量以获取您的应用 ID 和密钥。建议您使用环境变量来存储此信息,以保护您的应用密钥免受攻击者侵害。请确保更新您的 /.env 文件,包含您的应用 ID 和密钥。

FACEBOOK_APP_ID=1234567890
FACEBOOK_APP_SECRET=SomeFooAppSecret

重定向示例中的用户登录

以下是一个使用 重定向方法 登录您的应用的完整示例。

此示例还演示了如何 将短期访问令牌交换为长期访问令牌,如果条目不存在,则将用户保存到您的 users 表中。

最后,它将使用 Laravel 内置的用户身份验证登录用户。

Lumen 中的会话:“通过重定向登录”功能依赖于会话来存储 CSRF 令牌。由于 Lumen 5.2+ 中不存在会话,因此您需要使用 不同方法 获取访问令牌。为了测试,您可以直接从 Graph API Explorer 获取访问令牌(请确保从“应用程序”下拉菜单中选择您的应用)。

// Generate a login URL
Route::get('/facebook/login', function(SammyK\LaravelFacebookSdk\LaravelFacebookSdk $fb)
{
    // Send an array of permissions to request
    $login_url = $fb->getLoginUrl(['email']);

    // Obviously you'd do this in blade :)
    echo '<a href="' . $login_url . '">Login with Facebook</a>';
});

// Endpoint that is redirected to after an authentication attempt
Route::get('/facebook/callback', function(SammyK\LaravelFacebookSdk\LaravelFacebookSdk $fb)
{
    // Obtain an access token.
    try {
        $token = $fb->getAccessTokenFromRedirect();
    } catch (Facebook\Exceptions\FacebookSDKException $e) {
        dd($e->getMessage());
    }

    // Access token will be null if the user denied the request
    // or if someone just hit this URL outside of the OAuth flow.
    if (! $token) {
        // Get the redirect helper
        $helper = $fb->getRedirectLoginHelper();

        if (! $helper->getError()) {
            abort(403, 'Unauthorized action.');
        }

        // User denied the request
        dd(
            $helper->getError(),
            $helper->getErrorCode(),
            $helper->getErrorReason(),
            $helper->getErrorDescription()
        );
    }

    if (! $token->isLongLived()) {
        // OAuth 2.0 client handler
        $oauth_client = $fb->getOAuth2Client();

        // Extend the access token.
        try {
            $token = $oauth_client->getLongLivedAccessToken($token);
        } catch (Facebook\Exceptions\FacebookSDKException $e) {
            dd($e->getMessage());
        }
    }

    $fb->setDefaultAccessToken($token);

    // Save for later
    Session::put('fb_user_access_token', (string) $token);

    // Get basic info on the user from Facebook.
    try {
        $response = $fb->get('/me?fields=id,name,email');
    } catch (Facebook\Exceptions\FacebookSDKException $e) {
        dd($e->getMessage());
    }

    // Convert the response to a `Facebook/GraphNodes/GraphUser` collection
    $facebook_user = $response->getGraphUser();

    // Create the user if it does not exist or update the existing entry.
    // This will only work if you've added the SyncableGraphNodeTrait to your User model.
    $user = App\User::createOrUpdateGraphNode($facebook_user);

    // Log the user into Laravel
    Auth::login($user);

    return redirect('/')->with('message', 'Successfully logged in with Facebook');
});

有关用户认证方法的更多详细信息,请参阅 Facebook 登录

向 Facebook 发送请求

对 Facebook 的请求通过 Graph API 进行。此包是官方 Facebook PHP SDK v5 的 Laravel 包装器,因此官方 SDK 中可用的所有方法也在此包中可用。

获取用户信息

以下代码片段将检索表示已登录用户的 用户节点

try {
  $response = $fb->get('/me?fields=id,name,email', 'user-access-token');
} catch(\Facebook\Exceptions\FacebookSDKException $e) {
  dd($e->getMessage());
}

$userNode = $response->getGraphUser();
printf('Hello, %s!', $userNode->getName());

有关 get() 方法的更多信息。

Facebook 登录

当我们说“使用 Facebook 登录”时,我们实际上是指“通过 Facebook 获取用户访问令牌以代表用户调用 Graph API”。这是通过 OAuth 2.0 在 Facebook 上完成的。有几种方法可以使用 Facebook PHP SDK 中的“辅助函数”使用 Facebook 登录用户。

支持的四种登录方法如下

  1. 通过重定向登录(OAuth 2.0)
  2. 通过 JavaScript 登录(使用 JS SDK Cookie)
  3. 通过 App Canvas 登录(使用签名请求)
  4. 通过 Page Tab 登录(使用签名请求)

通过重定向登录

将用户登录到您的应用的一种最常见方式是使用重定向URL。

思路是生成一个唯一的URL,让用户点击。一旦用户点击链接,他们将被重定向到Facebook,要求他们授权您的应用请求的任何权限。一旦用户做出回应,Facebook将把用户重定向回您指定的回调URL,并带有一个成功响应或错误响应。

可以通过SDK的getRedirectLoginHelper()方法获取重定向助手。

生成登录URL

您可以得到一个登录URL,就像您使用Facebook PHP SDK v5一样。

Route::get('/facebook/login', function(SammyK\LaravelFacebookSdk\LaravelFacebookSdk $fb) {
    $login_link = $fb
            ->getRedirectLoginHelper()
            ->getLoginUrl('https://exmaple.com/facebook/callback', ['email', 'user_events']);
    
    echo '<a href="' . $login_link . '">Log in with Facebook</a>';
});

但是,如果您在配置文件中设置了default_redirect_uri回调URL,您可以使用getLoginUrl()包装方法,该方法将默认回调URL(default_redirect_uri)和权限范围(default_scope)设置为配置文件中设置的值。

$login_link = $fb->getLoginUrl();

或者,您可以将权限和自定义回调URL传递给包装器以覆盖默认配置。

注意:由于权限列表有时会发生变化,但回调URL通常保持不变,因此权限数组是getLoginUrl()包装方法中的第一个参数,这与SDK的getRedirectLoginHelper()->getLoginUrl($url, $permissions)方法的顺序相反。

$login_link = $fb->getLoginUrl(['email', 'user_status'], 'https://exmaple.com/facebook/callback');
// Or, if you want to default to the callback URL set in the config
$login_link = $fb->getLoginUrl(['email', 'user_status']);

从回调URL获取访问令牌

用户点击上面的登录链接并确认或拒绝应用权限请求后,他们将被重定向到指定的回调URL。

在回调URL上获取访问令牌的标准“SDK”方法是以下这样

Route::get('/facebook/callback', function(SammyK\LaravelFacebookSdk\LaravelFacebookSdk $fb) {
    try {
        $token = $fb
            ->getRedirectLoginHelper()
            ->getAccessToken();
    } catch (Facebook\Exceptions\FacebookSDKException $e) {
        // Failed to obtain access token
        dd($e->getMessage());
    }
});

在LaravelFacebookSdk中有一个包装方法,用于getRedirectLoginHelper()->getAccessToken(),名为getAccessTokenFromRedirect(),它将回调URL默认设置为laravel-facebook-sdk.default_redirect_uri配置值。

Route::get('/facebook/callback', function(SammyK\LaravelFacebookSdk\LaravelFacebookSdk $fb) {
    try {
        $token = $fb->getAccessTokenFromRedirect();
    } catch (Facebook\Exceptions\FacebookSDKException $e) {
        // Failed to obtain access token
        dd($e->getMessage());
    }
    
    // $token will be null if the user denied the request
    if (! $token) {
        // User denied the request
    }
});

通过JavaScript登录

如果您使用的是JavaScript SDK,您可以从JavaScript SDK设置的cookie中获取访问令牌。

默认情况下,JavaScript SDK不会设置cookie,因此您必须在初始化SDK时明确启用它,使用cookie: true

FB.init({
  appId      : 'your-app-id',
  cookie     : true,
  version    : 'v2.10'
});

使用FB.login()(见FB.login())通过JavaScript SDK登录用户后,您可以从存储在JavaScript SDK设置的cookie中的签名请求中获取用户访问令牌。

Route::get('/facebook/javascript', function(SammyK\LaravelFacebookSdk\LaravelFacebookSdk $fb) {
    try {
        $token = $fb->getJavaScriptHelper()->getAccessToken();
    } catch (Facebook\Exceptions\FacebookSDKException $e) {
        // Failed to obtain access token
        dd($e->getMessage());
    }

    // $token will be null if no cookie was set or no OAuth data
    // was found in the cookie's signed request data
    if (! $token) {
        // User hasn't logged in using the JS SDK yet
    }
});

从App Canvas登录

TokenMismatchException:默认的Laravel安装将在您尝试在Facebook中查看您的应用时抛出TokenMismatchException。请参阅如何解决此问题。

如果您的应用位于Facebook应用画布的上下文中,您可以从在第一页加载时通过POST到您的应用的签名请求中获取访问令牌。

注意:画布助手仅从Facebook接收到的签名请求数据中获取现有的访问令牌。如果访问您的应用的用户尚未授权您的应用或其访问令牌已过期,则getAccessToken()方法将返回null。在这种情况下,您需要使用重定向JavaScript登录用户。

使用SDK的画布助手从签名请求数据中获取访问令牌。

Route::match(['get', 'post'], '/facebook/canvas', function(SammyK\LaravelFacebookSdk\LaravelFacebookSdk $fb) {
    try {
        $token = $fb->getCanvasHelper()->getAccessToken();
    } catch (Facebook\Exceptions\FacebookSDKException $e) {
        // Failed to obtain access token
        dd($e->getMessage());
    }

    // $token will be null if the user hasn't authenticated your app yet
    if (! $token) {
        // . . .
    }
});

从页面标签登录

TokenMismatchException:默认的Laravel安装将在您尝试在Facebook中查看您的页面标签时抛出TokenMismatchException。请参阅如何解决此问题。

如果您的应用位于Facebook页面标签页的上下文中,这等同于应用画布,并且“从应用画布登录”方法也可以用来获取访问令牌。但是页面标签页在签名请求中还有额外的数据。

SDK提供了一个页面标签页辅助工具,用于从页面标签页上下文中的签名请求数据中获取访问令牌。

Route::match(['get', 'post'], '/facebook/page-tab', function(SammyK\LaravelFacebookSdk\LaravelFacebookSdk $fb) {
    try {
        $token = $fb->getPageTabHelper()->getAccessToken();
    } catch (Facebook\Exceptions\FacebookSDKException $e) {
        // Failed to obtain access token
        dd($e->getMessage());
    }

    // $token will be null if the user hasn't authenticated your app yet
    if (! $token) {
        // . . .
    }
});

其他授权请求

Facebook支持两种其他类型的授权URL - 重新请求和重新认证。

重新请求

重新请求(或重新请求?)会再次询问用户之前拒绝的权限。使用重新请求URL而不是直接使用正常登录链接来执行此操作是很重要的,因为

一旦有人拒绝了权限,登录对话框就不会再次询问他们,除非您明确告诉对话框您正在请求已拒绝的权限。- Facebook 文档

您可以使用getReRequestUrl()方法生成重新请求URL。

$rerequest_link = $fb->getReRequestUrl(['email'], 'https://exmaple.com/facebook/login');
// Or, if you want to default to the callback URL set in the config
$rerequest_link = $fb->getReRequestUrl(['email']);

重新认证

重新认证强制用户通过再次输入他们的Facebook账户密码来确认他们的身份。这在在您的Web应用中更改或查看敏感数据之前添加另一层安全措施时非常有用。

您可以使用getReAuthenticationUrl()方法生成重新认证URL。

$re_authentication_link = $fb->getReAuthenticationUrl(['email'], 'https://exmaple.com/facebook/login');
// Or, if you want to default to the callback URL set in the config
$re_authentication_link = $fb->getReAuthenticationUrl(['email']);
// Or without permissions
$re_authentication_link = $fb->getReAuthenticationUrl();

保存访问令牌

在大多数情况下,除非您计划在用户不在浏览您的应用时(例如,例如凌晨3点的CRON作业)代表用户向Graph API发出请求,否则您不需要将访问令牌保存到数据库中。

在您获取访问令牌后,您可以将它存储在会话中以便用于后续请求。

Session::put('facebook_access_token', (string) $token);

然后在每个调用Graph API的脚本中,您可以从会话中提取令牌并将其设置为默认值。

$token = Session::get('facebook_access_token');
$fb->setDefaultAccessToken($token);

在数据库中保存从Facebook获取的数据

将Graph API接收到的数据保存到数据库有时可能是一项繁琐的任务。由于Graph API以可预测的格式返回数据,因此SyncableGraphNodeTrait可以使将数据保存到数据库变得容易。

任何实现了SyncableGraphNodeTrait的Eloquent模型都会应用createOrUpdateGraphNode()方法。该方法确实使直接从Facebook返回的数据在本地数据库中创建或更新变得容易。

use SammyK\LaravelFacebookSdk\SyncableGraphNodeTrait;

class Event extends Eloquent {
    use SyncableGraphNodeTrait;
}

例如,如果您有一个名为Event的Eloquent模型,以下是您如何从Graph API获取特定事件并将其作为新条目插入数据库或使用新信息更新现有条目的示例。

$response = $fb->get('/some-event-id?fields=id,name');
$eventNode = $response->getGraphEvent();

// A method called createOrUpdateGraphNode() on the `Event` eloquent model
// will create the event if it does not exist or it will update the existing
// record based on the ID from Facebook.
$event = Event::createOrUpdateGraphNode($eventNode);

createOrUpdateGraphNode()将自动将返回的字段名称映射到数据库中的列名称。例如,如果您的events表上的列名称与事件节点的字段名称不匹配,您可以通过映射字段来映射。

字段映射

由于您的数据库中的列名称可能不匹配Graph节点的字段名称,因此您可以使用$graph_node_field_aliases静态变量映射您的User模型中的字段名称。

数组的是Graph节点上的字段名称。数组的是本地数据库中的列名称。

use SammyK\LaravelFacebookSdk\SyncableGraphNodeTrait;

class User extends Eloquent implements UserInterface
{
    use SyncableGraphNodeTrait;
    
    protected static $graph_node_field_aliases = [
        'id' => 'facebook_user_id',
        'name' => 'full_name',
        'graph_node_field_name' => 'database_column_name',
    ];
}

指定“可填充”字段

默认情况下,createOrUpdateGraphNode()方法将尝试将节点的所有字段插入到数据库中。但是,有时Graph API将返回您没有明确请求且不存在于您的数据库中的字段。在这些情况下,我们可以使用$graph_node_fillable_fields属性列出特定字段。

use SammyK\LaravelFacebookSdk\SyncableGraphNodeTrait;

class Event extends Eloquent
{
    use SyncableGraphNodeTrait;
    
    protected static $graph_node_fillable_fields = ['id', 'name', 'start_time'];
}

使用数据库列的名称。 例如,如果您已将数据库中的 id 字段别名为 facebook_id 列,您将在您的 $graph_node_fillable_fields 数组中指定 facebook_id

嵌套字段映射

由于Graph API将请求的一些字段作为其他节点/对象返回,因此您可以使用Laravel的array_dot() 表示法来引用这些字段。

例如,您可以向 /me/events 端点发出请求并遍历所有事件,并将它们保存到您的 Event 模型中。 事件节点 将返回作为 位置节点位置.location 字段。响应数据可能如下所示

{
  "data": [
    {
      "id": "123", 
      "name": "Foo Event", 
      "place": {
        "location": {
          "city": "Dearborn", 
          "state": "MI", 
          "country": "United States", 
          . . .
        }, 
        "id": "827346"
      }
    },
    . . .
  ]
}

假设您有一个如下所示的事件表

Schema::create('events', function(Blueprint $table)
{
    $table->increments('id');
    $table->bigInteger('facebook_id')->nullable()->unsigned()->index();
    $table->string('name')->nullable();
    $table->string('city')->nullable();
    $table->string('state')->nullable();
    $table->string('country')->nullable();
});

以下是您如何将嵌套字段映射到您的 Event 模型中的数据库表中

use SammyK\LaravelFacebookSdk\SyncableGraphNodeTrait;

class Event extends Eloquent
{
    use SyncableGraphNodeTrait;
    
    protected static $graph_node_field_aliases = [
        'id' => 'facebook_id',
        'place.location.city' => 'city',
        'place.location.state' => 'state',
        'place.location.country' => 'country',
    ];
}

日期格式

Facebook PHP SDK会将大多数日期格式转换为DateTime实例。当您要将日期/时间值插入数据库时(例如,事件节点start_time字段),这可能会引起问题。

默认情况下,SyncableGraphNodeTrait会将所有DateTime实例转换为以下date()格式

Y-m-d H:i:s

这应该是大多数情况下大多数关系型数据库的正确格式。但此格式缺少时区,这对于您的应用程序可能很重要。此外,如果您以不同的格式存储日期/时间值,则希望自定义将DateTime实例转换为的格式。为此,只需向您的模型添加一个$graph_node_date_time_to_string_format属性并将其设置为任何有效的日期格式

use SammyK\LaravelFacebookSdk\SyncableGraphNodeTrait;

class Event extends Eloquent
{
    use SyncableGraphNodeTrait;
    
    protected static $graph_node_date_time_to_string_format = 'c'; # ISO 8601 date
}

在 Laravel 中登录用户

Laravel Facebook SDK使您可以使用Laravel内置的认证驱动程序轻松登录用户。

更新用户表

为了使Facebook认证与Laravel内置的认证一起工作,您需要在您的用户表中存储Facebook用户的ID。

自然地,您需要为用户想要保留的所有其他信息创建一个列。

如果您需要在用户不在浏览您的应用程序(例如,凌晨3点的cron作业)时代表用户发出请求,则可以将访问令牌存储在数据库中。但通常您不需要在数据库中存储访问令牌。

您需要生成一个迁移来修改您的users表并添加任何新列。

注意:请确保将<name-of-users-table>更改为您的用户表的名称。

$ php artisan make:migration add_facebook_columns_to_users_table --table="<name-of-users-table>"

现在更新迁移文件以包括您希望在用户上保存的新字段。至少您需要保存Facebook用户ID。

use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class AddFacebookColumnsToUsersTable extends Migration
{
    public function up()
    {
        Schema::table('users', function(Blueprint $table)
        {
            // If the primary id in your you user table is different than the Facebook id
            // Make sure it's an unsigned() bigInteger()
            $table->bigInteger('facebook_user_id')->unsigned()->index();
            // Normally you won't need to store the access token in the database
            $table->string('access_token')->nullable();
        });
    }

    public function down()
    {
        Schema::table('users', function(Blueprint $table)
        {
            $table->dropColumn(
                'facebook_user_id',
                'access_token'
            );
        });
    }
}

别忘了运行迁移。

$ php artisan migrate

如果您计划使用Facebook用户ID作为主键,请确保您有一个名为id的列,该列是无符号的大整数并且已建立索引。如果您在数据库中的不同字段中存储Facebook ID,请确保该字段存在,并确保在模型中将其映射到您自定义的ID名称。

如果您使用Eloquent ORM并将访问令牌存储在数据库中,请确保在您的User模型中隐藏access_token字段,以防止可能的暴露。

不要忘记将SyncableGraphNodeTrait添加到您的用户模型中,这样您就可以将模型与从Graph API返回的数据同步。

# User.php
use SammyK\LaravelFacebookSdk\SyncableGraphNodeTrait;

class User extends Eloquent implements UserInterface {
    use SyncableGraphNodeTrait;

    protected $hidden = ['access_token'];
}

在Laravel中记录用户登录

用户使用Facebook登录并从Graph API获取用户ID后,您可以通过将已登录用户的User模型传递给Auth::login()方法来在Laravel中记录用户登录。

class FacebookController {
    public function getUserInfo(SammyK\LaravelFacebookSdk\LaravelFacebookSdk $fb) {
       try {
           $response = $fb->get('/me?fields=id,name,email');
       } catch (Facebook\Exceptions\FacebookSDKException $e) {
           dd($e->getMessage());
       }
       
       // Convert the response to a `Facebook/GraphNodes/GraphUser` collection
       $facebook_user = $response->getGraphUser();
       
       // Create the user if it does not exist or update the existing entry.
       // This will only work if you've added the SyncableGraphNodeTrait to your User model.
       $user = App\User::createOrUpdateGraphNode($facebook_user);
       
       // Log the user into Laravel
       Auth::login($user);
    }
}

使用多个应用

如果您想在同一脚本中使用多个Facebook应用,或者您想在运行时调整设置,您可以使用自定义设置创建一个新的LaravelFacebookSdk实例。

Route::get('/example', function(SammyK\LaravelFacebookSdk\LaravelFacebookSdk $fb) {
    // All the possible configuration options are available here
    $fb2 = $fb->newInstance([
      'app_id' => env('FACEBOOK_APP_ID2'),
      'app_secret' => env('FACEBOOK_APP_SECRET2'),
      'default_graph_version' => 'v2.10',
      // . . .
    ]);
});

错误处理

Facebook PHP SDK会抛出Facebook\Exceptions\FacebookSDKException异常。每当Graph返回错误响应时,SDK将抛出一个扩展自Facebook\Exceptions\FacebookSDKExceptionFacebook\Exceptions\FacebookResponseException。如果抛出Facebook\Exceptions\FacebookResponseException,您可以从getPrevious()方法中获取与错误相关的特定异常。

try {
    // Stuffs here
} catch (Facebook\Exceptions\FacebookResponseException $e) {
    $graphError = $e->getPrevious();
    echo 'Graph API Error: ' . $e->getMessage();
    echo ', Graph error code: ' . $graphError->getCode();
    exit;
} catch (Facebook\Exceptions\FacebookSDKException $e) {
    echo 'SDK Error: ' . $e->getMessage();
    exit;
}

LaravelFacebookSdk不会抛出任何自定义异常。

故障排除

在画布应用中获取TokenMismatchException

如果您的应用在应用画布或页面标签的上下文中提供服务,您在尝试在Facebook上查看应用时可能会看到TokenMismatchException错误。这是因为Facebook将通过发送带有signed_request参数的POST请求来渲染您的应用,而Laravel 5为每个非读取请求启用了CSRF保护,因此触发了错误。

尽管可以完全禁用此功能,但这绝对不推荐,因为CSRF保护是您网站上重要的安全功能之一,并且应默认在所有路由上启用。

在Laravel 5.1 & 5.2中禁用CSRF

将异常添加到您的画布端点中的$except数组,在您的app\Http\Middleware\VerifyCsrfToken.php文件中。

<?php

namespace App\Http\Middleware;

use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as BaseVerifier;

class VerifyCsrfToken extends BaseVerifier
{
    /**
     * The URIs that should be excluded from CSRF verification.
     *
     * @var array
     */
    protected $except = [
        'facebook/canvas',
        'facebook/page-tab',
        // ... insert all your canvas endpoints here
    ];
}

在Laravel 5.0中禁用CSRF

在Laravel 5.0中禁用CSRF验证稍微复杂一些,但有一篇解释如何在Laravel 5.0中禁用特定路由的CSRF保护的文章。

在您的app\Http\Middleware\VerifyCsrfToken.php文件中,添加一个excludedRoutes()方法。然后创建一个包含您画布应用或页面标签端点的路由数组。完整的文件如下所示

<?php namespace App\Http\Middleware;

use Closure;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as BaseVerifier;
use Illuminate\Session\TokenMismatchException;

class VerifyCsrfToken extends BaseVerifier
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     *
     * @throws TokenMismatchException
     */
    public function handle($request, Closure $next)
    {
        if ($this->isReading($request) || $this->excludedRoutes($request) || $this->tokensMatch($request)) {
            return $this->addCookieToResponse($request, $next($request));
        }

        throw new TokenMismatchException;
    }

    /**
     * Ignore CSRF on these routes.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return bool
     */
    private function excludedRoutes($request)
    {
        $routes = [
          'my-app/canvas',
          'my-app/page-tab',
          // ... insert all your canvas endpoints here
        ];

        foreach($routes as $route){
            if ($request->is($route)) {
                return true;
            }
        }

        return false;
    }
}

保存用户时获取QueryException

如果您使用MySQL,在通过createOrUpdateGraphNode()将用户保存到数据库时可能会得到一个QueryException

QueryException in Connection.php line 754:
SQLSTATE[HY000]: General error: 1364 Field 'password' doesn't have a default value

这是因为默认情况下启用了严格模式,将sql_mode设置为包括STRICT_TRANS_TABLES。由于我们不需要为使用Facebook登录的用户设置密码,因此该字段将为空。解决此错误的办法是将您的config/database.php文件中MySQL驱动的strict设置为false

测试

测试是用phpunit编写的。您可以使用以下命令从项目目录的根目录运行测试。

$ ./vendor/bin/phpunit