本文接在Azure上构建一个基于Facebook的营销式应用程序(上) 和
请看下篇《在Azure上构建一个基于Facebook的营销式应用程序(下)》
在一个Canvas页面
这个应用程序在Facebook上的Canvas页面会映射到这个应用程序在Azure上的base URL(例如:http://azurefacebooksample.cloudapp.net/)。这是一个展示初始的联系信息的窗体。这是基本的实现:
Listing 1
[CanvasAuthorize(Perms = "user_birthday")]
public ActionResult Index()
{
FacebookApp app = new FacebookApp();
dynamic response = app.Api("me");
Contact contact = new Contact();
contact.FirstName = response.first_name;
contact.LastName = response.last_name;
string query = "select birthday_date from user where uid = me()";
response = app.Fql(query);
if (response.Count > 0)
contact.DateOfBirth = response[0].birthday_date;
return View(contact);
}
这看起来十分的简单,这主要得益于Facebook C# SDK。这个SDK通过“CanvasAuthorize”特性处理了Facebook OAuth身份验证和应用程序的授权,并且提供了一个“FacebookApp”对象。你应该还记得,在一个新用户运行“AzureSample”以前,他们必须要给这个应用程序授予可以访问这个用户的出生日期的特殊权限。为了达到这个目的,我们需要做的所有事情就是,使用“CanvasAuthorize”特性,对任何一个需要授权的控制器操作进行标记,指定需要请求的扩展权限是可选的(在这里,我们指定了“user_birthday”权限)。
“FacebookApp”对象可以访问经过签名的请求信息,也可以访问Facebook的Graph API和REST API。在这个控制器中,我们使用“FacebookApp”调用一个Graph API(“me”),来返回当前用户的信息(其中包括***个名字和***一个名字)。我们不能使用这个Graph API来获取这个用户的出生日期,但是,我们可以求助于Facebook FQL,创建一个查询来找到当前用户的出生日期。我们可以通过“FacebookApp”来使用FQL(Formal Query Language) 。我们把这个用户的信息添加到一个新的“Contact”对象中,这是一个来自于域对象的数据模型对象。然后,我们把这个“contact”放到一个视图中。
这是这个索引视图的完整代码:
Listing 2
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage<AzureFacebookSample.Domain.Models.Contact>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Home
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<div>Please provide the following information. All fields are required.</div>
<% using (Html.BeginForm())
{%>
<%: Html.ValidationSummary(true)%>
<div class="editor-label">
<%: Html.LabelFor(model => model.FirstName)%>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.FirstName)%>
<%: Html.ValidationMessageFor(model => model.FirstName)%>
</div>
<div class="editor-label">
<%: Html.LabelFor(model => model.LastName)%>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.LastName)%>
<%: Html.ValidationMessageFor(model => model.LastName)%>
</div>
<div class="editor-label">
<%: Html.LabelFor(model => model.Email)%>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.Email)%>
<%: Html.ValidationMessageFor(model => model.Email)%>
</div>
<div class="editor-label">
<%: Html.LabelFor(model => model.DateOfBirth)%>
</div>
<div class="editor-field">
<%: Html.EditorFor(model => model.DateOfBirth)%>
<%: Html.ValidationMessageFor(model => model.DateOfBirth)%>
</div>
<div>You must be 18 or older to participate.</div>
<div class="editor-label">
<%: Html.LabelFor(model => model.Zip)%>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.Zip)%>
<%: Html.ValidationMessageFor(model => model.Zip)%>
</div>
<p>
<input type="submit" value="Next"/>
</p>
<%} %>
</aspContent>
这个视图指定了用于收集联系信息的窗体字段。验证是通过“Contact”数据模型上的特定注解来实现的。当用户点击“next”按钮的时候,这些窗体字段会被发送到控制器的post操作中:
Listing 3
[HttpPost]
[CanvasAuthorize]
Public ActionResult Index(Contact contact)
{
FacebookApp app = new FacebookApp();
contact.PartitionKey = app.UserId.ToString();
if (ModelState.IsValid)
{
contactRepository.Save(contact);
return this.CanvasRedirectToAction("SelectStore");
}
Return View(contact);
}
除了从窗体返回的联系信息之外,我们还会通过“FacebookApp”对象来抓取用户的Facebook ID,然后放到“Contact”中,以便于我们可以跟踪将来对这个应用程序的访问(客户只可以注册一次,领取一次奖品)。
#p#
在Azure Storage中存储“Contact”
在上面那段代码中,你可能已经注意到了,存储客户的Facebook ID的“Contact”属性叫作“PartitionKey”,而不是“FacebookId”。“Contact”是一个数据模型,这意味着它可以直接存储到Azure Table Storage中。它继承于“TableStorageEntity”,对于可存储的实体来说,“TableStorageEntity”是一个方便的基类,在这个类中,包含了“PartitionKey”属性和“RowKey”属性。
“AzureFacebookSample”使用Azure Table Storage,因为在Azure Table Storage中,插入操作和主键查询都很快,而且,它可以无限地扩展。在一个Azure表中,除了这些实体属性所必须列之外,每行还包括一个partition key,一个row key,和一个时间戳。为了获得***的性能,查询应该被严格地限制在那些基于主键的字段中,不应该包括含他的属性。
我们把Facebook ID作为partition key,把blank作为row key,然后把“contact”存储到一个Azure表中。这样做可以让存储的“contact”更容易使用(使用起来也更快),因为我们可以通过Facebook ID来查找或更新它们。
Web角色和Worker角色都可以通过域项目提供的“ContactRepository”来访问存储的“contact”对象。通过Windows Azure Toolkit中的一些类,“ContactRepository”可以直接和Azure storage进行交互,这可以让实现变得更加简洁。
Listing 4
public class ContactRepository : IContactRepository
{
readonly AzureTable<Contact> _table;
public ContactRepository()
{
var factory = new AzureStorageFactory ();
_table = (AzureTable<Contact>) factory.GetTable<Contact>();
}
public Contact GetFromFacebookId(Int64 facebookId)
{
return this.Get(facebookId.ToString());
}
public Contact Get(string partitionKey)
{
return _table.Get(c => c.PartitionKey == partitionKey && c.RowKey == "");
}
public void Save(Contact contact)
{
_table.AddOrUpdate(contact);
}
public void Delete(Contact contact)
{
_table.Delete(contact);
}
}
这段代码展示了“ContactRepository”的完整实现。在构造函数中,我们创建了一个“AzureStorageFactory”对象,然后使用“factory”来获取“Contact”表。“AzureStorageFactory”和“AzureTable”都是在WindowsAzureToolkit中定义的类,这两个类可以让使用Azure Table Storage变得更加容易。实际上,这个“contact”容器中的大多数方法都是“AzureTable”中的对应方法的封装。“Get()”方法直接通过“partition key”来查询“contact”。
在这个处理程序的***,应用程序使用这个容器把联系信息写入到Azure Storage中。因为处理过程还没有完成,所以这个“contact”还不能标记为已注册(在选择了商店以后,才应该这么做)。在索引操作开始的时候,这个“contact”容器还可以用来验证。
Listing 5
if (AlreadyRegistered(app.UserId))
{
return this.CanvasRedirectToAction("SignupComplete");
}
验证的实现如下所示:
Listing 6
private bool AlreadyRegistered(Int64 facebookId)
{
Contact contact = contactRepository.GetFromFacebookId(facebookId);
Return (contact != null) && contact.Registered;
}
本文接在Azure上构建一个基于Facebook的营销式应用程序(上)
请看下篇《在Azure上构建一个基于Facebook的营销式应用程序(下)》
原文名:Building a Facebook Marketing App on Azure 作者:Steve Apiki
【本文乃51CTO精选译文,转载请标明出处!】
【编辑推荐】