Статьи

Создание клиента Twitter для Windows Phone — отправка вашего первого твита

Мы создали слой аутентификации и основной пользовательский интерфейс. Если вы хотите перейти к тому, что было сделано до сих пор, используйте любую из ссылок ниже:

Первое, на что нужно обратить внимание при отправке твита, это требования к вызову метода API. Tweeting как таковой осуществляется через запрос POST статусов / обновлений . Это не ограниченный по скорости метод, поэтому вам не нужно беспокоиться о том, что Twitter отклонит ваш запрос на основании того, что пользователь слишком часто его называет. При этом метод требует аутентификации, и вам нужно будет предоставить сохраненные данные OAuth.

Давайте перейдем к кодированию. Первое, что я собираюсь здесь сказать, это то, что вам нужно правильно избежать статуса. Я не могу особо подчеркнуть, насколько важно избегать проблем, связанных с неправильно сформированной экранированной строкой состояния — это может быть серьезной проблемой отладки, поскольку Twitter API напрямую не сообщит вам причину проблемы. 

ПРИМЕЧАНИЕ. В случае неправильно сформированной экранированной строки состояния вы получите 401 Несанкционированный.

Вернемся к классу StringHelper и добавим дополнительный метод, который называется SanitizeStatus . Это обеспечит правильное экранирование специальных символов (к сожалению, Uri.EscapeDataString игнорирует некоторые из них из-за их незарезервированного статуса). На данный момент дополнительное экранирование должно быть сделано для :! () * и — по умолчанию все остальные символы, включая Юникод, экранированы правильно.

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;
}

ПОДСКАЗКА: При тестировании различных комбинаций символов, чтобы убедиться, что они экранированы правильно, попробуйте (любой ценой) избежать этого:

Большинство людей не будут знать, что ты чирикать. Кроме того, я не хочу нести ответственность за ваших потерянных подписчиков.

Теперь, когда метод готов, пришло время взглянуть на поток OAuth, который также претерпит некоторые незначительные изменения. Откройте OAuthClient класс и добавить еще одну запись в ТипЗапроса перечислении — StatusUpdate .

Теперь в методе PerformRequest добавьте необязательный строковый параметр — status , для которого по умолчанию установлено значение «» (пустая строка). Убедитесь, что вы проверили тип запроса внутри метода и, если он связан со статусом пользователя, установите для свойства ContentType значение application / x-www-form-urlencoded . Кроме того, вместо использования BeginGetResponse , используйте BeginGetRequestStream , так как состояние должно быть записано в поток запросов, прежде чем запрашивать ответ. У вас должно быть что-то вроде этого:

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 });
    }
}

Обратите внимание, что метод (POST) и заголовок авторизации остаются одинаковыми, поэтому здесь не нужно вносить никаких изменений, кроме тех, которые относятся к пропускаемым параметрам. Кроме того, обратите внимание, что в BeginGetRequestStream я передаю статус (неэкранированный) в качестве третьего объекта через асинхронное состояние.

Прежде чем я собираюсь посмотреть на метод SetStatusUpdate , давайте посмотрим, какие изменения должны быть сделаны внутри метода GetOAuthHeader . Поскольку я использую одну и ту же коллекцию параметров для генерации как составной строки подписи, так и заголовка OAuth, мне необходимо убедиться, что ненужные параметры не передаются в заголовок авторизации, для которого нужны только учетные данные OAuth. Для этого я создал отдельный статический класс ( BannedParameters ) со статическим массивом строк — Parameters .

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

Вернувшись в GetOAuthHeader, я сейчас выполняю проверку значения параметра:

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] + "\", ";
}

Когда я добавляю статус в строку подписи ( concat ), я вызываю SanitizeStatus, чтобы избежать проблем, вызванных специальными символами, которые не могут быть экранированы основными методами .NET. Заголовок OAuth будет игнорировать все строки в объявленном запрещенном массиве.

И последнее, но не менее важное, метод SetStatusUpdate:

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]) });
}

При создании тела POST я снова очищаю обновление статуса, которое нужно отправить (это не имеет никакого отношения к генерируемой мной строке подписи). Я отправляю данные и спрашиваю ответ — так просто. 

Когда мне нужно вызвать механизм твита, я просто создаю набор параметров и вызываю 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); 

И поскольку мы дошли до того, что становится очень трудно следовать, если у вас нет тестового исходного кода под рукой, я предлагаю вам текущее решение Visual Studio здесь .

Не стесняйтесь скачать и играть с ним.