(495) 925-0049, ITShop интернет-магазин 229-0436, Учебный Центр 925-0049
  Главная страница Карта сайта Контакты
Поиск
Вход
Регистрация
Рассылки сайта
 
 
 
 
 

WordPress. Работа с XML-RPC в Delphi

Источник: webdelphi

Источник: webdelphi 

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

1

2

3

4

5

6

7

8

9

10

11

[...]

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

[...]

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 блога. Удачная отправка запроса свидетельствует только о том, что мы отправили запрос и получили ответ, а _что_ находится в ответе ошибка или нет - мы пока не знаем .
Чтобы пока не забивать голову лишними способами и методами парсинга ответа от сервера, предлагаю в данном случае остановиться на применении простой проверки:

1

2

3

4

5

[...]

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

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

1

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

 

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

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

1

PXMLDocument = ^IXMLDocument;

 

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

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 нумеруются от нуля, то каждый чётный элемент структуры - это код ошибки, а нечётный - текст ошибки. Следовательно метод обработки сообщений об ошибке может выглядеть так:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

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

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

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')<>nil then //есть сообщение об ошибке

        begin

          Result:=false;

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

        end

      else

        Result:=true;

    end

else

  Result:=false;

end;

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

1

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 - данные по посту, которые необходимо запомнить.

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

1

2

3

4

5

6

7

8

9

10

type

TBlogPost = packed record

  id: integer;

  user_id: integer;

  dateCreated: string;//пока будем хранить дату "как есть"

  title: string;

end;

 

type

TBlogPosts = array of TBlogPost;

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

[...]

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

Ссылки по теме


 Распечатать »
 Правила публикации »
  Написать редактору 
 Рекомендовать » Дата публикации: 21.01.2010 
 

Магазин программного обеспечения   WWW.ITSHOP.RU
Enterprise Connectors (1 Year term)
Delphi Professional Named User
Quest Software. SQL Navigator Professional Edition
ABBYY Lingvo x6 Английская Домашняя версия, электронный ключ
erwin Data Modeler Standard Edition r9.7 - Product plus 1 Year Enterprise Maintenance Commercial
 
Другие предложения...
 
Курсы обучения   WWW.ITSHOP.RU
 
Другие предложения...
 
Магазин сертификационных экзаменов   WWW.ITSHOP.RU
 
Другие предложения...
 
3D Принтеры | 3D Печать   WWW.ITSHOP.RU
 
Другие предложения...
 
Новости по теме
 
Рассылки Subscribe.ru
Информационные технологии: CASE, RAD, ERP, OLAP
Новости ITShop.ru - ПО, книги, документация, курсы обучения
Программирование на Microsoft Access
CASE-технологии
OS Linux для начинающих. Новости + статьи + обзоры + ссылки
СУБД Oracle "с нуля"
Все о PHP и даже больше
 
Статьи по теме
 
Новинки каталога Download
 
Исходники
 
Документация
 
 



    
rambler's top100 Rambler's Top100