Get Facebook Comments For A Page Using Graph API In ASP.NET

For a site I'm working on, the Facebook's Comments plugin is being utilised on all our article pages. There was a requirement to pull in the latest comments in a listing page for each of these article pages as well as number of comments. Facebook's JavaScript library provides the ability to display a comments counter but not the ability to pull out x number of comments. So we'll have to go server-side and use Graph API to get the data we want.

In this post, I will show you how you can get back all comments for a page by it's full URL.

Prerequisites

Before we get into the main C# logic methods, you need to make sure we have a few things in place:

  • ApiWebRequestHelper Class
  • Newtonsoft Json
  • Facebook App Settings
  • Class Objects

ApiWebRequestHelper Class

Whenever I am making a call to Facebook's Graph API endpoints, I will be making references to a "ApiWebRequestHelper" helper class. This is something I developed last month to make it easier for me to deserialize XML or JSON requests to a strongly-typed class object. You can take a look at the full code here.

Newtonsoft Json

The Newtonsoft Json library is a key ingredient to any JSON web requests. I'd be surprised if you've never heard or used it. :-) Nevertheless, you can get it here: http://www.newtonsoft.com/json.

Facebook App Settings

I haven't created a Facebook App for quite some time and things have changed very slightly in terms of the interface and options presented. The key things you need to get out of your created App is:

  • Application ID
  • Application Secret
  • Client Token

I set the security settings with the following modes, which can be found in Settings > Advanced >  Security.

Facebook App Advanced API Settings

Class Objects

The following class objects will be used to deserialize Graph API requests into class objects.

The FacebookPageInfo, FacebookPage and FacebookPageShare objects will get the core information about the queried page, such as the Title and Description, as well as the comments and share counts.

namespace Site.BusinessObjects.Facebook
{
    public class FacebookPageInfo
    {
        [JsonProperty("id")]
        public string Id { get; set; }

        [JsonProperty("og_object")]
        public FacebookPage Page { get; set; }

        [JsonProperty("share")]
        public FacebookPageShare Share { get; set; }
    }

    public class FacebookPage
    {
        [JsonProperty("id")]
        public string Id { get; set; }

        [JsonProperty("description")]
        public string Description { get; set; }

        [JsonProperty("title")]
        public string Title { get; set; }

        [JsonProperty("type")]
        public string Type { get; set; }

        [JsonProperty("updated_time")]
        public DateTime UpdatedTime { get; set; }

        [JsonProperty("url")]
        public string Url { get; set; }
    }
}
namespace Site.BusinessObjects.Facebook
{
    public class FacebookPageShare
    {
        [JsonProperty("comment_count")]
        public int CommentCount { get; set; }

        [JsonProperty("share_count")]
        public int ShareCount { get; set; }
    }
}

All comments for a page will be stored in the following objects:

namespace Site.BusinessObjects.Facebook
{
    public class FacebookPageCommentInfo
    {
        public int TotalComments { get; set; }
        public List<FacebookCommentItem> Comments { get; set; }
    }
}
namespace Site.BusinessObjects.Facebook
{
    public class FacebookCommentItem
    {
        [JsonProperty("id")]
        public string Id { get; set; }

        [JsonProperty("created_time")]
        public DateTime CreatedTime { get; set; }

        [JsonProperty("from")]
        public FacebookCommentFrom From { get; set; }

        [JsonProperty("message")]
        public string Message { get; set; }
    }

    public class FacebookCommentFrom
    {
        [JsonProperty("id")]
        public string Id { get; set; }

        [JsonProperty("name")]
        public string Name { get; set; }
    }
}

Facebook Logic Class

Now that we have the pre-requisites in place, lets get to the code that will perform the required functions:

namespace Site.BusinessLogic
{
    public class FacebookLogic
    {
        private string _accessToken;

        /// <summary>
        /// Uses default Client ID and Secret as set in the web.config.
        /// </summary>
        public FacebookLogic()
        {
            GetAccessToken(Config.Facebook.ClientId, Config.Facebook.ClientSecret);
        }

        /// <summary>
        /// Requires  Client ID and Secret.
        /// </summary>
        /// <param name="clientId"></param>
        /// <param name="clientSecret"></param>
        public FacebookLogic(string clientId, string clientSecret)
        {
            GetAccessToken(clientId, clientSecret);
        }

        /// <summary>
        /// Gets page info that has been shared to Facebook.
        /// </summary>
        /// <param name="pageUrl"></param>
        /// <returns></returns>
        public FacebookPageInfo GetPage(string pageUrl)
        {
            return ApiWebRequestHelper.GetJsonRequest<FacebookPageInfo>($"https://graph.facebook.com/{pageUrl}?access_token={_accessToken}");
        }

        /// <summary>
        /// Gets comments for a page based on its absolute URL.
        /// </summary>
        /// <param name="pageUrl"></param>
        /// <param name="maxComments"></param>
        public FacebookPageCommentInfo GetPageComments(string pageUrl, int maxComments)
        {
            try
            {
                // Get page information in order to retrieve page ID to pass to commenting.
                FacebookPageInfo facebookPage = GetPage(pageUrl);

                if (facebookPage.Page != null)
                {
                    return new FacebookPageCommentInfo
                    {
                        TotalComments = facebookPage.Share.CommentCount,
                        Comments = GetCommentsByPageId(facebookPage.Page.Id, maxComments).Comments
                    };
                }
                else
                {
                    return null;
                }
            }
            catch (Exception ex)
            {
                // NOTE: Log exception here...

                return null;
            }
        }

        /// <summary>
        /// Gets comments by Facebook's Page ID.
        /// </summary>
        /// <param name="fbPageId"></param>
        /// <param name="max"></param>
        /// <returns></returns>
        public FacebookCommentInfo GetCommentsByPageId(string fbPageId, int max = 10)
        {
            return ApiWebRequestHelper.GetJsonRequest<FacebookCommentInfo>($"https://graph.facebook.com/comments?id={fbPageId}&access_token={_accessToken}&limit={max}");
        }

        /// <summary>
        /// Retrieves Access Token from Facebook App.
        /// </summary>
        /// <param name="clientId"></param>
        /// <param name="clientSecret"></param>
        private void GetAccessToken(string clientId, string clientSecret)
        {
            UriBuilder builder = new UriBuilder($"https://graph.facebook.com/oauth/access_token?client_id={Config.Facebook.ClientId}&client_secret={Config.Facebook.ClientSecret}&grant_type=client_credentials");

            try
            {
                using (WebClient client = new WebClient())
                {
                    // Get Access Token from incoming response.
                    string data = client.DownloadString(builder.Uri);

                    NameValueCollection parsedQueryString = HttpUtility.ParseQueryString(data);

                    _accessToken = parsedQueryString["access_token"];
                }
            }
            catch (Exception ex)
            {
                // NOTE: Log exception here...
            }
        }
    }
}

By default, on initiation of the FacebookLogic class, the Application ID and Secret values will be inherited from the web.config, or you can pass in these values directly with the class overload parameters.

Out of all the methods used here, we're interested in only using one: GetPageComments(). What you will notice from this method is that we cannot get the comments from one API call alone. We first have to make an extra API call to get the ID of the page. This ID is passed to the GetCommentsByPageId() method, to return all comments.

Usage

Comments for a page can be returned by adding the following in your code, where you will then be able to access properties to iterate through the comments:

FacebookLogic fbl = new FacebookLogic();

// Pass in the page URL and number of comments to be returned.
var pageComments = fbl.GetPageComments("https://www.surinderbhomra.com/", 2);

Whenever you call this piece of code, I would make sure you cache the results for 5 - 10 minutes, so you do not use up your API request limits.

Detecting Facebook In-App Browser

It seems there is going to be a growing trend where apps on our mobile devices will open webpages whilst you are inside the app itself instead of using the devices' native browser. A prime example of this is Facebook. In recent updates during the tail end of last year, both their iOS and Android offerings open webpages from within the application.

This isn't a bad thing. In fact I quite like having webpages opening within the application, since this creates a nice seamless experience. However, the Facebook in-app browser doesn't seem to render a webpage in the same manner as the devices' own native browser (Safari/Chrome). I started noticing this whilst working on a complex website that was very much custom JavaScript driven.

The only thing I could do is modify specific mark-up or features that affected my website negatively when opened from within Facebook by detecting the user-agent. In my code (using ASP.NET C#), I was required to carry out additional browser checks:

//User is within Facebook browser.
if (Request.UserAgent.IndexOf("FBAN") > -1)
{
    if (Request.UserAgent.Contains("iPhone OS 8_0_2"))
    {
        //You are using iPhone version 8.0.2.
    }
    
    if (Request.UserAgent.Contains("Chrome"))
    {
        //You are in the Facebook App in Android.
    }
}
else
{
    //You are not in Facebook App.
}

You can modify the code above to create a nice self-contained method to return an enumeration as I ended up doing to be used when required.

Google Needn’t Worry About Facebook’s Advertising

Today I came across this really interesting tweet on my Twitter timeline today:

Limited Run, posted on their Facebook profile stating that they would be deleting their account due to the amount Facebook is charging for clicks on their advertising. Here’s the interesting part: About 80% of the clicks Facebook charged Limited Run, JavaScript wasn't on. And if the person clicking the ad doesn't have JavaScript, it's very difficult for an analytics service to verify the click. Only 1-2% of people going to their site have JavaScript disabled, not 80% like the clicks coming from Facebook.

Interesting stuff.

Before Limited Run takes down their Facebook profile, I’ve attached a screenshot of their post below:

Limited Pressing Facebook Post

Reading this post today reminded me on a news article I read on “virtual likes” and how advertising through Facebook doesn’t necessarily mean you’ll be any better off. It all comes down to the level of engagement user’s have with a profile page. If users are just liking the page and not interacting with your posts or general content, those likes are worth nothing. Some companies are wising up to the effectiveness of Facebook’s advertising strategy.

Limited Run isn’t the first to ditch Facebook ad’s, General Motor’s pulled away from Facebook ad’s earlier this year due to the ad’s Facebook produce do not have the visual impact needed to justify the cost.

I think certain aspects of Facebook is a joke filled mostly of people looking for attention, not an effective marketing tool.

Simple Way To Integrate Facebook Connect Using ASP.NET

Facebook ConnectIf I need to login and authenticate a Facebook user in my ASP.NET website, I either use the Facebook Connect's JavaScript library or SocialAuth.NET. Even though these two methods are sufficient for the purpose, I don't think it's the most ideal or efficient way.

The Facebook Connect JavaScript library is quite basic and doesn't have the flexibility required for full .NET integration through FormsAuthentication. Whereas SocialAuth.NET provides full .NET integration and all authentication is done server-side with minimal development.

I'd say if you are looking for a straight-forward way to integrate social site authentication, SocialAuth.NET is the way to go. It's API can communicate with other social sites such as Twitter, LinkedIn and Gmail.

Recently, I found a better and more efficient way to authenticate Facebook users on my site using Graph API and Hammock.

Hammock is a C# a REST library for .NET that greatly simplifies consuming and wrapping RESTful services. This allows us to embrace the social site’s core technology instead of using varied SDK's or API's. There are many community driven frameworks and API's readily available on the Internet, but they can really cause problems if they evolve too quickly or haven’t been thoroughly tested.

Suddenelfilio, has written a useful blog post on connecting Facebook using Hammock. You will see by his example that you can interact with Facebook anyway you want.

The same principle could also be applied to other website API's that use REST based services, such as Twitter.

Running Facebook Applications Locally

Having the ability to run and develop Facebook applications within the comfort of a local environment is a must. Previously, I always thought in order to work on Facebook applications a public facing URL was required to allow Facebook to communicate with your application directly. Fortunately this is not the case.

All you need to do is set up a server alias in your hosts file and use this alias as an “App Domain” within your Facebook Application settings.

Quick Example

I created a new site in Microsoft IIS called: “facebook.surinder-test.com” running under port 8008. Feel free to change the port number to your own choosing. To browse the site we need to add this web address to Windows host file (C:\Windows\System32\drivers\etc\):

# localhost name resolution is handled within DNS itself.
#    127.0.0.1       localhost
#    ::1             localhost

127.0.0.1     facebook.surinder-test.com
127.0.0.1     localhost

This will route all your requests to ”facebook.surinder-test.com” to localhost.

Next, make the following changes to your Facebook Application settings page:

 

FB App Screen

Integrating Into Google Plus - Is it worth it?

Google PlusWhen I first heard Google were introducing their own social-networking platform, I was intrigued to say the least on what they could offer compared to the other social sites I use: Facebook and Twitter.

As I stated in one of my earlier posts, I am more of a tweeter since I can share my blog posts easily along with my random ramblings. I think Facebook will have a problem competing alongside Twitter or Google+. Facebook is seen to be more of a personal social network rather than a open professional network and that’s its biggest downfall. It’s quite difficult to cross the boundaries between posting professional/business content alongside personal posts. Thankfully, this is something Google Plus does quite well through its new “circle’s” feature allowing complete control on who see’s what.

I jumped at the chance of using Google Plus when I was offered an invite during the initial release. I was very impressed. Simple and straight-forward. My posts looked really beautiful within its minimalist user interface. Well what else would you expect from Google? Don’t get me started on the eye-sore that is Facebook’s new interface – I’ll leave that for another blog post.

For me, Google Plus is like an extension of Twitter with some added benefits such as:

  • Ability to make posts private/public.
  • Follow people by adding them to a circle.
  • No character limit on the length of posts.
  • Nice interoperability with the search-daddy that is Google.

For a new social networking site, I get a higher click-through-rate to my blog than I ever got compared to tweeting on Twitter. In the process, I managed to get more people adding me to their circle. So take any remarks regarding the inactivity of Google+ with a pinch of salt. I don’t buy it. Google encompasses a big community that you feel part of.

I briefly touched upon the interoperability factor with Google search. People underestimate the power of having the backing of Google search. For example, what if you wrote an article and linked it to your Google+ profile? This information will be displayed as author information within search results to help users discover great content and learn more about the person who wrote the article.

One thing that did surprise me is the fact that at this point in time there’s no advertisement. Unlike its predecessors (yes I that’s how confident I am in Google Plus), you always manage to find advertisement in some form or another. I can view my profile page without constantly having an advert rubbing my single relationship status to my face – something Facebook does far too often.

I trust Google more with my data over Facebook any day. I know Google can’t exactly be trusted either but unlike Facebook they’re not always in the the news on a monthly basis regarding some type of data scandal. At time of writing, it is being reported Facebook is now facing a privacy suit over internet tracking.

In conclusion, integrating ones self into Google Plus is definitely worth it. I only recently started to make more of an effort on Google+ and I find myself posting my content here over other social-networking sites. The key to making a good start is to make some of your posts public to show others your interests and even connect to these type of people either by adding them to a circle or joining a hangout.

On a final note, if you have a Google Plus account and like what I post then why not circle me. :-)

Has Facebook Redefined Friendship?

Definition Of FriendI was a late bloomer when when it came to joining the social networking giant that is Facebook (around late 2007). The only reason I can remember for ever joining the site was just because all people around me were submitting their profiles like crazy. Not wanting to miss out on this new trend, I decided to “pop” my social networking cherry and take the plunge!

Looking back on my first experience on Facebook I was amazed at how easily I could connect with friends and people I used to know from a past life (school, work etc). Within a few months my Facebook profile spread through the social networking vine in quick haste and found myself receiving friend requests. But it became ever so prevalent that the people who requested me to add them as a friend weren’t people I would necessarily call a friend. I knew of them and that is where my connection ends. So in some ways Facebook has redefined the term “friend”.

Facebook has broken down the friendship barriers considerably. Its made it really easy. Too easy in fact. It was only a couple days ago when my sister said: “Look! I got more friends than you!”. In all honesty I wasn’t really bothered…ok maybe a little. This is where personal feelings come into play.

One of the feelings I will call: “Facebook guilt”. Facebook guilt is when you receive a friend request and don’t act on it. You simply ignore it hoping they would just forget or even worse…remove their request altogether. Hoping by not accepting their friend request you haven’t made an enemy or caused emotional discourse.

Then there is “Facebook rejection”. An example of this is the following conversation I had with with a work mate of mine a few years back:

Anonymous friend: I’ve sent you a Facebook friend request.
Me: Ok cool.
Anonymous friend: How come you haven’t approved it yet?
Me: Mate, I’ve been on holiday for over a week and haven’t checked Facebook yet. I’ll do it today.
Anonymous friend: Thanks Surinder!

I was surprised that he took not responding to his friend request as a personal hit.

Social Networking sites have created a trend that makes us more interested in the number of people in our social circles rather than the relationships we have with them.

So where do I stand in the social networking medium? My Facebook activity has drastically declined over the years. Currently, I have 114 friends with majority of them I know quite well and only a handful of them I haven’t really met. Nowadays, I have become more of a tweeter. I just feel that Twitter has met my social needs over Facebook. Its just more flexible and open. If someone likes you they follow you, if not they don’t.