Facebook SDK 3.0 執行時造成 DateTime Error 解決方法

這陣子有機會使用 Facebook SDK 3.0 來開發程式,在一開始先下載 Sample 熟悉一下程式的執行方式,無奈有時一執行就會出現日期解析失敗的訊息,或是第 1 次開啟頁面正常,按 F5 重整後就會出現錯誤訊息,後來下載原始碼來修改後解決。

這陣子有機會使用 Facebook SDK 3.0 來開發程式,一開始先下載 Sample 熟悉一下程式的執行方式,無奈有時一執行就會出現日期解析失敗的訊息,或是第 1 次開啟頁面正常,按 F5 重整後就會出現錯誤訊息,後來下載原始碼來修改後解決。

這程式是要放在 facebook 畫布(Canvas)以 iframe 方式呈現,例如以 http://apps.facebook.com/myapps 方式進入。


英文錯誤訊息:

The string was not recognized as a valid DateTime. There is a unknown word starting at index 11.


中文錯誤訊息:

字串無法辨認為有效的 DateTime。索引 11 的起點有一個未知的文字。

 

顯示出第 5 行執行錯誤:

 

因錯誤的地方是使用 Facebook Developer Toolkit 3.0 中提供的方法,因此需要下載原始碼來進行修改,先到這裡下載 SDK_Source.zip,解壓後:

1. 開啟專案:Facebook

2. 開啟檔案:Session\IFrameCanvasSession.cs

3. 要修改的函式:internal override CachedSessionInfo LoadCachedSession()

4. 找到下面這行就是程式錯誤的地方:


return new CachedSessionInfo(sessionKeyCookie.Value, long.Parse(userIdCookie.Value), DateTime.Parse(expiryTimeCookie.Value));

在將 expiryTimeCookie 值轉為 DateTime 時會因為格式錯誤而發生例外。

 

而 expiryTimeCookie 的值是在 CacheSession 函式中的這一行設定:


HttpContext.Current.Response.Cookies.Set(new HttpCookie(EXPIRY_TIME_COOKIE, sessionInfo.ExpiryTime.ToString()));

將 sessionInfo.ExpiryTime.ToString() 的值印出來後會有亂碼的文字「2009/12/22 銝? 08:00:00」,試著改為 sessionInfo.ExpiryTime.ToString("yyyy/MM/dd HH:mm:ss") 寫法仍然無效,後來試著用 DateTime.ParseExact() 可以正確的解析出來,因此修改後程式碼如下:


HttpContext.Current.Response.Cookies.Set(new HttpCookie(EXPIRY_TIME_COOKIE,
    DateTime.ParseExact(sessionInfo.ExpiryTime.ToString("yyyy/MM/dd HH:mm:ss"),
    "yyyy/MM/dd HH:mm:ss",
    System.Globalization.CultureInfo.InvariantCulture).ToString("yyyy/MM/dd HH:mm:ss")));

重新編譯後再試就沒問題了。

但有朋友反應,即使用 DateTime.ParseExact() 還是會遇到同樣的問題,所以我再加上日期是否正確的檢查,若不是正確日期格式就以系統日期加上 8 個小時(注意,這方法不是好的方法,但我還沒看出來這時間是如何計算出來的,這日期我還看過取出來是「1969/12/31 16:00:00」),這樣就可以順利的往下執行了 ^^

 

後來朋友提出另一種解法,但這種解法只適用在第 1 次瀏覽不會出現錯誤,按 F5 重整後才會出現錯誤的情況。

做法:在第 1 次進入頁面(default.asx)時直接取出 facebook 的 uid 值,若發生例外則轉址到另 1 個頁面(login.aspx),在 login.aspx 中讓 User 同意應用程式的執行,同意完應用程式執行後再轉回 default.aspx 即可。

default.aspx.cs:


using System;
using Facebook.Web;

public partial class _Default : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        CanvasIFrameBasePage trs = new CanvasIFrameBasePage();
        try
        {
            long uid = trs.Api.Session.UserId;
        }
        catch
        {
            Response.Redirect("login.aspx");
        }
        finally
        {
            trs.Dispose();
        }
    }
}

login.aspx (全部就只放這3行即可):


<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Login.aspx.cs" Inherits="Login" %>
<%@ Register TagPrefix="Facebook" Namespace="Facebook.Web" Assembly="Facebook.Web" %>
<Facebook:CanvasIFrameLoginControl runat="server" ID="login" RequireLogin="true" />

login.aspx.cs:


using System;
public partial class Login : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        Response.Redirect("default.aspx");
    }
}

 

參考資料: