уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.

Сегодня решил немного продолжить тему работы с 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 и попробуем получить все комментарии из блога.

0 0 голоса
Рейтинг статьи
уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.
Подписаться
Уведомить о
7 Комментарий
Межтекстовые Отзывы
Посмотреть все комментарии
DremLIN.Ru
DremLIN.Ru
18/01/2010 19:23

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

DremLIN.Ru
DremLIN.Ru
18/01/2010 20:10

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

deksden
deksden
19/01/2010 11:18

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

trackback
Модуль FeedBurner API для Delphi 2010. | Delphi в Internet
09/12/2012 05:13

[…] delphi xmlrpc ———————— Думаю, что многие из тех, кто читает мой блог — это студенты, учащиеся различных ВУЗов Москвы и других городом, поэтому, наверное им будет интересно про дипломы вуза москва. Так что, если Вас интересует документ об образовании, например дипломы вуза Москва, то проходите по приведенной выше ссылке. ———————— Понравилась статья? Тогда: […]

trackback

[…] Небольшие изменения в работе блога.    WordPress. Работа с XML-RPC в Delphi. […]