The new Facebook application template and library for ASP.NET MVC

The new Facebook application template and library for ASP.NET MVC

Rate This
  • Comments 37

If you’re looking to build a Facebook App using ASP.NET MVC, then you’re in the right place. With the release of ASP.NET and Web Tools 2012.2, we’ve added lots of updates to our Facebook Application template and library since we first introduced it in the Preview.

The library, Microsoft.AspNet.Mvc.Facebook, can be downloaded as an independent NuGet package and it contains everything you need to create a Facebook Canvas application using ASP.NET MVC. It’s already included by default in the Facebook Application template to give you a quick jump start on building a Canvas Page.

Please refer to the tutorial for instructions on how to setup the Facebook Application template. In this post I’ll mainly focus on the design/API changes that we made from the Preview to RC. This will give you a pretty good idea of how the new APIs work in the updated Facebook Application template.

image

Introducing FacebookContext

Before RC, we used to model bind the User and other Facebook objects as parameters. The problem with model binding these parameters is that we needed to call Facebook Graph API to get the values and the model binders in MVC don’t support async. That’s why in RC, we’re replacing them with a FacebookContext parameter from which you can access the user ID, access token and the parsed signed request. More importantly, the FacebookContext has an instance of the FacebookClient which can be used to make asynchronously calls to Facebook to get the values for the User and other Facebook objects.

image

The instance of FacebookClient is populated with the required information such as AppId, AppSecret and AccessToken, so it’s ready to use.

public async Task<ActionResult> Index(FacebookContext context)
{
    if (ModelState.IsValid)
    {
        MyAppUser user = await context.Client.GetCurrentUserAsync<MyAppUser>();
        return View(user);
    }
 
    return View("Error");
}

FacebookClient Extensions

We’ve added a number of extension methods to FacebookClient to make the common tasks, such as getting the current user and his/her friends, simpler.

image

In fact, most of the extension methods above are based on GetFacebookObjectAsync<TFacebookObject>(string objectPath). If you’re familiar with Facebook C# SDK, you might be wondering how it is different from GetTaskAsync<TResult>(string path). Well, the main difference between them is that GetFacebookObjectAsync<TFacebookObject>(string objectPath) will automatically generate “fields” query parameter when making the request so you get back only the properties you need. It reduces the payload size and sometime this can be quite significant. For example, if you have the following type named “MyUser”.

public class MyUser
{
    public string Id { get; set; }
    public string Name { get; set; }
    public string Birthday { get; set; }
}

GetFacebookObjectAsync<MyUser>(“me”) will get back something like the following.

{
    "id": "1234567",
    "name": "sample name",
    "birthday": "01/01/2000"
}

but GetTaskAsync<MyUser>(“me”) will get back the full payload. The difference can be even bigger when you’re retrieving a list of friends.

{  
    "id": "1234567",
    "name": "sample name",
    "first_name": "sample name",
    "middle_name": "sample name",
    "last_name": "sample name",
    "link": "https://www.facebook.com/sample",
    "username": "sample",
    "birthday": "01/01/2000",
    "location": 
    {
        "id": "123456789",
        "name": "Redmond, Washington"  
    },
    "quotes": "Very long quotes...................",
    "gender": "male",
    "email": "sample@sample.com",
    "timezone": -4,
    "locale": "en_US",
    "verified": true,
    "updated_time": "2012-10-11T00:25:53+0000"
}

Being able to automatically generate the “fields” query parameter also makes it easier to retrieve connections (Picture, Friends, Likes, etc) as fields. For instance, you can get back the user information and what user Likes (a connection) in a single request by defining the types below. GetFacebookObjectAsync<MyUser> will automatically take care of generating the appropriate “fields” query to include the Likes connection (assuming you have user_likes permission).

public class MyUser
{
    public string Id { get; set; }
    public string Name { get; set; }
    public string Birthday { get; set; }
    public FacebookGroupConnection<Like> Likes { get; set; }
}
 
public class Like
{
    public string Name { get; set; }
    public string Category { get; set; }
}

Modeling Facebook Objects

Unlike before, where your Facebook user/object types were required to derive from FacebookUser/FacebookObject, now you can use any POCO type as long as it contains public properties with the matching name. We’ll deserialize the Facebook response using Json.NET. For example, the following Facebook response for a user will match the “MyUser” type below.

{  
    "id": "1234567",
    "name": "sample name",
    "link": https://www.facebook.com/sample
}

Note that the property derialization is case insensitive.

public class MyUser
{
    public string Id { get; set; }
    public string Name { get; set; }
    public string Link { get; set; }
}

Defining Connections

As noted previously, now you can include connections (Picture, Friends, Likes, etc) as properties using FacebookConnection<T> and FacebookGroupConnection<T>.

image

public class MyAppUser
{
    public string Name { get; set; }
    public string Email { get; set; }
 
    public FacebookConnection<FacebookPicture> Picture { get; set; }
    public FacebookGroupConnection<MyAppUserFriend> Friends { get; set; }
}
 
public class FacebookPicture
{
    public string Url { get; set; }
}
 
public class MyAppUserFriend
{
    public string Name { get; set; }
    public string Link { get; set; }
 
    public FacebookConnection<FacebookPicture> Picture { get; set; }
}

These types will match exactly how Facebook returns the connection data in JSON.

{
    "name": "sample name",
    "email": "sample@sample.com",
    "id": "1234567",
    "picture": {
        "data": {...}
    },
    "friends": {    
        "data": [      
            {...},
            {...},
            {...}
        ]
    }
}

Property Attributes

Since we use Json.NET to do the deserialization, you can use JsonProperty/JsonIgnore to rename/exclude a property.

public class MyAppUser
{
    [JsonIgnore] // This ignores the property
    public string MyProperty { get; set; }
 
    [JsonProperty("picture")] // This renames the property to picture.
    public FacebookConnection<FacebookPicture> ProfilePicture { get; set; }
}

In addition you can apply modifiers to the connections using FacebookFieldModifier. For example, you can get the URL to a larger profile picture by adding the modifier “type(large)” to Picture. Go to Facebook Graph API to see the modifiers that are available. When GetFacebookObjectAsync<TFacebookObject> makes the request, these modifiers will be added to the ?field query parameter.

public class MyAppUser
{
    [FacebookFieldModifier("type(large)")] // This sets the picture size to large.
    public FacebookConnection<FacebookPicture> Picture { get; set; }
}
 
public class FacebookPicture
{
    public string Url { get; set; }
}

Facebook Authorization

FacebookAuthorizeAttribute can be used just like before to ensure that the signed_request parameter (sent by Facebook) is valid before invoking an action. Additionally, you can require a set of permissions to be granted before reaching the action by passing them into the attribute. In case the authorization fails, either because of invalid signed request or missing permissions, the users will be redirected to a Facebook OAuth dialog asking them to login or grant the required permissions.

Note that FacebookAuthorizeAttribute is not an authorization filter anymore, the actual authorization is done by FacebookAuthorizeFilter. That’s why it’s important to register the FacebookAuthorizeFilter globally to enable this functionality.

public static class FacebookConfig
{
    public static void Register(FacebookConfiguration configuration)
    {
        // Other settings removed for clarity...
 
        // Adding the authorization filter to check for Facebook signed requests and permissions
        GlobalFilters.Filters.Add(new FacebookAuthorizeFilter(configuration));
    }
}

Having a global authorization filter allowed us to combine the permissions declared on both the controller and the action. Which means the user will see the OAuth dialog once instead twice for the “Profile” action below when the permissions are missing.

[FacebookAuthorize("email")]
public class UserController : Controller
{
    [FacebookAuthorize("user_photos")]
    public async Task<ActionResult> Profile(FacebookContext context) 
    {
        // Implementation removed for clarity
    }
}

image

AuthorizationRedirectPath

When the permissions are missing, now you have the option of showing an “info” page before redirecting user to the OAuth dialog. In that page you can explaining why your app requires certain permissions so that users are more likely to grant them. To do that, add the following to your web.config and the user will be redirected to Home/Permissions when the authorization fails due to missing permissions.

<appSettings>
  <add key="Facebook:AuthorizationRedirectPath" value="Home/Permissions" />
</appSettings>

On the action that is receiving the redirect, you can use FacebookRedirectContext parameter to access information like the required permissions and the RedirectUrl to the Facebook OAuth dialog.

public class HomeController : Controller
{
    // Other actions removed for clarity...
 
    // This action will handle the redirects from FacebookAuthorizeFilter when 
    // the app doesn't have all the required permissions specified in the FacebookAuthorizeAttribute.
    // The path to this action is defined under appSettings (in Web.config) with the key 'Facebook:AuthorizationRedirectPath'.
    public ActionResult Permissions(FacebookRedirectContext context)
    {
        if (ModelState.IsValid)
        {
            return View(context);
        }
 
        return View("Error");
    }
}
@using Microsoft.AspNet.Mvc.Facebook
@model FacebookRedirectContext
 
@if (Model.RequiredPermissions.Length > 0)
{
    <h3>You need to grant the following permission(s) on Facebook to view this page:</h3>
    <ul>
        @foreach (var permission in Model.RequiredPermissions)
        {
            <li>@permission</li>
        }
    </ul>
    <a class="buttonLink" href="@Html.Raw(Model.RedirectUrl)" target="_top">Authorize this application</a>
}

Facebook Realtime Update Support

Out of the box, we provide you with an abstract FacebookRealtimeUpdateController, which is a Web API controller that will handle all the HTTP requests sent by Facebook Realtime Update service. It will take care of verifying the subscription and validating the integrity of the payload by checking the X-Hub-Signature HTTP header. All you need to provide is a verify token and your business logic to handle the update. Here is a sample implementation of FacebookRealtimeUpdateController.

public class UserRealtimeUpdateController : FacebookRealtimeUpdateController
{
    private readonly static string UserVerifyToken = ConfigurationManager.AppSettings["Facebook:VerifyToken:User"];
 
    public override string VerifyToken
    {
        get
        {
            return UserVerifyToken;
        }
    }
 
    public override Task HandleUpdateAsync(ChangeNotification notification)
    {
        if (notification.Object == "user")
        {
            foreach (var entry in notification.Entry)
            {
                // Your logic to handle the update here
            }
        }
    }
}

Note that you can have multiple custom FacebookRealtimeUpdateController to handle different subscriptions (Users, Permissions, etc) with different verify tokens.

FacebookConfiguration

The static FacebookSettings class has been replaced by FacebookConfiguration. Notice that many components such as FacebookAuthorizeFilter are taking an instance of FacebookConfiguration in the constructor which makes unit testing easier. For global access to FacebookConfiguration within the application, you can use GlobalFacebookConfiguration.Configuration, which is a singleton instance.

image

LoadFromAppSettings

This method loads the following properties from appSettings.

  • AppId – this property is required.
  • AppSecret – this property is required.
  • AppNamespace – this property is required if you provided a namespace to the app you created on https://developers.facebook.com/apps.
  • AppUrl – this property is optional. By default it’s set to https://apps.facebook.com/{AppId} or https://apps.facebook.com/{AppNamespace} when the AppNamespace is set. The only time you might want to set this is if Facebook changes their app URL structure.
  • AuthorizationRedirectPath – this property is optional. See the AuthorizationRedirectPath section above for more information.
<appSettings>
  <add key="Facebook:AppId" value="" />
  <add key="Facebook:AppSecret" value="" />
  <add key="Facebook:AppNamespace" value="" />
  <add key="Facebook:AppUrl" value="" />
  <add key="Facebook:AuthorizationRedirectPath" value="" />
</appSettings>

ClientProvider property

You can set this property to customize how the FacebookClient is created. GlobalFacebookConfiguration.Configuration uses an instance of DefaultFacebookClientProvider which creates clients that use Json.NET serializers.

PermissionService property

You can set this property to change how current user permissions are retrieved. GlobalFacebookConfiguration.Configuration uses an instance of DefaultFacebookPermissionService which calls into Facebook Graph API to retrieve the current user permissions.

 

Well, that concludes our overview of the new APIs in the Facebook Application library. I would encourage you to install the ASP.NET and Web Tools 2012.2 and start playing with the Facebook Application template. Please do send us your feedback and feel free to use our CodePlex site to report any issues you might find.

Have fun building Facebook Apps with ASP.NET MVC!

Yao

Leave a Comment
  • Please add 7 and 6 and type the answer here:
  • Post
  • Thanks Yao, I'll test tomorrow at work.

  • Hi Yao,

    I can't find the "FacebookConnection" object nor the "FacebookGroupConnection"

    Can you help me?

  • Hi Alex,

    Did you install the latest update (www.asp.net/.../fall-2012-update)?

    FacebookConnection<T> and FacebookGroupConnection<T> should be under the namespace Microsoft.AspNet.Mvc.Facebook in Microsoft.AspNet.Mvc.Facebook.dll

    Let me know if you still have problem finding them after installing the update.

  • Hi Yao,

    I've found it. It's because my User inherits Friend and caused this exception:

    Circular type references are not supported.

    I have removed the Inheritance and it worked.

  • Hi Yao,

    I'm doing Facebook development with the new Microsoft.AspNet.Mvc.Facebook sdk and Authroize attributes and filters. Works great on my desktop. When I start the sample app with my test mobile url end point and the authentication process starts it short circuits half way through with the following message:

    An error occurred. Please try again later.

    API Error Code: 196

    API Error Description: Cannot redirect to desktop web canvas URL on a mobile device

    Error Message: redirect_uri is not owned by the application.

    I've also tried setting the Facebook:AppUrl appsetting in the config to my mobile uri and then the browser gets caught in a redirect loop where the action decorated with the FacebookAuthroize attribute does not recognize authentication has already occured. How do I get a mobile site working with the FacebookAuthorize attribute?

  • Hi John,

    Currently the Microsoft.AspNet.Mvc.Facebook SDK is limited to Facebook Canvas apps: developers.facebook.com/.../canvas. However, we'll consider providing a better support for Facebook Mobile web apps in a future release.

    Thanks,

    Yao

  • Hi Yao,

    From your last comment, can I assume that the template won't work properly if you put your app on a facebook page tab?

    I created a canvas app with the template which is working well. When I add it to a page tab, it doesn't work properly - it seems that POST requests work ok but when I click on a normal hyperlink (GET), the app breaks out of the page tab and opens the canvas app page instead.

    Do you have any idea why this should be?

    By the way, your previous reply to me worked great.

    Thanks Paulo

  • Further to my comment above, I think it's because the app reauthenticates if the signed_request is not present in the GET request and because I have actions decorated with [FacebookAuthorize] it reauthenticates and then redirects to the canvas app url outside of the frame. As an experiment, I appended the signed_request to the link href and it worked as I want, i.e. stayed inside the page tab.

    Any idea how I should proceed?

  • Hi Yao,

    I'm using web api for my ajax calls and i want to gain access to the FacebookContext object and all of it's benefits,

    How can i achieve this?

  • Hi Paulo,

    Yes, currently the Microsoft.AspNet.Mvc.Facebook is primarily targeting the Canvas app. We'll look into providing better support for Page Tab and Mobile in a future release. Meanwhile, the workaround of passing the signed request should probably be fine. Another thing you can look into is to extend the FacebookAuthorizeFilter. It has a couple of methods that you can override to change the redirect URL (CreateRedirectResult, OnAuthorization).

  • Hi Alex,

    You can pass the raw signed_request string, which can be obtained from Request.Params["signed_request"], to Web API and then create the FacebookContext based on the signed_request. Here is a sample code snippet on how to do that: gist.github.com/a67081576f0ff50edf3a

  • Hi Yao,

    Thanks for your quick response.

    I tried using Request.Params["signed_request"] in my web api controller but I got a null string.

    Any idea where did i go wrong?

  • Hi Alex,

    Request.Params["signed_request"] only works when Facebook is trying to load your MVC page through IFrame. What you can do here is try generating the signed_request string in a hidden field using @Html.FacebookSignedRequest(), grab it using JQuery or JS ($("#signed_request").val()) and pass the signed_request as a query parameter named "signedRequest. Your Web API should be able to get the signed_request using a method signature that look like this: Get(string signedRequest).

  • Thanks Yao, it worked ))

  • How do use this to only login with facebook. I don't want to make a canvas app.

Page 2 of 3 (37 items) 123