Основы работы с сервером livejournal.com

Немного рекламы


Введение

Эта статья рассказывает про основы взаимодействия с сервером livejournal.com, также известный как Живой Журнал. Большая часть статьи представляет собой описание методов авторизации разными способами, а в конце приводится пример того, как программно отправлять сообщения в блог.

Для статьи была написана тестовая программа LJServerTest, которая производит авторизацию различными способами и может отправлять тестовую строку в блог. Программа была написана на языке C# под платформу .NET Framework 2.0. Все дальнейшие примеры также демонстрируются участками кода на C#. Исходники программы можно скачать отсюда.

Общие сведения

Вся документация по работе с сервером Живого Журнала (далее ЖЖ) располагается по адресу http://www.livejournal.com/doc/server/, но для наших целей достаточно ограничиться разделом 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.


ljserver.png: 598x595, 15k (31.05.2012 10:04)
Рисунок 1. Главное окно программы LJServer.

На рисунке 2 Изображена UML-диаграмма классов, отвечающих за работу с сервером в тестовой программе. Базовым является абстрактный класс LJServer, в котором собраны все действия, не зависящие от протокола обмена данными (Flat или XML-RPC). От него производятся два класса: FlatLJServer для работы по протоколу Flat и XmlLJServer для работы по протоколу XML-RPC. По сути класс LJServer выполняет всю сетевую работу, а производные классы только подготавливают необходимые запросы. Так как это демонстрационная программа, то выполнение запросов и получение ответов происходит синхронно в главном потоке программы. Поэтому на это время программа "подвисает". В реальных условиях, конечно, эти действия надо выполнять асинхронно или выделять им отдельный поток. Также здесь минимальное количество проверок на ошибки.


ljserveruml.png: 411x772, 19k (31.05.2012 10:04)
Рисунок 2. UML-диаграмма классов, отвечающих за работу с сервером ЖЖ.

Обратите внимание на абстрактное свойство базового класса ServerUri. Через это свойство производный класс определяет адрес, куда следует слать запросы. Этот адрес для протоколов Flat и XML-RPC разный. В случае плоского протокола он должен быть http://www.livejournal.com/interface/flat, а в случае работы по протоколу XML-RPC - http://www.livejournal.com/interface/xmlrpc.

Работа с сервером

Давайте сначала рассмотрим то, каким образом мы будем отправлять запросы на сервер. Для этого в классе LJServer реализована функция SendRequest, принимающая текстовую строку с телом запроса и возвращающая текст ответа. Пояснения работы этой функции даны в комментариях в следующем участке кода. Отметим только, что здесь для простоты нет никаких проверок на ошибки соединения и т.п. В реальной программе этого, разумеется, допускать нельзя. Итак, исходник функции SendRequest:

protected string SendRequest (string textRequest)
{
        // Выводим в лог посылаемый запрос
        _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-авторизации с передачей пароля в явном виде надо заполнить следующие параметры:

ПараметрЗначение
modelogin
auth_methodclear
userИмя пользователя ЖЖ
passwordПароль

В программе LJServerTest запрос для этого типа авторизации формируется в методе LoginClear, который принимает два параметра: имя пользователя и пароль.

Запрос выполняется следующим образом:

public override void LoginClear (string user, string password)
{
        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. Вот как он выглядит:

public override void LoginClearMD5 (string user, string password)
{
        string request = string.Format ("mode=login&auth_method=clear&user={0}&hpassword={1}",
                user, ComputeMD5 (password) );
        this.SendRequest (request);
}

Как видите, ничего сложного. Метод ComputeMD5 я взял из исходников OpenSource-программы LJ.NET, которая, кстати, очень помогла в изучении работы сервера ЖЖ. Вот как выглядит этот метод:

/// <summary>
/// Посчитать 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 выглядит следующим образом:

public override string GetChallenge ()
{
        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# это будет выглядеть примерно следующим образом:

protected string GetAuthResponse (string password, string challenge)
{
        // 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, принимающий в качестве параметров имя пользователя и пароль:

public override void LoginChallenge (string username, string password)
{
        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. Здесь нет ничего принципиально нового, поэтому просто приведу код этой функции:

public override string SessionGenerate (string login, string password)
{
        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:

public override void LoginCookies (string login, string password)
{
        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".

А теперь, собственно, сам код:

public override void PostEventChallenge (string text, string subj,
        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 нашлось в описании функции postevent для протокола XML-RPC (http://www.livejournal.com/doc/server/ljp.csp.xml-rpc.postevent.html). Правда описание этого параметра довольно скромное. Из него можно узнать, что это число используется для вычисления другого возвращаемого значения - itemid. Каким образом это происходит остается загадкой. Возможно, это описано где-то в другом месте документации, но мне этого найти не удалось.

Еще одним возвращаемым значением является адрес нового сообщения. Этот параметр, тоже не описан в документации, но тут и так все понятно. Кстати, интересно каким образом рассчитывается число для адреса html-страницы нового сообщения.

Работа по протоколу XML-RPC

Под конец давайте рассмотрим какой-нибудь пример авторизации с использованием протокола XML-RPC. По сути здесь не будет ничего нового, просто по-другому формируются запросы и ответы (в виде XML). Отмечу только, что значение заголовка Content-Type запроса должно быть text/xml.

Давайте для простоты рассмотрим Clear-авторизацию с передачей MD5-хеша пароля. Запрос в этом случае выглядит следующим образом:

<?xml version='1.0'?>

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

Обратите внимание, что при работе по такому протоколу переменные имеют разные типы. Например, имя пользователя является строкой и задается следующим образом:

<member><name>username</name>
<value><string>jenyay_test</string></value>
</member>

Как видите, параметр заключен внутрь тега member. Имя параметра окружено тегом name, а его значение - тегом value, внутрь которого вставлен тег, описывающий тип параметра, в нашем случае string.

Для примера, вот как выглядит целочисленный параметр:

<member><name>ver</name>
<value><int>1</int></value>
</member>

В таком же виде приходит и ответ сервера. Для экономии места я не буду приводить здесь ответ сервера на предыдущий запрос. При желании вы можете его увидеть, самостоятельно выполнив запрос в тестовой программе. Не забудьте только переключить протокол XML-RPC с помощью соответствующего ComboBox-а. Напомню, что за работу с сервером ЖЖ в формате XML-RPC в тестовой программе отвечает класс XmlLJServer, производный от LJServer.

Тестовая программа может выполнять все те операции, которые мы рассмотрели в этой статье и по протоколу XML-RPC. Есть только один момент, который хотелось бы отметить. Мне так и не удалось отправить в журнал текст на русском языке, скорее всего я что-то делал не так. Кодировка при передаче данных должна быть UTF-8.

Еще хотелось бы предупредить, что в примере из документации к функции getchallenge есть одна неточность. Так как функция не принимает никаких параметров, то не надо в запросе писать "<param></param>". Правильный запрос должен выглядеть следующим образом:

<?xml version='1.0'?>
<methodCall>
<methodName>LJ.XMLRPC.getchallenge</methodName>
<params>
</params>
</methodCall>

Ссылки

Следующие статьи из серии про про работу с сервером ЖЖ:

Немного рекламы

Вы можете подписаться на новости сайта через RSS, Группу Вконтакте или Канал в Telegram.
5 stars

Рейтинг 4.8/5. Всего 12 голос(а, ов)



alcodream 07.12.2010 - 08:58

Кодировка

Делал отправку javascript'ом, пост на русском отправился только после encodeURIComponent(). Спасибо за статью.

alcodream 07.12.2010 - 09:02

А есть способ получить юзерпик дефолтный без парсинга?

Jenyay 07.12.2010 - 17:12

Если свой юзерпик, то по идее можно (сейчас по памяти не вспомню имя функции API), а если чужой юзерпик по нику, то придется парсить HTML.

beespace 18.02.2011 - 17:08

Спасибо за статью

+1

 20.02.2011 - 09:55

пытаясь залогиниться при помощи протокола Flat, сервер посылает следующее сообщение:
errmsg
Client error : No mode specified
succsess
FAIL

в чем может быть ошибка?


Подписаться на комментарии
Автор:
Тема:
 Ваш комментарий
 
 
Введите код 324
 

Антон 07.02.2009 - 21:32

Смысл anum

По ходу anum это число, которое генерится для получения нового номера записи. Запись в ЖЖ имеет вид user.livejournal.com/number.html так вот, если к номеру записи N пользователя прибавить anum следующей записи, то получится номер следующей записи.

Тоже Антон 05.04.2009 - 12:29

Про anum

номер записи вычисляется как itemid * 256 + anum. Можете проверить. Правда не знаю как этой информацией воспользоваться в практических целях и следовательно зачем она вообще нужна...

LostMan 23.11.2009 - 17:58

Спасибо большое.

Alex 20.04.2010 - 00:59

по протоколу XML-RPC

Прекрасно идет отправка постов в жж.

Alex 20.04.2010 - 01:03

по протоколу XML-RPC

Можно сразу получить сессионный кук
http://www.livejournal.com/doc/server/ljp.csp.xml-rpc.sessiongenerate.html