Over a million developers have joined DZone.

Building a Twitter client for Windows Phone - Sending your first tweet

· Mobile Zone

We have built the authentication layer and the core UI. If you want to go over what was done so far, use any of the links below:

First thing you need to look at when sending a tweet are the API method invocation requirements. Tweeting as such is done through the statuses/update POST request. It is not a rate-limited method, therefore you don't have to worry about Twitter rejecting your request based on the fact that the user is calling it way too often. That being said, the method requires authentication and you will have to provide the stored OAuth data.

Let's get to coding. The first thing I am going to say here is that you need to properly escape the status. I cannot emphasize enough how important it is to avoid problems connected to a malformed escaped status string - it might be a major debugging problem since Twitter API won't directly tell you the cause of the problem. 

NOTE: In case of a malformed escaped status string, you will get a 401 Unauthorized.

Let's get back to the StringHelper class and add an additional method, called SanitizeStatus. It will make sure that special characters are properly escaped (unfortunately, Uri.EscapeDataString ignores some of them due to their unreserved status). For now, additional escaping should be done for: ! ( ) * and ' - every other character, including Unicode, is escaped properly by default.

public static string SanitizeStatus(string status)
{
    string blockedChars = @"!()*'";

    foreach (char c in blockedChars)
    {
        if (status.IndexOf(c) != -1)
        {
            status = status.Replace(c.ToString(), "%" + String.Format("{0:X}", Convert.ToInt32(c)));
        }
    }

    return status;
}

PROTIP: When testing various character combinations to make sure those are escaped properly, try (at all costs) avoiding this:

Most people will have no idea what you're tweeting. Besides, I don't want to be responsible for your lost followers.

Now that the method is ready, it's time to take a look at the OAuth flow, that will also go through some minor modifications. Open the OAuthClient class and add another entry in the RequestType enum - StatusUpdate.

Now, in the PerformRequest method, add an optional string parameter - status, having its default value set to "" (empty string). Make sure that you check the request type inside the method, and if it is related to the user status - set the ContentType property to application/x-www-form-urlencoded. Also, instead of using BeginGetResponse, use BeginGetRequestStream, since the status should be written to the request stream before asking for a response. You should have something like this:

public static void PerformRequest(Dictionary<string, string> parameters, string url, string consumerSecret, string token, RequestType type, string status = "")
{
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
    request.Method = "POST";
    string OAuthHeader = OAuthClient.GetOAuthHeader(parameters, "POST", url, consumerSecret, token);
    request.Headers["Authorization"] = OAuthHeader;
    
        
    if (type == RequestType.StatusUpdate)
    {
        request.ContentType = "application/x-www-form-urlencoded";
        request.BeginGetRequestStream(new AsyncCallback(SetStatusUpdate), new object[] { request, type, status });
    }
    else
    {
        request.BeginGetResponse(new AsyncCallback(GetResponse), new object[] { request, type });
    }
}

Notice that the method (POST) and the authorization header are still the same, so no modifications have to be introduced there, other than related to what parameters to skip. Also, notice that in the BeginGetRequestStream, I am passing the status (unescaped) as the third object through the async state.

Before I am going to look at the SetStatusUpdate method, let's see what modifications have to be done inside the GetOAuthHeader method. Because I am using the same collection of parameters to generate both the concatenated signature string and the OAuth header, I need to make sure that unnecessary parameters are not passed to the authorization header, which only needs the OAuth credentials. To do this, I created a separate static class (BannedParameters) with a static string array - Parameters.

public static class BannedParameters
{
    public static string[] Parameters = new string[] { "status" };
}

Back in GetOAuthHeader, I am now performing the parameter value check:

foreach (string k in parameters.Keys)
{
    if (k == "status")
        concat += k + "=" + StringHelper.SanitizeStatus(StringHelper.EncodeToUpper(parameters[k])) + "&";
    else
        concat += k + "=" + parameters[k] + "&";

    if (!BannedParameters.Parameters.Contains(k))
        OAuthHeader += k + "=" + "\"" + parameters[k] + "\", ";
}

When I am adding the status to the signature string (concat), I am making sure to call SanitizeStatus to avoid problems caused by special characters that cannot be escaped by core .NET methods. The OAuth header will ignore all strings in the declared banned array.

Last but not least, the SetStatusUpdate method:

static void SetStatusUpdate(IAsyncResult result)
{
    HttpWebRequest request = (HttpWebRequest)((object[])result.AsyncState)[0];
    string status = "status=" + StringHelper.SanitizeStatus(StringHelper.EncodeToUpper(((object[])result.AsyncState)[2].ToString()));
    byte[] data = Encoding.UTF8.GetBytes(status);

    using (Stream s = request.EndGetRequestStream(result))
    {
        s.Write(data, 0, data.Length);
    }
    
    request.BeginGetResponse(new AsyncCallback(GetResponse), new object[] { request, (RequestType)(((object[])result.AsyncState)[1]) });
}

When creating the POST body, I am once again sanitizing the status update to be sent (it has nothing to do with the signature string I am generating). I am sending the data, and asking for a response - as simple as that. 

When I need to invoke the tweeting mechanism, I simply build a set of parameters and invoke PerformRequest:

Dictionary<string, string> parameters = new Dictionary<string, string>();
parameters.Add("oauth_consumer_key", AuthConstants.ConsumerKey);
parameters.Add("oauth_signature_method", "HMAC-SHA1");
parameters.Add("oauth_timestamp", StringHelper.UNIXTimestamp);
parameters.Add("oauth_nonce", Guid.NewGuid().ToString().Replace("-", ""));
parameters.Add("oauth_version", "1.0");
parameters.Add("oauth_token", App.Token);
parameters.Add("status", txtStatus.Text);

OAuthClient.PerformRequest(parameters, "http://api.twitter.com/1/statuses/update.json", AuthConstants.ConsumerSecret, App.TokenSecret, OAuthClient.RequestType.StatusUpdate, txtStatus.Text); 

And since we got to the point where it gets really hard to follow if you don't have the test source code on your hand, I am offering you the current Visual Studio solution here.

Feel free to download and play with it.

Topics:

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}