Maarten Balliauw {blog}

ASP.NET MVC, Microsoft Azure, PHP, web development ...

NAVIGATION - SEARCH

ASP.NET MVC 2 Preview 1 released!

Today, Phil Haack did a blog post on the release of ASP.NET MVC 2 Preview 1! Get it while it’s fresh :-) An updated roadmap is also available on CodePlex.

Guess now is about time to start revising my ASP.NET MVC 1.0 Quickly book…

kick it on DotNetKicks.com

New features in ASP.NET MVC Preview 1

Templated helpers

Templated helpers are not new: ASP.NET Dynamic Data already used this feature. Basically, you are creating a default control when you want to display/edit a specific data type in a view. For example, a System.String will have a user control defined that renders a textbox. However, if you want this to be a TinyMCE control by default, you’ll have to change the templated helper in one place and you’re done.

More concrete: create a new view in your application: Views\Shared\DisplayTemplates\String.ascx. The code for that view would be:

[code:c#]

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<strong><%= Html.Encode(Model) %></strong>

[/code]

There you go: every string you want to put in a view using the <%= Html.DisplayFor(c => person.Name) %> HtmlHelper will render that string in bold.

Note that your domain class can also use UI hints to specify the templated helper to use when rendering:

[code:c#]

public class Person {
    [UIHint("NameTextBox")]
    public string Name { get; set; }

    // ...
}

[/code]

This will make sure that when Person’s Name is rendered, the NameTextBox.ascx control will be used instead of the default one.

Areas

Finally, native support for areas! Areas help you split your application into more logical subsections, which is useful when working with large projects.Each area is implemented as a separate ASP.NET MVC. When compiling, ASP.NET MVC invokes a build task which merges all areas into the main application.

Check MSDN for a detailed example on using areas. I’ll get this one in MvcSiteMap as soon as possible.

Support for DataAnnotations

The new ASP.NET MVC 2 default model binder makes use of the System.ComponentModel.DataAnnotations namespace to perform validation at the moment of binding data to the model. This concept was used for ASP.NET Dynamic Data, recently picked up by the RIA services team and now also available for ASP.NET MVC.

Basically, what you have to do in order to validate your domain objects, is decorating the class’properties with some DataAnnotations:

[code:c#]

public class Person {
    [Required(ErrorMessage = "Name is required.")]
    [StringLength(60, ErrorMessage = "Name should not exceed 60 characters.")]
    public string Name { get; set; }

    // ...
}

[/code]

Easy no? Now just use the model binder inside your controller and validation will occur “automagically”.

Also check my blog post on TwitterMatic for another example.

HttpPost attribute

A small update: [AcceptVerbs(HttpVerbs.Post)] can now be written as [HttpPost]. Easier to read IMHO.

This means that:

[code:c#]

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Person person) {
    // ...
}

[/code]

becomes the following:

[code:c#]

[HttpPost]
public ActionResult Create(Person person) {
    //...
}

[/code]

DefaultValueAttribute

Default parameter values in an action method can now be specified using an attribute. This attribute currently only seems to support primitive types (such as integers, booleans, strings, …). Here’s an example:

[code:c#]

public class PersonController : Controller {
    public ActionResult Create([DefaultValue("Maarten")]string name) {
        // ...
    }
}

[/code]

kick it on DotNetKicks.com

ReMIX Belgium session on ASP.NET MVC

image Just learned I’ll be doing a session on ASP.NET MVC at ReMIX Belgium. ReMix brings the best of MIX09 in Las Vegas to Belgium: it bring us international speakers presenting on the best of MIX09, as well as local cases, with a story focus on User Experience (UX).

The session will be around building a Twitter clone in 60 minutes. Bear with me at ReMIX 2009!

Abstract: “What are you doing right now?, that's Twitter's question to its users. How about you creating own microblogging platform? "What are you working on?", "What are you reading?", ..., are all specific questions for your own community. This session takes you along in building a Twitter clone using the ASP.NET MVC framework.”

Query the cloud with PHP (PHPLinq and Windows Azure)

PHPLinq Architecture I’m pleased to announce PHPLinq currently supports basic querying of Windows Azure Table Storage. PHPLinq is a class library for PHP, based on the idea of Microsoft’s LINQ technology. LINQ is short for language integrated query, a component in the .NET framework which enables you to perform queries on a variety of data sources like arrays, XML, SQL server, ... These queries are defined using a syntax which is very similar to SQL.

Next to PHPLinq querying arrays, XML and objects, which was already supported, PHPLinq now enables you to query Windows Azure Table Storage in the same manner as you would query a list of employees, simply by passing PHPLinq a Table Storage client and table name as storage hint in the in() method:

[code:c#]

$result = from('$employee')->in( array($storageClient, 'employees', 'AzureEmployee') )
            ->where('$employee => $employee->Name == "Maarten"')
            ->select('$employee');

[/code]

The Windows Azure Table Storage layer is provided by Microsoft’s PHP SDK for Windows Azure and leveraged by PHPLinq to enable querying “the cloud”.

kick it on DotNetKicks.com

Authenticating users with RPXNow (in ASP.NET MVC)

ASP.NET MVC RPX RPXNow Don’t you get sick of having to register at website X, at website Y, at website Z, …? It is really not fun to maintain all these accounts, change passwords, … Luckily, there are some large websites offering delegated sign-in (for example Google Accounts, Live ID, Twitter OAuth, …). You can use these delegated sign-in methods on your own site, removing the need of registering yet another account. Unfortunately, not everyone has an account at provider X…

A while ago, I found out about the free service JanRain is offering: RPXNow. This (free!) service combines the strengths of several major account providers (Google Accounts, Live ID, Twitter, Facebook, OpenID, …) into one simple API. This means that people are able to log in to your website if they have an account at one of these providers! Here’s how to use it in ASP.NET MVC…

Download sample code: Rpx4Mvc.zip (246.97 kb)

kick it on DotNetKicks.com

Creating some HTML helpers

After registering at RPXNow, you will receive an application name and API key. This application name should be used when embedding the login control that is provided. To simplify embedding a login link, I’ve created two HtmlHelper extension methods: one for embedding the login control, another one for providing a login link:

[code:c#]

public static class HtmlHelperExtensions
{
    public static string RpxLoginEmbedded(this HtmlHelper helper, string applicationName, string tokenUrl)
    {
        return "<iframe src=\"https://" + applicationName + ".rpxnow.com/openid/embed?token_url=" + tokenUrl + "\" scrolling=\"no\" frameBorder=\"no\" style=\"width:400px;height:240px;\" class=\"rpx-embedded\"></iframe>";
    }

    public static string RpxLoginPopup(this HtmlHelper helper, string applicationName, string tokenUrl, string linkText)
    {
        return "<script src=\"https://rpxnow.com/openid/v2/widget\" type=\"text/javascript\"></script><script type=\"text/javascript\">RPXNOW.overlay = true; RPXNOW.language_preference = 'en';</script>" +
            "<a class=\"rpxnow\" onclick=\"return false;\" href=\"https://" + applicationName + ".rpxnow.com/openid/v2/signin?token_url=" + tokenUrl + "\">" + linkText + "</a>";     
    }
}

[/code]

I can now add a login link in my views more easily:

[code:c#]

<%=Html.RpxLoginPopup("localtestapp", "http://localhost:1234/Account/Login", "Login") %>

[/code]

Creating the RPX implementation

The RPX implementation is quite easy. When a user logs in, a token is posted to your web application. Using this token and the API key, you can query the RPX service for a profile Here’s a simple class which can take care of all this:

[code:c#]

public class RpxLogin
{
    protected string apiKey = "";

    public RpxLogin(string apiKey)
    {
        this.apiKey = apiKey;
    }

    public RpxProfile GetProfile(string token)
    {
        // Fetch authentication info from RPX
        Uri url = new Uri(@"https://rpxnow.com/api/v2/auth_info");
        string data = "apiKey=" + apiKey + "&token=" + token;

        // Auth_info request
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
        request.Method = "POST";
        request.ContentType = "application/x-www-form-urlencoded";
        request.ContentLength = data.Length;

        StreamWriter requestWriter = new StreamWriter(request.GetRequestStream(), Encoding.ASCII);
        requestWriter.Write(data);
        requestWriter.Close();

        HttpWebResponse response = (HttpWebResponse)request.GetResponse();
        TextReader responseReader = new StreamReader(response.GetResponseStream());
        string responseString = responseReader.ReadToEnd();
        responseReader.Close();

        // De-serialize JSON
        JavaScriptSerializer serializer = new JavaScriptSerializer();
        RpxAuthInfo authInfo = serializer.Deserialize<RpxAuthInfo>(responseString);

        // Ok?
        if (authInfo.Stat != "ok")
        {
            throw new RpxException("RPX login failed");
        }

        return authInfo.Profile;
    }
}

[/code]

Note that the RPX service returns JSON data, which I can deserialize using the JavaScriptSerializer. That’s really all it takes to get the logged-in user name.

Plumbing it all together

All of the above can be plumbed together in a new AccountController. This will have to be extended for your application (i.e. for storing the logged in username in a membership database, … Simply add these two action methods in a blank AccountController and you are ready to RPX!

[code:c#]

[HandleError]
public class AccountController : Controller
{
    public ActionResult Login(string token)
    {
        if (string.IsNullOrEmpty(token)) {
            return View();
        } else {
            IRpxLogin rpxLogin = new RpxLogin("b2e418e8e2dbd8cce612b829a9234ed4a763b2c0");
            try
            {
                RpxProfile profile = rpxLogin.GetProfile(token);

                FormsAuthentication.SetAuthCookie(profile.DisplayName, false);
            }
            catch (RpxException)
            {
                return RedirectToAction("Login");
            }

            return RedirectToAction("Index", "Home");
        }
    }

    [Authorize]
    public ActionResult Logout()
    {
        FormsAuthentication.SignOut();
        return RedirectToAction("Index", "Home");
    }
}

[/code]

Download a sample application: Rpx4Mvc.zip (246.97 kb)

kick it on DotNetKicks.com

PHP SDK for Windows Azure - Milestone 2 release

WindowsAzure I’m proud to announce our second milestone for the PHP SDK for Windows Azure project that Microsoft and RealDolmen started back in May. Next to our regular releases on CodePlex, we’ll also be shipping a Zend Framework version of the PHP SDK for Windows Azure. Announcements on this will be made later.

The current milestone is focused on Windows Azure Table Storage, enabling you to use all features this service offers from any PHP application, be it hosted in-premise or on Windows Azure.

Get it while it’s hot: PHP SDK for Windows Azure CTP2 - PHPAzure CTP2 (0.2.0)

Detailed API documentation is provided in the download package, while more descriptive guidance is available on the project site.

Working with Azure Table Storage from PHP

Let’s provide a small example on the new Table Storage support in the PHP SDK for Windows Azure. The first thing to do when you have a clean storage account on the Azure platform is to create a new table:

[code:c#]

/** Microsoft_Azure_Storage_Table */
require_once 'Microsoft/Azure/Storage/Table.php';

$storageClient = new Microsoft_Azure_Storage_Table('table.core.windows.net', 'myaccount', 'myauthkey');
$storageClient->createTable('mynewtable');

[/code]

Easy, no? Note that we did not provide any schema information here as you would do in a regular database. Windows Azure Table Storage can actually contain entities with different properties in the same table. You can work with an enforced schema, but this will be client-side. More info on that matter is available here.

Now let’s add a person to the “mynewtable” in the cloud:

[code:c#]

$person = new Microsoft_Azure_Storage_DynamicTableEntity('partition1', 'row1');
$person->Name = "Maarten";
$person->Age  = 25;

$storageClient->insertEntity('mynewtable', $person);

[/code]

Again, no rocket science. The Microsoft_Azure_Storage_DynamicTableEntity class used provides fluent access to entities in Table Storage. More info on this class is available here.

Now let’s add a property to this $person instance and merge it into Table Storage:

[code:c#]

$person->Blog = "www.maartenballiauw.be";
$storageClient->mergeEntity('mynewtable', $person);

[/code]

Wow! We just added a Blog property to this object! I could have also used updateEnity for this, but that one would have overwritten eventual changes that were made to my $person in the meantime.

Now for some querying. Let’s retrieve all entities in “mynewtable” that have an Age of 25:

[code:c#]

$entities = $storageClient->storageClient->retrieveEntities(
    'mynewtable',
    $storageClient->select()
                  ->from($tableName)
                  ->where('Age eq ?', 25)
);

foreach ($entities as $entity)
{
    echo 'Name: ' . $entity->Name . "\n";
}

[/code]

I guess this al looks quite straightforward. The fluent query building API provides a syntax similar to how you would build a query in SQL.

Another nice feature of the PHP SDK for Windows Azure is support for batch transactions. Here’s an example of how to work with transactions on Table Storage:

[code:c#]

// Start batch
$batch = $storageClient->startBatch();

// Insert entities in batch
$entities = array( ...... );
foreach ($entities as $entity)
{
    $storageClient->insertEntity('mynewtable', $entity);
}

// Commit
$batch->commit();

[/code]

The batch will fail as a whole if one insert, update, delete, ... does not work out, just like with a transaction on a regular relational database like MySQL or SQL Server.

If you're interested in cloud computing and WIndows Azure, and want to keep using PHP, make sure to get the latest version of the PHP SDK for Windows Azure to leverage all functionality that is available in the cloud. Here's the link: PHP SDK for Windows Azure CTP2 - PHPAzure CTP2 (0.2.0)

I’m an MVP ASP.NET!

MVPLogoYesterday evening, I received the following e-mail:

Dear Maarten Balliauw,

Congratulations! We are pleased to present you with the 2009 Microsoft® MVP Award! This award is given to exceptional technical community leaders who actively share their high quality, real world expertise with others. We appreciate your outstanding contributions in ASP/ASP.NET technical communities during the past year.

(...)

Toby Richards
General Manager
Community Support Services

Translated in short: I’m now officially an MVP ASP.NET! I am really honored to be recognized as an MVP. Thank you for the nomination!

The past year has been quite fun, doing lots of blog posts, a book on ASP.NET MVC, various speaking sessions at VISUG, DevDays, PHP Conference, … Guess now I’m obligated to stay active in community :-) The next two weeks will be community less however, since I’m off on a vacation. See/write/… you in a week or two!

How we built TwitterMatic.net - Part 7: Deploying to the cloud

TwitterMatic - Schedule your Twitter updates“Knight Maarten The Brave Coffeedrinker had managed all there is to building an application to interact with the great god of social networking, Twitter. A barn in the cloud, worker roles, web roles, a gate keeper, … The moment of truth came near: would the application survive if it was thrown at the azure cloud? Would the digital villagers like the application?”

This post is part of a series on how we built TwitterMatic.net. Other parts:

kick it on DotNetKicks.com

Deploying to the cloud

First of all, I’m not going into full detail on deployment, as this is really easy: right-click the project in Visual Studio, pick “Publish…”. A package will be built and you will be redirected to the deployment interface, where you can upload the package and start your application. No need to buy servers, no need for your own datacenter: just upload and run!

Deployment interface

Linking a domain name to Azure

One more thing though… The default URL’s on which a Windows Azure is hosted, look like http://twittermatic.cloudapp.net. Though this works perfectly, its not a friendly name in my opinion. I want www.twittermatic.net to point to my application!

There’s a reason for providing these unfriendly URL’s though. They are an abstraction to the virtual IP addresses that Windows Azure uses, making it easier to changes these IP’s without having to stop or disrupt your application.

If you want to make use of your own domain name, there’s an easy option: create a CNAME record in your DNS, and you’re done. For TwitterMatic, I’ve created the following records in my nameserver:

www.twittermatic.net IN CNAME twittermatic.cloudapp.net

Ask your ISP to fix this for you, or check Steve marx’ blog post on how to do this with GoDaddy.com.

Conclusion

RealDolmen Windows Azure “Everything seemed to fall in place. A moment of peace was about to fall on the digital village, since the digital villagers could now tell the latest stories, even when they were asleep or busy working. Knight Maarten The Brave Coffeedrinker finished his job. A sample application in the cloud was created. It was time for him to move on to a new quest.”

If you want the source code for TwitterMatic, check http://twittermatic.codeplex.com.

kick it on DotNetKicks.com

How we built TwitterMatic.net - Part 6: The back-end

TwitterMatic - Schedule your Twitter updates“Now that the digital villagers could enter their messages in the application, another need arose: knight Maarten The Brave Coffeedrinker would have to recruit a lot of slaves to tell all these messages to the great god of social networking, Twitter. Being a peaceful person, our knight thought of some digital slaves, sent from the azure sky. And so he started crafting a worker role.”

This post is part of a series on how we built TwitterMatic.net. Other parts:

kick it on DotNetKicks.com

The back-end

The worker role will monitor the table storage for scheduled Tweets. If it’s time to send them, the Tweet will be added to a queue. This queue is then processed by another thread in the worker role, which will publish the Tweet to Twitter. Well be using two threads for this:

TwitterMatic worker role

We’ll fire up these treads in the worker role’s Start method:

[code:c#]

public class WorkerRole : RoleEntryPoint
{
    protected Thread enqueueingThread;
    protected Thread publishingThread;

    public override void Start()
    {
        RoleManager.WriteToLog("Information", "Started TwitterMatic worker process.");

        RoleManager.WriteToLog("Information", "Creating enqueueing thread...");
        enqueueingThread = new Thread(new ThreadStart(EnqueueUpdates));
        RoleManager.WriteToLog("Information", "Created enqueueing thread.");

        RoleManager.WriteToLog("Information", "Creating publishing thread...");
        publishingThread = new Thread(new ThreadStart(PublishUpdates));
        RoleManager.WriteToLog("Information", "Created publishing thread.");

        RoleManager.WriteToLog("Information", "Starting worker threads...");
        enqueueingThread.Start();
        publishingThread.Start();
        RoleManager.WriteToLog("Information", "Started worker threads.");

        enqueueingThread.Join();
        publishingThread.Join();

        RoleManager.WriteToLog("Information", "Stopped worker threads.");

        RoleManager.WriteToLog("Information", "Stopped TwitterMatic worker process.");
    }

    // ...
}

[/code]

Note that we are also logging events to the RoleManager, which is the logging infrastructure provided by Windows Azure. These logs can be viewed from the Windows Azure deployment interface.

EnqueueUpdates Thread

The steps EnqueueUpdates will take are simple:

EnqueueUpdates Thread

Here’s how to do that in code:

[code:c#]

protected void EnqueueUpdates()
{
    StorageAccountInfo info = StorageAccountInfo.GetDefaultQueueStorageAccountFromConfiguration(true);
    QueueStorage queueStorage = QueueStorage.Create(info);
    MessageQueue updateQueue = queueStorage.GetQueue("updatequeue");
    if (!updateQueue.DoesQueueExist())
        updateQueue.CreateQueue();

    while (true)
    {
        RoleManager.WriteToLog("Information", "[Enqueue] Checking for due tweets...");
        List<TimedTweet> dueTweets = Repository.RetrieveDue(DateTime.Now.ToUniversalTime());
        if (dueTweets.Count > 0)
        {
            RoleManager.WriteToLog("Information", "[Enqueue] " + dueTweets.Count.ToString() + " due tweets.");

            foreach (var tweet in dueTweets)
            {
                if (tweet.SendStatus != "Pending delivery")
                {
                    updateQueue.PutMessage(new Message(tweet.RowKey));
                    tweet.SendStatus = "Pending delivery";
                    Repository.Update(tweet);
                    RoleManager.WriteToLog("Information", "[Enqueue] Enqueued tweet " + tweet.RowKey + " for publishing.");
                }
            }
            RoleManager.WriteToLog("Information", "[Enqueue] Finished processing due tweets.");
        }
        else
        {
            RoleManager.WriteToLog("Information", "[Enqueue] No due tweets.");
        }
        Thread.Sleep(120000);
    }
}

[/code]

PublishUpdates Thread

The steps PublishUpdates will take are simple:

PublishUpdates Thread

Here’s how to do that in code:

[code:c#]

protected void PublishUpdates()
{
    StorageAccountInfo info = StorageAccountInfo.GetDefaultQueueStorageAccountFromConfiguration(true);
    QueueStorage queueStorage = QueueStorage.Create(info);
    MessageQueue updateQueue = queueStorage.GetQueue("updatequeue");
    if (!updateQueue.DoesQueueExist())
        updateQueue.CreateQueue();

    while (true)
    {
        RoleManager.WriteToLog("Information", "[Publish] Checking for pending tweets...");
        while (updateQueue.PeekMessage() != null)
        {
            Message queueItem = updateQueue.GetMessage(120);

            RoleManager.WriteToLog("Information", "[Publish] Preparing to send pending message " + queueItem.ContentAsString() + "...");

            TimedTweet tweet = null;
            try
            {
        tweet = Repository.RetrieveById("", queueItem.ContentAsString());
            }
            finally
            {
                if (tweet == null)
                {
                    RoleManager.WriteToLog("Information", "[Publish] Pending message " + queueItem.ContentAsString() + " has been deleted. Cancelling publish...");
                    updateQueue.DeleteMessage(queueItem);
                }
            }
            if (tweet == null)
                continue;

            IOAuthTwitter oAuthTwitter = new OAuthTwitter();
            oAuthTwitter.OAuthConsumerKey = Configuration.ReadSetting("OAuthConsumerKey");
            oAuthTwitter.OAuthConsumerSecret = Configuration.ReadSetting("OAuthConsumerSecret");
            oAuthTwitter.OAuthToken = tweet.Token;
            oAuthTwitter.OAuthTokenSecret = tweet.TokenSecret;

            TwitterContext ctx = new TwitterContext(oAuthTwitter);
            if (!string.IsNullOrEmpty(ctx.UpdateStatus(tweet.Status).ID))
            {
                RoleManager.WriteToLog("Information", "[Publish] Published tweet " + tweet.RowKey + ".");

                tweet.SentOn = DateTime.Now;
                tweet.SendStatus = "Published";
                tweet.RetriesLeft = 0;
                updateQueue.DeleteMessage(queueItem);
                Repository.Update(tweet);
            }
            else
            {
                tweet.RetriesLeft--;
                if (tweet.RetriesLeft > 0)
                {
                    tweet.SendStatus = "Retrying";

                    RoleManager.WriteToLog("Information", "[Publish] Error publishing tweet " + tweet.RowKey + ". Retries left: " + tweet.RetriesLeft.ToString());
                }
                else
                {
                    tweet.RetriesLeft = 0;

                    tweet.SendStatus = "Failed";

                    RoleManager.WriteToLog("Information", "[Publish] Error publishing tweet " + tweet.RowKey + ". Out of retries.");
                }
                updateQueue.DeleteMessage(queueItem);
                Repository.Update(tweet);
            }
        }
        Thread.Sleep(60000);
    }
}

[/code]

Conclusion

We now have an overview of worker roles, and how they can be leveraged to perform background tasks in a Windows Azure application.

In the next part of this series, we’ll have a look at the deployment of TwitterMatic.

kick it on DotNetKicks.com

How we built TwitterMatic.net - Part 5: the front-end

TwitterMatic - Schedule your Twitter updates“After having found a god-like guardian for his application, Knight Maarten The Brave Coffeedrinker found out that his application still had no functional front-end. It’s OK to have a guardian and a barn in the cloud, but if there’s nothing to guard, this is a bit useless. Having asked the carpenter and the smith of the village, our knight decided that the so-called “ASP.NET MVC” framework might help in his quest.”

This post is part of a series on how we built TwitterMatic.net. Other parts:

kick it on DotNetKicks.com

The front-end

In part 2 of this series, we have already created the basic ASP.NET MVC structure in the web role project. There are few action methods and views to create: we need one for displaying our scheduled tweets and one for scheduling a tweet. We’ll concentrate on the latter in this post.

Action methods

The Create action method will look like this:

[code:c#]

// GET: /Tweet/Create

public ActionResult Create()
{
    TimedTweet tweet = new TimedTweet();

    ViewData["SendOnDate"] = tweet.SendOn.ToShortDateString();
    ViewData["SendOnTime"] = tweet.SendOn.ToShortTimeString();

    return View(tweet);
}

// POST: /Tweet/Create

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(int UtcOffset, string SendOnDate, string SendOnTime, FormCollection collection)
{
    TimedTweet tweet = new TimedTweet(this.User.Identity.Name);
    try
    {
        tweet.SendOn = DateTime.Parse(SendOnDate + " " + SendOnTime).AddMinutes(UtcOffset);

        // Ensure we have a valid SendOn date
        if (!TimedTweetValidation.ValidateFutureDate(tweet.SendOn))
        {
            ModelState.AddModelError("SendOn", "The scheduled time should be in the future.");
        }

        if (this.TryUpdateModel(tweet, new string[] { "Status" }, collection.ToValueProvider()) && ModelState.IsValid)
        {
            // ...
            Repository.Insert(this.User.Identity.Name, tweet);

            return RedirectToAction("Index");
        }
        else
        {
            // ...
            return View(tweet);
        }
    }
    catch
    {
        // ...
        return View(tweet);
    }
}

[/code]

As you can see, we’re doing the regular GET/POST differentiation here: GET to show the Create view, POST to actually do something with user-entered data. Nothing too fancy in the code, just passing some data to the repository we created in an earlier post.

Views

The view for our Create action is slightly more work. I’ve noticed a cool date picker at http://ui.jquery.com, and a cool time picker at http://haineault.com/media/jquery/ui-timepickr/page/. Why not use them both?

Here’s the plain, simple view, no jQuery used:

[code:c#]

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<TwitterMatic.Shared.Domain.TimedTweet>" %>

<asp:Content ID="title" ContentPlaceHolderID="TitleContent" runat="server">
    Schedule a tweet for <%=User.Identity.Name%>
</asp:Content>

<asp:Content ID="content" ContentPlaceHolderID="MainContent" runat="server">
    <h3>Schedule a tweet for <%=User.Identity.Name%></h3>

    <% if (!ViewData.ModelState.IsValid) { %>
        <%= Html.ValidationSummary("Could not schedule tweet. Please correct the errors and try again.") %>
    <% } %>

    <% using (Html.BeginForm()) {%>
        <%=Html.Hidden("UtcOffset", 0)%>

        <fieldset>
            <legend>Schedule a tweet</legend>
            <p>
                <label for="Status">Message:</label>
                <%= Html.TextArea("Status") %>
                <%= Html.ValidationMessage("Status", "*") %>
                <span id="status-chars-left">140</span>
            </p>
            <p>
                <label for="SendOn">Send on:</label>
                <%= Html.TextBox("SendOnDate", ViewData["SendOnDate"]) %>
                <%= Html.TextBox("SendOnTime", ViewData["SendOnTime"]) %>
            </p>
            <p>
                <button type="submit" value="Schedule">
                    Schedule tweet!
                </button>
            </p>
        </fieldset>

    <% } %>

    <p style="clear: both;">
        <%= Html.ActionLink("Back to list of scheduled tweets", "Index", "Tweet", null, new { @class = "more" })%>
    </p>
    <p style="clear: both;">&nbsp;</p>
</asp:Content>

[/code]

Nothing fancy in there: just a boring data-entry form. Now let’s spice that one up: we’ll add the datepicker and timepicker:

[code:c#]

<script type="text/javascript">
    $(function() {
        $("#SendOnDate").datepicker({ minDate: 0, showAnim: 'slideDown', dateFormat: 'mm/dd/yy' });
        $("#SendOnTime").timepickr({ convention: 12, rangeMin: ['00', '05', '10', '15', '20', '25', '30', '35', '40', '45', '50', '55'] });
    });
</script>

[/code]

We’re telling jQuery to make a datepicker of the DOM element with id #SendOnDate, and to make a timepickr of the element #SendOnTime. Now let’s add some more useful things:

[code:c#]

<script type="text/javascript">
    $(function() {
        calcCharsLeft();
        $("#Status").keyup(function() {
            calcCharsLeft();
        });
    });

    var calcCharsLeft = function() {
        var charsLeft = (140 - $("#Status").val().length);

        $("#status-chars-left").html(charsLeft);
        if (charsLeft < 0) {
            $("#status-chars-left").css('color', 'red');
            $("#status-chars-left").css('font-weight', 'bold');
        } else {
            $("#status-chars-left").css('color', 'white');
            $("#status-chars-left").css('font-weight', 'normal');
        }
    }
</script>

[/code]

jQuery will now do some more things when the page has loaded: we’re telling the browser to call calcCharsLeft every time a key is pressed in the message text area. This way, we can add a fancy character counter next to the text box, which receives different colors when certain amount of text is entered.

Validation: using DataAnnotations

In the action methods listed earlier in this post, you may have noticed that we are not doing a lot of validation checks. Except for the “Time in the future” check, we’re actually not doing any validation at all!

The reason for not having any validation calls in my controller’s action method, is that I’m using a different model binder than the default one: the ASP.NET MVC team’s DataAnnotationsModelBinder. This model binder makes use of the System.ComponentModel.DataAnnotations namespace to perform validation at the moment of binding data to the model. This concept was used for ASP.NET Dynamic Data, recently picked up by the RIA services team and now also available for ASP.NET MVC.

Basically, what we have to do, is decorating our TimedTweet class’ properties with some DataAnnotations:

[code:c#]

public class TimedTweet : TableStorageEntity, IComparable
{
    public string Token { get; set; }
    public string TokenSecret { get; set; }

    [Required(ErrorMessage = "Twitter screen name is required.")]
    public string ScreenName { get; set; }

    [Required(ErrorMessage = "Message is required.")]
    [StringLength(140, ErrorMessage = "Message length must not exceed 140 characters.")]
    public string Status { get; set; }

    [Required(ErrorMessage = "A scheduled time is required.")]
    [CustomValidation(typeof(TimedTweetValidation), "ValidateFutureDate", ErrorMessage = "The scheduled time should be in the future.")]
    public DateTime SendOn { get; set; }
    public DateTime SentOn { get; set; }
    public string SendStatus { get; set; }
    public int RetriesLeft { get; set; }

    public bool Archived { get; set; }

    // ...
}

[/code]

See how easy this is? Add a [Required] attribute to make a property required. Add a [StringLength] attribute to make sure a certain length is not crossed, … The DataAnnotationsModelBinder will use these hints as a guide to perform validation on your model.

Conclusion

We now know how to work with ASP.NET MVC’s future DataAnnotations validation and have implemented this in TwitterMatic.

In the next part of this series, we’ll have a look at the worker role for TwitterMatic.

kick it on DotNetKicks.com

How we built TwitterMatic.net - Part 4: Authentication and membership

TwitterMatic - Schedule your Twitter updates“Knight Maarten The Brave Coffeedrinker just returned from his quest to a barn in the clouds, when he discovered that he forgot to lock the door to his workplace. He immediately asked the digital village’s smith.to create a lock and provide him a key. Our knight returned to his workplace and concluded that using the smith’s lock would be OK, but having the great god of social networking, Twitter, as a guardian, seemed like a better idea. “O, Auth!”, he said. And the god provided him with a set of prayers, an API, which our knight could use.”

This post is part of a series on how we built TwitterMatic.net. Other parts:

kick it on DotNetKicks.com

Authentication and membership

Why reinvent the wheel when we already have so many wheel manufacturers? I’m really convinced that from now on, nobody should EVER provide subscribe/login/password retrieval/… functionality to his users again! Use OpenID, or Live ID, or Google Accounts, or JanRain’s RPX bundling all types of existing authentication mechanisms. Did you hear me? NEVER provide your own authentication mechanism again, unless you have a solid reason for it!

Since we’re building an application for Twitter, and Twitter provides OAuth API for delegating authentication, why not use OAuth? As a start, here’s the flow that has to be respected when working with OAuth.

OAuth request flow diagram

Now let’s build this in to our application…

Implementing OAuth in TwitterMatic

First of all: if you are developing something and it involves a third-party product or service, chances are there’s something useful for you on CodePlex. In TwitterMatic’s case, that useful tool is LINQ to Twitter, providing OAuth implementation as well as a full API to the Twitter REST services. Thank you, JoeMayo!

The only thing we still have to do in order for TwitterMatic OAuth authentication to work, is create an AccountController in the web role project. Let’s start with a Login action method:

[code:c#]

IOAuthTwitter oAuthTwitter = new OAuthTwitter();
oAuthTwitter.OAuthConsumerKey = configuration.ReadSetting("OAuthConsumerKey");
oAuthTwitter.OAuthConsumerSecret = configuration.ReadSetting("OAuthConsumerSecret");

if (string.IsNullOrEmpty(oauth_token)) {
    // Not authorized. Redirect to Twitter!
    string loginUrl = oAuthTwitter.AuthorizationLinkGet(
        configuration.ReadSetting("OAuthRequestTokenUrl"),
        configuration.ReadSetting("OAuthAuthorizeTokenUrl"),
        false,
        true
    );
    return Redirect(loginUrl);
}

[/code]

Our users will now be redirected to Twitter in order to authenticate, if the method receives an empty or invalid oauth-token. If we however do retrieve a valid token, we’ll use FormsAuthentication cookies to keep the user logged in on TwitterMatic as well. Note that we are also saving the authentication token as the user’s password, we’ll be needing this same token to post updates afterwards.

[code:c#]

// Should be authorized. Get the access token and secret.
string userId = "";
string screenName = "";

oAuthTwitter.AccessTokenGet(oauth_token, configuration.ReadSetting("OAuthAccessTokenUrl"),
    out screenName, out userId);
if (oAuthTwitter.OAuthTokenSecret.Length > 0)
{
    // Store the user in membership
    MembershipUser user = Membership.GetUser(screenName);
    if (user == null)
    {
        MembershipCreateStatus status = MembershipCreateStatus.Success;
        user = Membership.CreateUser(
            screenName,
            oAuthTwitter.OAuthToken + ";" + oAuthTwitter.OAuthTokenSecret,
            screenName, 
            "twitter", 
            "matic",
            true,
            out status);
    }

    // Change user's password
    user.ChangePassword(
        user.GetPassword("matic"),
        oAuthTwitter.OAuthToken + ";" + oAuthTwitter.OAuthTokenSecret
    );
    Membership.UpdateUser(user);

    // All is well!
    FormsAuthentication.SetAuthCookie(screenName, true);
    return RedirectToAction("Index", "Home");
}
else
{
    // Not OK...
    return RedirectToAction("Login");
}

[/code]

Here’s the full code to AccountController:

[code:c#]

[HandleError]
public class AccountController : Controller
{
    protected IConfigurationProvider configuration;

    public ActionResult Login(string oauth_token)
    {
        IOAuthTwitter oAuthTwitter = new OAuthTwitter();
        oAuthTwitter.OAuthConsumerKey = configuration.ReadSetting("OAuthConsumerKey");
        oAuthTwitter.OAuthConsumerSecret = configuration.ReadSetting("OAuthConsumerSecret");

        if (string.IsNullOrEmpty(oauth_token)) {
            // Not authorized. Redirect to Twitter!
            string loginUrl = oAuthTwitter.AuthorizationLinkGet(
                configuration.ReadSetting("OAuthRequestTokenUrl"),
                configuration.ReadSetting("OAuthAuthorizeTokenUrl"),
                false,
                true
            );
            return Redirect(loginUrl);
        } else {
            // Should be authorized. Get the access token and secret.
            string userId = "";
            string screenName = "";

            oAuthTwitter.AccessTokenGet(oauth_token, configuration.ReadSetting("OAuthAccessTokenUrl"),
                out screenName, out userId);
            if (oAuthTwitter.OAuthTokenSecret.Length > 0)
            {
                // Store the user in membership
                MembershipUser user = Membership.GetUser(screenName);
                if (user == null)
                {
                    MembershipCreateStatus status = MembershipCreateStatus.Success;
                    user = Membership.CreateUser(
                        screenName,
                        oAuthTwitter.OAuthToken + ";" + oAuthTwitter.OAuthTokenSecret,
                        screenName,
                        configuration.ReadSetting("OAuthConsumerKey"),
                        configuration.ReadSetting("OAuthConsumerSecret"),
                        true,
                        out status);
                }

                // Change user's password
                user.ChangePassword(
                    user.GetPassword(configuration.ReadSetting("OAuthConsumerSecret")),
                    oAuthTwitter.OAuthToken + ";" + oAuthTwitter.OAuthTokenSecret
                );
                Membership.UpdateUser(user);

                // All is well!
                FormsAuthentication.SetAuthCookie(screenName, true);
                return RedirectToAction("Index", "Home");
            }
            else
            {
                // Not OK...
                return RedirectToAction("Login");
            }
        }
    }

    public ActionResult Logout()
    {
        FormsAuthentication.SignOut();
        return RedirectToAction("Index", "Home");
    }
}

[/code]

Using ASP.NET provider model

The ASP.NET provider model provides abstractions for features like membership, roles, sessions, … Since we’ll be using membership to store authenticated users, we’ll need a provider that works with Windows Azure Table Storage. The Windows Azure SDK samples contain a project '”AspProviders”. Reference it, and add the following to your web.config:

[code:xml]

<?xml version="1.0"?>
<configuration>
  <!-- ... -->

  <appSettings>
    <add key="DefaultMembershipTableName" value="Membership" />
    <add key="DefaultRoleTableName" value="Roles" />
    <add key="DefaultSessionTableName" value="Sessions" />
    <add key="DefaultProviderApplicationName" value="TwitterMatic" />
    <add key="DefaultProfileContainerName" />
    <add key="DefaultSessionContainerName" />
  </appSettings>

  <connectionStrings />

  <system.web>
    <!-- ... -->

    <authentication mode="Forms">
      <forms loginUrl="~/Account/Login" />
    </authentication>

    <membership defaultProvider="TableStorageMembershipProvider">
      <providers>
        <clear/>
        <add name="TableStorageMembershipProvider"
             type="Microsoft.Samples.ServiceHosting.AspProviders.TableStorageMembershipProvider"
             description="Membership provider using table storage"
             applicationName="TwitterMatic"
             enablePasswordRetrieval="true"
             enablePasswordReset="true"
             requiresQuestionAndAnswer="true"
             minRequiredPasswordLength="1"
             minRequiredNonalphanumericCharacters="0"
             requiresUniqueEmail="false"
             passwordFormat="Clear" />
      </providers>
    </membership>

    <profile enabled="false" />

    <roleManager enabled="true" defaultProvider="TableStorageRoleProvider" cacheRolesInCookie="false">
      <providers>
        <clear/>
        <add name="TableStorageRoleProvider"
             type="Microsoft.Samples.ServiceHosting.AspProviders.TableStorageRoleProvider"
             description="Role provider using table storage"
             applicationName="TwitterMatic" />
      </providers>
    </roleManager>

    <sessionState mode="Custom" customProvider="TableStorageSessionStateProvider">
      <providers>
        <clear />
        <add name="TableStorageSessionStateProvider"
             type="Microsoft.Samples.ServiceHosting.AspProviders.TableStorageSessionStateProvider"
             applicationName="TwitterMatic" />
      </providers>
    </sessionState>

    <!-- ... -->
  </system.web>

  <!-- ... -->
</configuration>

[/code]

TwitterMatic should now be storing sessions (if we were to use them), membership and roles in the cloud, by just doing some configuration magic. I love ASP.NET for this!

Conclusion

We now know how to leverage third-party authentication (OAuth in our case) and have implemented this in TwitterMatic.

In the next part of this series, we’ll have a look at the ASP.NET MVC front end and how we can validate user input before storing it in our database.

kick it on DotNetKicks.com