Мы создали слой аутентификации и основной пользовательский интерфейс. Если вы хотите перейти к тому, что было сделано до сих пор, используйте любую из ссылок ниже:
- Создание клиента Twitter для Windows Phone — аутентификация по ПИН
- Создание клиента Twitter для Windows Phone — сохранение данных авторизации и проверка того, что пользователя запомнили
- Создание клиента 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 здесь .
Не стесняйтесь скачать и играть с ним.