Подписка

добавить на Яндекс

Наши проекты

Delphi+Google

Google API

Google API в Delphi - проект с открытым исходным кодом.

Chrono

Chrono

Хронометр - программа для ведения списка задач.

ODFProc

ODFProc

ODFProc - работа с документами OpenOffice в Lazarus и FreePascal.

Поддержка блога

А тут я коплю на лицензию Delphi XE на iPad =).
Сумма пожертвования не фиксирована.

Публикации

Год назад

Случайный пост

Последние

Сообщения форума

Комментарии

Социальные сети

Google

Facebook

Twitter

Опрос

Вы сейчас или в ближайшем обозримом будущем планируете разрабатывать кроссплатформенное приложение с использованием Firemonkey?



Loading ... Loading ...

Блоги и сообщества

Статьи по Delphi DelphiFeeds.ru - Все Delphi-блоги Рунета Сообщество умных людей VR-Online.RU Бесплатный журнал для программистов и всех, кто интересуется IT Статьи и уроки по Delphi Новостной блог о высоких технологиях
Система Orphus
Опубликовал Vlad 18 января 2010 в 18:00.
Категории: Delphi в Web.


Сегодня решил немного продолжить тему работы с XML-RPC в WordPress. Как это обычно со мной бывает, идея родилась в момент чтения случайного блога, вздумалось взглянуть на работу с постами в блоге и, заодно, попробовать написать что-нибудь под свои нужды.

Естественно программу я сегодня не выложу, но некоторые выкладки, листинги и идеи в посте будут присутствовать.

Вкратце работу с XML-RPC я рассматривал в посте "XML-RPC в Delphi. Первое знакомство с WordPress изнутри." Сегодня попробуем продвинуться дальше в своей работе и использовать несколько взаимосвязанных методов для получения определенной информации из блога.

Конкретизируем цель на сегодня: необходимо получить данные по постам в блоге, используя доступные методы из API WordPress.

Для достижения поставленной цели нам понадобятся следующие модули Delphi: XMLIntf, xmldomXMLDoc и библиотека synapce или компонент Indy idHTTP (кому как угодно).

1. Тестируем соединение с блогом.

Полагаю, что первое, что следует сделать - это проверить корректность работы с блогом на предмет следующих возможных ошибок:

  1. В блоге отключена возможность использования XML-RPC
  2. Пользователь предоставил некорректные данные (url, логин или пароль).

Для проверки возможности работы с XML-RPC в блоге достаточно воспользоваться методом demo.sayHello. Если ответом будет строка "Hello", значит всё в порядке и можно приступать к следующему шагу проверки. Для выполнения этой проверки нам потребуется выполнить три простенькие задачки:

  • сформировать правильный XML-документ
  • отправить запрос на сервер и получить ответ
  • проанализировать ответ

Формируем XML-документ, который должен выглядеть так:

<methodCall>
   <methodName>demo.sayHello</methodName>
     <params>
        <param>
           <value>
                <string>test</string>
           </value>
        </param>
     </params>
</methodCall>

Для этого воспользуемся интерфейсом IXMLDocument:

[...]
var doc: IXMLDocument; //документ
    Root: IXMLNode;    //корневой узел
begin
  inherited Create;
  doc:=NewXMLDocument();//создаем пустой документ
  Root:=Doc.CreateElement('methodCall','');//добавляем корневой узел
  Doc.DocumentElement:=Root;
  Root.AddChild('methodName').NodeValue:='demo.sayHello';//добавляем название метода
Root.AddChild('params').AddChild('param').AddChild('value').AddChild('string').NodeValue:='test';//записываем параметры метода
[...]

Так как сам по себе XML-документ достаточно простой, то я позволил себе немного "похалявить" и последней строкой кода записал сразу все узлы и значение единственного параметра для нашего метода.
Теперь можно отправить документ на сервер и получить ответ:

[...]
with THTTPSend.Create do
    begin
      Doc.SaveToStream(Document);//записываем документ в тело запроса
      if HTTPMethod('POST',aURL) then
        begin
          //запрос успешно отправлен и получен ответ
        end
     else
       begin
         //запрос не удался
       end;
    end;
[...]

Что мне нравится в Synapce, так это то, что не требуется лишних "телодвижений" в плане заполнения заголовков Content-Length, Content-Type и пр. Конечно никто не мешает заполнить все возможные заголовки самому, но можно обойтись и так, как показал я выше - всё на автомате.
Двигаемся дальше - проводим анализ ответа.
Позволю себе напомнить Вам, что удачная отправка запроса на сервер никак не свидетельствует о том, что мы успешно получили доступ к XML-RPC блога. Удачная отправка запроса свидетельствует только о том, что мы отправили запрос и получили ответ, а _что_ находится в ответе ошибка или нет - мы пока не знаем.
Чтобы пока не забивать голову лишними способами и методами парсинга ответа от сервера, предлагаю в данном случае остановиться на применении простой проверки:

[...]
Doc.LoadFromStream(Document,xetUTF_8);//записали XML-документ
if Doc.DocumentElement.ChildNodes.FindNode('fault')=nil then
   ShowMessage('XML-RPC работает исправно')
[...]

В соответствии со спецификацией XML-RPC сообщения об ошибках содержится в узле с названием fault. Следовательно, применительно к нашему случаю достаточно проверить наличие такого узла в ответном XML-документе - если его нет, то значит проверка прошла успешно, был сформирован корректный запрос и XML-RPC работает исправно.
Переходим к следующему шагу - проверке на корректность предоставленных данных пользователем и возможности работы пользователя с XML-RPC блога.
С XML-RPC блога имеет право работать только администратор, следовательно, необходимо узнать кто пробует получить доступ. Для этого воспользуемся методом wp.getUsersBlogs. Параметрами метода являются логин и пароль.
Но прежде, чем приступим к отправке запроса и получению ответа, думаю, стоит немного задуматься о будущем и предусмотреть работу с ошибками, формирование документов и т.д.
В предыдущей проверке, можно сказать, было баловство - простейших вариант работы типа:
отправил/получил/тут_же_разобрал/забыл/пошел_дальше.
Так как я планирую развивать модуль по работе с API WordPress и дальше, то есть смысл определиться со следующими моментами в работе:

  1. Сформировать "скелет" документа
  2. Записать в документ все необходимые параметры, учитывая типы данных
  3. Отправить запрос и получить ответ от сервера
  4. Проанализировать ответ и, если в ответе содержится сообщение об ошибке, то правильно его прочитать

Все эти четыре шага я сделал отдельными методами класса.  Под "скелетом" документа я понимаю следующее содержимое:

<methodCall>
 <methodName>MethodName</methodName>
 <params>    </params>
</methodCall>

То есть часть документа, содержащая имя метода и узел params без содержимого. Дальше на останется только правильно заполнить список параметров. Чем мы сейчас и займемся.

Всего в XML-RPC предусмотрено использование шести простых типов данных:

  1. int и i4 - целые числаinteger)
  2. double - дробные числа
  3. string - строки
  4. base64 - закодированная строка
  5. dateTime.iso8601 - дата/время
  6. boolean

Заводим новый тип данных:

TSimpleType = (tsInt, tsI4, tsString, tsDouble, tsDateTime, tsBase64, tsBoolean);

С помощью значений этого типа будем определять тэг для значения параметра.

Так как операции создания "скелета" документа и добавления параметров метода разнесены по разным функциям, то создадим ещё один вспомогательный тип данных:

PXMLDocument = ^IXMLDocument;

Теперь сам метод добавления параметра в документ:

procedure TBlog.SetParam(SimpleType: TSimpleType; Value: string;
Document: PXMLDocument);
var Root: IXMLNode;
begin
  if Document^.IsEmptyDoc then Exit;//документ пуст
  Root:=Document^.DocumentElement.ChildNodes.FindNode('params');
  if Root=nil then Exit; //узел не найден
  case SimpleType of
    tsInt:Root.AddChild('param').AddChild('value').AddChild('int').NodeValue:=Value;
    tsI4:Root.AddChild('param').AddChild('value').AddChild('i4').NodeValue:=Value;
    tsString:Root.AddChild('param').AddChild('value').AddChild('string').NodeValue:=Value;
    tsDouble:Root.AddChild('param').AddChild('value').AddChild('double').NodeValue:=Value;
    tsDateTime:Root.AddChild('param').AddChild('value').AddChild('dateTime.iso8601').NodeValue:=Value;
    tsBase64:Root.AddChild('param').AddChild('value').AddChild('base64').NodeValue:=Value;
    tsBoolean:Root.AddChild('param').AddChild('value').AddChild('boolean').NodeValue:=Value;
  end;
end;

Этот метод работает только в случае записи простого типа. При работе со структурами необходимо доработать алгоритм.

Теперь про анализ сообщений об ошибке. Рассмотрим пример того, как выглядит сообщение об ошибке в XML-RPC:

<methodResponse>
<fault>
  <value>
    <struct>
      <member>
        <name>faultCode</name>
        <value>
            <int>403</int>
        </value>
      </member>
      <member>
        <name>faultString</name>
        <value>
          <string>Bad login/pass combination.</string>
        </value>
      </member>
  </struct>
</value>
</fault>
</methodResponse>

Сообщение об ошибке приходит нам в структуре. Причём, если считать, что теги member нумеруются от нуля, то каждый чётный элемент структуры - это код ошибки, а нечётный - текст ошибки. Следовательно метод обработки сообщений об ошибке может выглядеть так:

function TBlog.ParseErrors(aDocument: PXMLDocument): TStringList;
var i:integer;
List: IDOMNodeList;
code: string;
begin
  List:=aDocument^.DOMDocument.getElementsByTagName('member');
  Result:=TStringList.Create;
  for i:=0 to List.length-1 do
    begin
      case i mod 2 of
        0:code:=(List.item[i].lastChild.firstChild as IDOMNodeEx).text; //чётный элемент - читаем код ошибки
        1://нечётный элемент - читаем текст ошибки и записываем результат
          Result.Add(code+' '+(List.item[i].lastChild.firstChild as IDOMNodeEx).text);
      end;
    end;
end;

Здесь код и текст ошибки записывается в TStringList. Думаю, что при необходимости можно легко сделать, чтобы код и текст читались в разные списки или массивы. Нам пока это не требуется.

Отправку документа мы уже рассматривали, поэтому сразу привожу метод проверки данных на корректность:

function TBlog.CheckUserAccess(const aURL, aUser, aPassword: string;var Error:string): boolean;
var Doc:IXMLDocument;
begin
  Doc:=GetDocument('wp.getUsersBlogs'); //создали "скелет"
//добавляем параметры
  SetParam(tsString,aUser,@Doc);
  SetParam(tsString,aPassword,@Doc);
  SendQuery(@Doc,aURL); //отправляем запрос
  if not Doc.IsEmptyDoc then //если документ записан корректно
    begin
      if Doc.DocumentElement.ChildNodes.FindNode('fault')&lt;&gt;nil then //есть сообщение об ошибке
        begin
          Result:=false;
          Error:=ParseErrors(@Doc).Strings[0];
        end
      else
        Result:=true;
    end
else
  Result:=false;
end;

Если пришло сообщение об ошибке, то записываем сообщение в переменную Error. В данном случае структура содержит только одно сообщение об ошибке - поэтому я так легко прописал:

Error:=ParseErrors(@Doc).Strings[0];

Итак, две проверки сделаны и мы определили, что XML-RPC включен и работает исправно, а пользователь ввёл корректные данные логина и пароля и может работать с API WordPress. Что дальше? А дальше начинаем основную работу - получаем данные по комментариям в блоге.

2. Получаем данные о постах блога.

Итак, что предоставляет в наше распоряжение WordPress. Сначала сделаем кратки обзор методов из xmlrpc.php.

wp.getPostStatusList - выводит значения для статуса поста. По сути на выходе будем имеет четыре строки:  'draft', 'pending',  'private', 'publish'.

Пока этот метод нам бесполезен.

blogger.getRecentPosts - эта функция уже из API Blogger, но поддерживается в WordPress. На выходе будем иметь последние посты блога, включая весь контент поста.

Можно использовать метод, НО работа программы будет замедлена так как придётся "тягать" по Сети пост целиком. А если попробуем получить список постов блога целиком, то, видимо придётся ложиться спать, не дождавшись результата. Следовательно - пока оставляем метод в стороне.

metaWeblog.getRecentPosts - аналогично предыдущему методу.

mt.getRecentPostTitles - метод из MovableType API. Судя по названию - то, что нам надо. Смотрим описание метода.

Метод возвращает список, содержащий заголовки постов блога. При этом контент в список не записывается.

Входные параметры:

  • String blogid
  • String username
  • String password
  • int numberOfPosts

blogid всегда равен 1 (см. описание в xmlrpc.php)

numberOfPosts - количество постов, которые необходимо вывести в список. Если параметр имеет значение больше, чем количество постов в блоге, то метод возвращает список всех постов.

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

  • дату создания элемента
  • userid
  • postid
  • заголовок.

Замечательно. Воспользуемся этим методом, а заодно научимся анализировать сложные структуры ответа.

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

Массивы не имеют названия и описываются тегом <array>. Он содержит один элемент <data> и один или несколько дочерних элементов <value>, где задаются данные. В качестве элементов массива могут выступать любые другие типы в произвольном порядке, а также другие массивы - что позволяет описывать многомерные массивы. Так же можно описывать массив структур. Например, массив из четырех элементо будет выглядеть так:

<array>
  <data>
     <value><i4>34</i4></value>
     <value><string>Привет, Мир!</string></value>
     <value><boolean>0</boolean></value>
     <value><i4>-34</i4></value>
  </data>
</array>

У нас на выходе из метода mt.getRecentPostTitles
будет содержаться массив структур, причём одна структура - это информация по одному посту блога. Следовательно, чтение данных по постам блога можно условно разделить на следующие шаги:

  1. Выделяем из XML-документа все элементы value
  2. В каждом value читаем все элементы member
  3. каждый второй дочерний элемент у member - данные по посту, которые необходимо запомнить.

Начнем сразу с обработки ответа. Вводим новый тип данных:

type
TBlogPost = packed record
  id: integer;
  user_id: integer;
  dateCreated: string;//пока будем хранить дату "как есть"
  title: string;
end;
 
type
TBlogPosts = array of TBlogPost;

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

[...]
//т.к. в массиве всего 1 тэг data, то можно получить список элементов так
Values:=Doc.DOMDocument.getElementsByTagName('data').item[0].childNodes;
SetLength(Result,Values.length);
for i:= 0 to Values.length-1 do
  begin
    Members:=Values[i].firstChild.childNodes;//получили все members для 1 value
    for j:=0 to Members.length - 1 do
      begin
        with Result[i]do
          case j of
            0:dateCreated:=(Members[j].lastChild.firstChild as IDOMNodeEx).text;
            1:user_id:=StrToInt((Members[j].lastChild.firstChild as IDOMNodeEx).text);
            2:id:=StrToInt((Members[j].lastChild.firstChild as IDOMNodeEx).text);
            3:title:=(Members[j].lastChild.firstChild as IDOMNodeEx).text;
          end;
      end;
end;
[...]

Соответственно, если получено сообщение об ошибке, то можно воспользоваться рассмотренной ранее функцией.

На сегодня всё. В следующий раз продолжим работу с API и попробуем получить все комментарии из блога.

----------------------
Пока я пишу следующий пост, Вы можете прогуляться и купить ковер, если у Вас его ещё нет :)

CCleaner - это отличная программа для очистки Вашего компьютера от ненужного хлама. Можете ccleaner скачать бесплатно и забыть о том, что такое ручная уборка жёсткого диска ПК.

Интересный блог http://vsesmispb.livejournal.com/. Первое, что бросается в глаза - риторический вопрос от автора "Нахрена козе баян" :) Ответ на этот вопрос найдёт только самый внимательный читатель:)
----------------------
Понравилась статья? Тогда:
Делись! Загружай! Плюсуй!
   Отправить PDF на   
Читай ещё статьи на WebDelphi.ru

Комментарии (5)

WP_Cloudy
  • DremLIN.Ru пишет:

    По поводу Delphi XML-RPC на sourceforge.net оставил комментарий в предыдущем посте:
    http://www.webdelphi.ru/2009/10/xml-rpc-v-delphi-pervoe-znakomstvo-s-wordpress-iznutri/

  • Vlad пишет:

    Так я уже ответил :) Спасибо за ссылку — посетителям пригодится 100%.

  • DremLIN.Ru пишет:

    Я сам по-осени (2009) сильно озаботился темой XML-RPC в Delphi. Даже подключился к разработке Delphi XML-RPC на sourceforge.net. Я конвертировал старое CVS хранилище в SVN, создал себе бранч-ветку для экспериментов…
    (Добрый владелец проекта дал мне роль  админа проекта, флаг в руки, барабан на шею и сославшись на полное отсутствие времени на свой проект — зеленый свет на доработку…)
    Частично адаптировал проект под Unicode (D2009, D2010), перевел на Indy10… А потом на работе навалилось работы и …. В общем не закончил я процесс до конца…
    Вот здесь я выложил «рыбу» может кому-нибудь пригодиться… dbxmlrpc
    Была задумка «поженить» Delphi DB-Aware и XML-RPC. То есть хотелось реализовать прозрачную передачу данных стандартными для Delphi средствами (dataset, datasource, dbgrid и т.д.) по протоколу  XML-RPC.
    Хватило только на подготовку платформы… (D2009, D2010, Indy10). До работы с DB я так и не дошел. Пока не до этого, к сожалению…
    Эх… Жаль не знал про Ваш сайт раньше…
    Вот как-то так…
    Сергей.

  • Vlad пишет:

    Спасибо, Сергей за комментарий, вполне возможно, что проект оживет…Кстати, то, что пробовали скрестить БД — очень хорошо. Мне это пока не требуется, а вот один из читателей блога, думаю, должен заинтересоваться, если прочитает комментарии. Я пока только учусь работать с XML-RPC :) Почему-то после того как поработал в Lazarus очень сильно привязался к библиотеке Synapce. Может потому что всё работает просто и быстро, может просто отвык от Indy и сейчас как-то не хочется работать с этими компонентами. Вот и сейчас даже уже в Delphi 2010 «прикрутил» себе синапс и пробую соорудить что-нибудь на свой лад…Просто стало интересно между делом написать что-нибудь для работы именно с WordPress. Что получится пока не знаю — может клиент для блога, а может просто библиотекой выложу все на радость людям :)

  • deksden пишет:

    Маленькое стилистическое замечание: имхо, правильное название библиотеки SynapSe

Ваш ответ

Внимание: Все комментарии модерируются, и это может вызвать задержку их публикации. Отправлять комментарий заново не требуется.

Пожалуйста, заключайте исходный код в тэги [code][/code].
Если код большой, то воспользуйтесь Вставкой кода на отдельной странице и оставьте в комментарии ссылку на исходник