Основы работы с сервером livejournal.com
Дата публикации: 14.10.2007
Дата последней правки: 09.02.2023
Содержание
Введение
Общие сведения
Работа с сервером
Авторизация
Отправка сообщений в блог
Работа по протоколу XML-RPC
Ссылки
Введение
Эта статья рассказывает про основы взаимодействия с сервером livejournal.com, также известный как Живой Журнал. Большая часть статьи представляет собой описание методов авторизации разными способами, а в конце приводится пример того, как программно отправлять сообщения в блог.
Для статьи была написана тестовая программа LJServerTest, которая производит авторизацию различными способами и может отправлять тестовую строку в блог. Программа была написана на языке C# под платформу .NET Framework 2.0. Все дальнейшие примеры также демонстрируются участками кода на C#. Исходники программы можно скачать отсюда.
Общие сведения
Вся документация по работе с сервером Живого Журнала (далее ЖЖ) располагается по адресу https://stat.livejournal.com/doc/server/index.html, но для наших целей достаточно ограничиться разделом Client/Server Protocol, в котором и описываются принципы взаимодействия с сервером.
Для передачи данных на сервер ЖЖ используется протокол HTTP с методом передачи POST. Работа с сервером возможна по двум внутренним протоколам: в виде так называемого плоского (Flat) протокола и в виде XML-RPC. Страница документации по работе по плоскому протоколу расположена здесь, а в формате XML-RPC здесь. Отличия этих протоколов в том, что в первом случае запрос задается в виде:
mode=function&параметр1=значение1&параметр2=значение2&...&параметрN=значениеN
где mode является обязательным параметром, значением которого определяет функцию сервера ЖЖ, которую мы хотим вызвать. Возможные функции для протокола Flat перечислены на этой странице. За параметром mode задаются параметры, которые зависят от вызываемой функции. Ответ представляет собой чередующиеся строки: первая строка - параметр, вторая - значение, третья - параметр, четвертая - значение и т.д.
В формате XML-RPC и запрос, и ответ представляются в формате XML. Большую часть статьи мы будем использовать плоский протокол, а в конце кратко рассмотрим один пример, с протоколом XML-RPC.
Для этой статьи я написал небольшую программку, которая использует некоторые функции сервера ЖЖ, которые мы рассмотрим в статье. Скачать ее исходники можно отсюда. Скриншот главного окна программы показан на рисунке 1. Программа написана на языке C# под платформу .NET Framework 2.0.
Рисунок 1. Главное окно программы LJServer.
На рисунке 2 Изображена UML-диаграмма классов, отвечающих за работу с сервером в тестовой программе. Базовым является абстрактный класс LJServer, в котором собраны все действия, не зависящие от протокола обмена данными (Flat или XML-RPC). От него производятся два класса: FlatLJServer для работы по протоколу Flat и XmlLJServer для работы по протоколу XML-RPC. По сути класс LJServer выполняет всю сетевую работу, а производные классы только подготавливают необходимые запросы. Так как это демонстрационная программа, то выполнение запросов и получение ответов происходит синхронно в главном потоке программы. Поэтому на это время программа "подвисает". В реальных условиях, конечно, эти действия надо выполнять асинхронно или выделять им отдельный поток. Также здесь минимальное количество проверок на ошибки.
Рисунок 2. UML-диаграмма классов, отвечающих за работу с сервером ЖЖ.
Обратите внимание на абстрактное свойство базового класса ServerUri. Через это свойство производный класс определяет адрес, куда следует слать запросы. Этот адрес для протоколов Flat и XML-RPC разный. В случае плоского протокола он должен быть http://www.livejournal.com/interface/flat, а в случае работы по протоколу XML-RPC - http://www.livejournal.com/interface/xmlrpc.
Работа с сервером
Давайте сначала рассмотрим то, каким образом мы будем отправлять запросы на сервер. Для этого в классе LJServer реализована функция SendRequest, принимающая текстовую строку с телом запроса и возвращающая текст ответа. Пояснения работы этой функции даны в комментариях в следующем участке кода. Отметим только, что здесь для простоты нет никаких проверок на ошибки соединения и т.п. В реальной программе этого, разумеется, допускать нельзя. Итак, исходник функции SendRequest:
{
// Выводим в лог посылаемый запрос
_log.WriteLine ("\r\n*** Request:");
_log.WriteLine (textRequest);
// Преобразуем запрос из строки в byte[]
byte[] byteArray = Encoding.UTF8.GetBytes (textRequest);
// Поулчаем класс запроса
HttpWebRequest request = (HttpWebRequest)WebRequest.Create (ServerUri);
// Заполняем параметры запроса
request.Credentials = CredentialCache.DefaultCredentials;
request.Method = "POST";
request.ContentLength = textRequest.Length;
request.ContentType = this.ContentType;
// Очищаем коллекцию от старых cookie и добавляем туда новые
request.CookieContainer = new CookieContainer ();
request.CookieContainer.Add (_cookies);
// Заполняем параметры Proxy (_proxy == null, если прокси не используется)
request.Proxy = _proxy;
// Если есть cookie с именем "ljsession", то для авторизации с ее помощью
// необходимо добавить заголовок с именем "X-LJ-Auth" и значением "cookie"
if (_cookies["ljsession"] != null)
{
request.Headers.Add ("X-LJ-Auth", "cookie");
}
// Отправляем данные запроса
Stream requestStream = request.GetRequestStream ();
requestStream.Write (byteArray, 0, textRequest.Length);
// Получаем класс ответа
HttpWebResponse response = (HttpWebResponse)request.GetResponse ();
// Читаем ответ
Stream responseStream = response.GetResponseStream ();
StreamReader readStream = new StreamReader (responseStream, Encoding.UTF8);
string currResponse = readStream.ReadToEnd ();
// Выводим в лог ответ сервера
_log.WriteLine ("\r\n*** Response:");
_log.WriteLine (currResponse);
// Выведем в лог полученные в ответе cookie
_log.WriteLine ("\r\n*** Cookies:");
for (int i = 0; i < response.Cookies.Count; i++)
{
_log.WriteLine (response.Cookies[i].ToString());
}
readStream.Close ();
response.Close ();
return currResponse;
}
Так как взаимодействие с сервером, как уже говорилось, происходит по протоколу HTTP, то мы будем использовать классы HttpWebRequest и HttpWebResponse из библиотеки .NET Framework соответственно для отправки запроса и получения ответа.
Единственный момент, который в данный момент может быть непонятен, это участок кода, где создается заголовок с именем X-LJ-Auth. Для чего это делается, рассмотрим в разделе, посвященном авторизации с помощью cookie. Переменная _log отвечает за вывод текста в многострочный TextBox в главном окне программы. Переменная _proxy определяется также из главного окна программы. Она задает параметры прокси-сервера, или, если он не используется, этой переменной присваивается значение null.
Обратите внимание, что запрос происходит с помощью метода POST, хотя данные для протокола Flat будут напоминать параметры для метода GET. С помощью абстрактного свойства ContentType производные классы определяют каким должен быть заголовок ContentType запроса. Для протокола Flat здесь должно быть application/x-www-form-urlencoded, а для XML-RPC text/xml.
Авторизация
Важнейшей функцией сервера ЖЖ является авторизация пользователя. Без нее невозможно выполнить практически ни одного действия (есть только служебная функция getchallenge, которая не требует авторизации).
Авторизация пользователя может проходить тремя способами. Будем называть их так же английскими словами, как они называются в документации:
- Clear. В этом случае имя пользователя (логин) и пароль (или его хеш md5) передаются на сервер в незашифрованном виде. Этот способ является самым простым и в то же время самым незащищенным, поэтому его лучше не использовать. Но мы его все-равно рассмотрим.
- Challenge / Response. При этом способе авторизации клиент получает с сервера так называемый оклик (challenge), на который отвечает следующим запросом, в котором передает серверу имя пользователя и пароль в зашифрованном виде. Каким образом происходит шифрование мы также рассмотрим.
- Cookies. В данном случае клиент авторизуется на сервере любым из описанным выше способом, а в ответ получает cookie, которое и используется в дальнейшем для авторизации. Этот метод также будет рассмотрен.
Функция сервера login
Авторизацию использует практически каждая функция сервера, но мы в качестве примера будем использовать функцию login, документация по которой для протокола Flat расположена на этой странице.
Эта функция предназначена для получения от сервера некоторой информации о пользователе ЖЖ, но нам это в данный момент не интересно, эта функция будет использоваться только для демонстрации того, каким образом происходит авторизация.
Рассмотрим параметры этой функции. Так как эта статья не претендует на роль полной документации по серверу ЖЖ, то здесь мы рассмотрим только те параметры, которые будут использоваться в примерах, а некоторые необязательные параметры опустим. Их описание есть в документации.
Параметр | Описание |
---|---|
mode | Обязательный параметр, значение которого в данном случае должно быть login |
user | Обязательный параметр, обозначающий имя пользователя в ЖЖ |
auth_method | Обязательный параметр, обозначающий тип авторизации. Значение этого параметра может принимать следующие значения: clear для авторизации типа Clear challenge для авторизации типа challenge / response cookie для авторизации с помощью cookie |
password | Параметр, используемый при авторизации типа Clear в случае, если пароль передается в явном виде |
hpassword | Параметр, используемый при авторизации типа Clear в случае, если вместо пароля передается его хеш md5 |
auth_challenge | При использовании авторизации типа challenge / response этот параметр содержит значение оклика (challenge), полученного от сервера. Способ получения оклика будет рассмотрен в разделе Авторизация с помощью метода challenge / response. |
auth_response | При использовании авторизации типа challenge / response этот параметр содержит значение ответа для сервера, используемый для авторизации. Принцип формирования этого значения будет также рассмотрен в разделе Авторизация с помощью метода challenge / response. |
ver | Необязательный параметр, определяющий номер версии протокола. Этот параметр может принимать два значения: 0 (используется как значение по умолчанию, если параметр не задан) или 1. Различия в версиях протокола описаны на этой странице документации. В частности, версия 1 протокола требует, чтобы все данные передавались в кодировке UTF-8. |
Функция возвращает довольно большое число параметров, но нас пока будут интересовать только два:
Параметр | Описание |
---|---|
success | Принимает значение OK, если вызов функции прошел удачно и FAIL в противном случае |
errmsg | Сообщение об ошибке. Этот параметр возвращается только в том случает, если значение параметра success равно FAIL |
Clear-авторизация
Этот тип авторизации является самым простым, поэтому с него и начнем. Но следует помнить, что в реальных программах, работающих с сервером ЖЖ, эту авторизацию лучше не использовать из-за того, что в данном случае пароль от блога передается в явном виде, так что злоумышленники могут без проблем его перехватить. Даже хеш md5 в этом случае не поможет.
На самом деле этот тип авторизации можно разделить на два варианта:
- На сервер передается сам пароль.
- На сервер передается хеш md5 пароля.
Передача пароля в явном виде
Начнем с первого случая, когда пароль передается в явном виде, и заодно рассмотрим как формируется запрос по протоколу Flat.
Для Clear-авторизации с передачей пароля в явном виде надо заполнить следующие параметры:
Параметр | Значение |
---|---|
mode | login |
auth_method | clear |
user | Имя пользователя ЖЖ |
password | Пароль |
В программе LJServerTest запрос для этого типа авторизации формируется в методе LoginClear, который принимает два параметра: имя пользователя и пароль.
Запрос выполняется следующим образом:
{
string request = string.Format ("mode=login&auth_method=clear&user={0}&password={1}",
user, password);
this.SendRequest (request);
}
Как видно из этого участка кода, параметры разделяются с помощью символа '&', а значения присваиваются после знака '=', т.е. как при задании параметров способом GET через адресную строку браузера.
Давайте посмотрим, что нам ответит на такой запрос сервер. Для игр с сервером ЖЖ я создал отдельного пользователя - jenyay_test, на котором и буду демонстрировать все примеры. Итак, ответ сервера:
frgrp_1_name
Family
frgrp_1_sortorder
50
frgrp_2_name
Local Friends
frgrp_2_sortorder
50
frgrp_3_name
Online Friends
frgrp_3_sortorder
50
frgrp_4_name
School
frgrp_4_sortorder
50
frgrp_5_name
Work
frgrp_5_sortorder
50
frgrp_6_name
Mobile View
frgrp_6_sortorder
50
frgrp_maxnum
6
name
jenyay_test
success
OK
Я не буду полностью описывать все параметры, которые возвращает сервер ЖЖ, все они описаны в документации. В данный момент нас будет интересовать только параметр success, который определяет удалась ли нам авторизация. Как видите, в данном случае сервер вернул значение success как OK, т.е. пароль правильный.
А вот как выглядел бы ответ, если бы мы ошиблись при вводе пароля:
errmsg
Invalid password
success
FAIL
Как видите, параметр success равняется значению FAIL, а в параметре errmsg описана ошибка, в данном случае Invalid password.
Возвращаемые сервером параметры при прочих видах авторизации не будут отличаться от того, что было описано здесь, поэтому далее мы не будем на них акцентировать внимание при вызове функции login.
Передача MD5-хеша пароля
Второй, формально чуть более защищенный способ авторизации состоит в том, чтобы передавать на сервер не сам пароль, а его хеш MD5. Запрос в этом случае выглядит практически так же, как и в предыдущем случае за тем исключением, что параметр password не используется, а используется hpassword, значение которого и будет хешем MD5.
В программе LJServerTest за этот тип авторизации отвечает метод LoginClearMD5 класса LJServer. Вот как он выглядит:
{
string request = string.Format ("mode=login&auth_method=clear&user={0}&hpassword={1}",
user, ComputeMD5 (password) );
this.SendRequest (request);
}
Как видите, ничего сложного. Метод ComputeMD5 я взял из исходников OpenSource-программы LJ.NET, которая, кстати, очень помогла в изучении работы сервера ЖЖ. Вот как выглядит этот метод:
/// Посчитать md5. Взято у lj.net. http://lj-net.cvs.sourceforge.net/lj-net/lj-net/Utils.cs?view=markup
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
protected string ComputeMD5(string text)
{
MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider ();
byte[] hashBytes = md5.ComputeHash( Encoding.UTF8.GetBytes( text ) );
StringBuilder sb = new StringBuilder();
foreach( byte hashByte in hashBytes )
sb.Append( Convert.ToString( hashByte, 16 ).PadLeft( 2, '0' ) );
return sb.ToString();
}
Авторизация с помощью метода challenge / response
Этот тип авторизации является более защищенным, чем предыдущий. Авторизация методом challenge / response состоит из нескольких этапов:
1. Запрос с сервера так называемого оклика (challenge).
2. Используя полученный оклик, формируется ответ, при расчете которого используется пароль.
3. Происходит авторизация, но вместо пароля передаются исходный оклик, полученный на первом этапе, и ответ, сформированный на предыдущем этапе.
То есть через сеть пароль не передается ни в явном виде, ни в виде его MD5-хеша. Давайте рассмотрим эти этапы авторизации поподробнее.
Запрос оклика с сервера
Так называемый оклик (challenge) представляет собой случайную величину, используя которую шифруется пароль. Для получения оклика на сервере существует специальная функция getchallenge, которая не ожидает никаких параметров, поэтому весь запрос представляет собой просто строку
mode=getchallenge
Ответ сервера на такой запрос будет примерно таким:
auth_scheme
c0
challenge
c0:1192269600:2533:60:8TSdW2fFDrYhQeWM9dpl:10b1d9a13f0e61b5f084c844f74612a2
expire_time
1192272193
server_time
1192272133
success
OK
При этом функция возвращает следующие параметры:
Параметр | Описание |
---|---|
success | Так же как и в предыдущих случаях обозначает удачно ли прошел запрос на сервер. Принимает значения OK или FAIL |
challenge | Собственно, сам оклик |
auth_scheme | Версия схемы идентификации. Этот параметр можно пока игнорировать |
server_time | Время на сервере, когда был сформирован оклик, измеряемое в секундах в формате UNIX-времени |
expire_time | Время окончания действия полученного оклика, измеряемое в секундах в формате UNIX-времени |
Как видим, оклик представляет собой строку вида c0:1192269600:2533:60:8TSdW2fFDrYhQeWM9dpl:10b1d9a13f0e61b5f084c844f74612a2. Каким образом она формируется в данный момент не важно. Главное, что на ее основе мы будем производить дальнейшие действия. Запрос оклика в программе LJServerTest выглядит следующим образом:
{
string request = "mode=getchallenge";
string challengeResponse = SendRequest (request);
StringDictionary dict = MakeDict (challengeResponse);
if (dict["success"] == "OK")
{
return dict["challenge"];
}
return "";
}
Здесь метод MakeDict является вспомогательный для формирования словаря типа StringDictionary из ответа сервера, в котором ключом является имя параметра, а значением, соответственно, значение полученного параметра. Метод GetChallenge посылает запрос на получение оклика и, если вызов произошел удачно, возвращает значение полученного оклика.
Формирование ответа
Теперь, когда мы получили оклик, мы можем сформировать ответ, используя который и будет происходить дальнейшая авторизация. Ответ вычисляется по следующей формуле:
MD5 (challenge + MD5(password))
Т.е. вычисляем хеш MD5 пароля, слева добавляем строку отклика и из всего этого вычисляем еще раз MD5-хеш. Буквы в полученном хеше должны быть представлены в нижнем регистре. Именно это значение и будет в дальнейшем передаваться на сервер. На языке C# это будет выглядеть примерно следующим образом:
{
// md5 от пароля
string hpass = ComputeMD5 (password);
string constr = challenge + hpass;
string auth_response = ComputeMD5 (constr);
return auth_response;
}
Авторизация
Теперь нам остается только вызвать нужную функцию (в нашем случае login), которая будет использовать авторизацию типа challenge / response. При таком типе авторизации не используются параметры password и hpassword, а используются auth_challenge, значение которого должно равняться значению оклика, полученного от сервера, и auth_response - параметр, который задает ответ серверу на оклик. При этом значение параметра auth_method должно равняться challenge.
В программе LJServerTest за авторизацию типа challenge/response отвечает метод LoginChallenge класса LJServer, принимающий в качестве параметров имя пользователя и пароль:
{
string challenge = GetChallenge ();
string auth_response = GetAuthResponse (password, challenge);
string request = string.Format ("mode=login&auth_method=challenge&auth_challenge={0}&auth_response={1}&user={2}", challenge, auth_response, username);
SendRequest (request);
}
Полную последовательность запросов и ответов при таком типе авторизации можно увидеть в логе программы при нажатии на кнопку "Авторизоваться (Challenge / Response)".
Авторизация с помощью cookie
А теперь рассмотрим последний из возможных способов авторизации - с помощью cookie. В программе LJServer для этого типа авторизации предусмотрен метод LoginCookies. Суть этого метода состоит в том, что мы авторизуемся один раз любым описанным выше способом, но при этом создаем cookie, которое и будет использоваться для дальнейших авторизаций.
В качестве значения cookie, должна быть так называемая сессия (ljsessoin), которую можно получить, вызвав функцию сервера sessiongenerate, описание которой находится на этой странице документации. Входные параметры ее напоминают параметры уже используемой нами функции login, поэтому повторно описывать их не стоит. Также эта функция имеет еще несколько необязательных параметров, которые в данный момент нас также не интересуют. В результате работы этой функции мы получаем ljsessoin (в качестве одноименного возвращаемого значения).
В нашей тестовой программе для генерации сессии предназначен метод SessionGenerate из класса LJServer. В данном случае для получения ljsessoin используется метод авторизации challenge / response. Здесь нет ничего принципиально нового, поэтому просто приведу код этой функции:
{
string challenge = GetChallenge ();
string auth_response = GetAuthResponse (password, challenge);
string request = string.Format ("mode=sessiongenerate&auth_method=challenge&auth_challenge={0}&auth_response={1}&user={2}&expiration=long",
challenge,
auth_response,
login);
string challengeResponse = SendRequest (request);
StringDictionary dict = MakeDict (challengeResponse);
if (dict["success"] != "OK")
{
return "";
}
return dict["ljsession"];
}
Когда сессия получена, необходимо проделать следующие операции для авторизации:
1. Создать cookie с этим значением и именем ljsession. В качестве хоста для cookie необходимо указать "livejournal.com", а в качестве пути "/".
2. В запросе в качестве параметра auth_method использовать значение cookie.
3. Добавить созданную cookie в запрос перед отправкой на сервер.
4. Добавить новый заголовок для запроса. Заголовок должен называться "X-LJ-Auth", а значением его должно быть слово "cookie".
В программе LJServerTest авторизацию с помощью cookie выполняем метод LoginCookies из класса LJServer:
{
string ljsession = SessionGenerate (login, password);
Cookie cookie = new Cookie ("ljsession", ljsession, "/", "livejournal.com");
_cookies = new CookieCollection ();
_cookies.Add (cookie);
string request = string.Format ("mode=login&auth_method=cookie&user={0}", login);
SendRequest (request);
}
Напомню лишь, что переменная _cookies имеет тип CookieCollection и хранит в себе cookies, которые добавляются в запрос перед отправкой. Как видите, после создания cookie строка запроса становится намного короче и нагляднее, чем при других типах авторизации.
Ну что ж, мы рассмотрели с вами три типа авторизации. Каким из них пользоваться решать вам. Скажу только, что Semagic для отправки сообщений использует метод challenge / response, а LJ.NET - cookie.
Отправка сообщений в блог
Итак, мы с вами научились проходить авторизацию на сервере Живого Журнала. Для демонстрации этого мы использовали функцию сервера login, через которую можно получить некоторые сведения о пользователе. Давайте применим полученные знания для еще более интересной задачи - отправки сообщений в свой блог. Для этого предназначена функция postevent. Давайте рассмотрим ее параметры. Я не буду здесь перечислять те параметры, которые используются для авторизации - они те же самое, что и в других функциях. Также я не буду описывать необязательные поля (некоторые из них мы рассмотрим ниже отдельно).
Параметр | Описание |
---|---|
event | Сам текст сообщения, который хотим отправить в блог |
lineendings | Определяет тип перевода строки в тексте. Здесь может быть \r\n, используемый в Windows, \n как в Unix или \r как в Mac. Как написано в документации, этот параметр в основном нужен тем, кто сидит на Маках, остальные могут пропустить этот параметр, но на сервере сообщения хранятся с переносами строк как в Unix |
subject | Заголовок для сообщения, что на жаргоне обозначают словом "сабж" |
year mon day hour day | Обозначают дату и время (соответственно год, месяц, день, час и минуту), за которое будет датироваться новая запись |
prop_* | Необязательные параметры, которые обозначают текущую музыку пользователя, его настроение и т.п. Все эти параметры описаны на этой странице документации. |
Вызов этой функции ничем не отличается от вызовов других функций, но надо помнить, что все поля, которые могут содержать русские буквы и другие нелатинские символы или цифры, необходимо предварительно url-закодировать. В библиотеке .NET Framework для этого предназначена функция HttpUtility.UrlEncode(). Если забыть сделать url-кодирование, то сервер возвращает ошибку, но вот описание ошибки в возвращенном параметре errmsg будет скорее всего указывать на неправильное значение совсем другого параметра.
Прежде чем рассматривать код для отправки сообщений давайте сначала кратко рассмотрим дополнительные параметры, имена которых начинаются с prop_. Полный список этих параметров описан на этой странице документации, но мы рассмотрим только один из них, чтобы понять в целом как их использовать. Рассматриваемым параметром будет current_music, который определяет музыку, которую слушает в данный момент автор сообщения. Таким образом, параметр, отвечающий за музыку, должен иметь имя prop_current_music. Значение параметра, разумеется, тоже должно быть url-закодировано.
В нашей тестовой программе в качестве музыки передается значение, которое определяет из какого класса был отправлен текст. Т.е. для протокола Flat это будет "FlatLJserver".
А теперь, собственно, сам код:
string user, string password)
{
string challenge = GetChallenge ();
string auth_response = GetAuthResponse (password, challenge);
DateTime date = DateTime.Now;
string request = string.Format ("mode=postevent&auth_method=challenge&auth_challenge={0}
&auth_response={1}&
user={2}&
event={3}&
subject={4}&
allowmask=0&
year={5}&mon={6}&day={7}&
hour={8}&min={9}&ver=1&
prop_current_music={10}&
ver=1",
challenge,
auth_response,
user,
HttpUtility.UrlEncode (text),
HttpUtility.UrlEncode (subj),
date.Year, date.Month, date.Day,
date.Hour, date.Minute,
HttpUtility.UrlEncode ("FlatLJserver"));
SendRequest (request);
}
Здесь, чтобы не делать слишком длинную строку на странице, часть кода, где формируется запрос я разбил на несколько строк. Для отправки сообщений здесь используется авторизация типа challenge / response. Для простоты программа LJServerTest отправляет все время один и тот же текст на сервер. Для Flat-протокола это "Этот пост отправлен из тестовой программы" с заголовком "Проверка". Сервер ЖЖ похоже проверяет, чтобы нельзя было отправить один и тот же текст несколько раз. Поэтому, если вы захотите повторно отправить текст из тестовой программы, то удалите предварительно сообщение, которое было отправлено программой до этого. Что интересно, при повторной отправке сообщения с таким же текстом сервер не сообщает об ошибке, а просто возвращает ссылку на уже существующую запись.
Кстати, раз уж мы заговорили о возвращаемых значениях, то тут мы натыкаемся на противоречия между тем, что написано в документации и что сервер возвращает на самом деле. Скорее всего после изменений в коде сервера забыли изменить документацию. В справке кроме стандартных возвращаемых значений success и errmsg обещают еще один параметр itemid - уникальный номер поста. В реальности есть еще несколько возвращаемых значений. Вот, например, как выглядит ответ, полученный мной только что:
anum
187
itemid
41
success
OK
url
http://jenyay-test.livejournal.com/10683.html
Описание возвращаемого значения anum нашлось в описании функции postevent для протокола XML-RPC (https://stat.livejournal.com/doc/server/ljp.csp.xml-rpc.postevent.html). Правда описание этого параметра довольно скромное. Из него можно узнать, что это число используется для вычисления другого возвращаемого значения - itemid. Каким образом это происходит остается загадкой. Возможно, это описано где-то в другом месте документации, но мне этого найти не удалось.
Еще одним возвращаемым значением является адрес нового сообщения. Этот параметр, тоже не описан в документации, но тут и так все понятно. Кстати, интересно каким образом рассчитывается число для адреса html-страницы нового сообщения.
Работа по протоколу XML-RPC
Под конец давайте рассмотрим какой-нибудь пример авторизации с использованием протокола XML-RPC. По сути здесь не будет ничего нового, просто по-другому формируются запросы и ответы (в виде XML). Отмечу только, что значение заголовка Content-Type запроса должно быть text/xml.
Давайте для простоты рассмотрим Clear-авторизацию с передачей MD5-хеша пароля. Запрос в этом случае выглядит следующим образом:
<methodCall>
<methodName>LJ.XMLRPC.login</methodName>
<params>
<param>
<value><struct>
<member><name>username</name>
<value><string>jenyay_test</string></value>
</member>
<member><name>hpassword</name>
<value><string>XXXXXXXXX</string></value>
</member>
<member><name>ver</name>
<value><int>1</int></value>
</member>
</struct></value>
</param>
</params>
</methodCall>
Обратите внимание, что при работе по такому протоколу переменные имеют разные типы. Например, имя пользователя является строкой и задается следующим образом:
<value><string>jenyay_test</string></value>
</member>
Как видите, параметр заключен внутрь тега member. Имя параметра окружено тегом name, а его значение - тегом value, внутрь которого вставлен тег, описывающий тип параметра, в нашем случае string.
Для примера, вот как выглядит целочисленный параметр:
<value><int>1</int></value>
</member>
В таком же виде приходит и ответ сервера. Для экономии места я не буду приводить здесь ответ сервера на предыдущий запрос. При желании вы можете его увидеть, самостоятельно выполнив запрос в тестовой программе. Не забудьте только переключить протокол XML-RPC с помощью соответствующего ComboBox-а. Напомню, что за работу с сервером ЖЖ в формате XML-RPC в тестовой программе отвечает класс XmlLJServer, производный от LJServer.
Тестовая программа может выполнять все те операции, которые мы рассмотрели в этой статье и по протоколу XML-RPC. Есть только один момент, который хотелось бы отметить. Мне так и не удалось отправить в журнал текст на русском языке, скорее всего я что-то делал не так. Кодировка при передаче данных должна быть UTF-8.
Еще хотелось бы предупредить, что в примере из документации к функции getchallenge есть одна неточность. Так как функция не принимает никаких параметров, то не надо в запросе писать "<param></param>". Правильный запрос должен выглядеть следующим образом:
<methodCall>
<methodName>LJ.XMLRPC.getchallenge</methodName>
<params>
</params>
</methodCall>
Ссылки
Следующие статьи из серии про про работу с сервером ЖЖ:
- Еще раз про авторизацию на сервере livejournal.com
- Программная отправка комментариев в livejournal
- Реализация бота для отправки комментариев в Живой Журнал на языке Python
- Общая документация по серверу ЖЖ.
- LJ.NET - Клиент для ЖЖ с открытыми исходниками на C#. Очень помогал мне разбираться в протоколе. Исходники написаны довольно понятно.
- Semagic - Распространенный клиент, также с открытыми исходниками, но на C++. Его также можно использовать, чтобы проследить обмен данными между клиентом и сервером.
- Proxomitron - В принципе это довольно неплохая банерорезалка, но она попала в этот список благодаря тому, что может показывать данные, которыми обменивается клиент и сервер. Для этого достаточно использовать Proxomitron как прокси-сервер.
- Официальный сайт протокола XML-RPC
- XML-RPC.NET - библиотека для работы с протоколом XML-RPC. Предназначена для .NET Framework.
Вы можете подписаться на новости сайта через RSS, Группу Вконтакте или Канал в Telegram.