Подписка


Подписаться на Google Buzz

Друзья блога

Пульс блога

Опрос

Каких статей следует публиковать больше в блоге?

View Results

Loading ... Loading ...

Система Orphus

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

DelphiFeeds.ru - Все Delphi-блоги Рунета О раскрутке блога по программированию

Счётчики


Анализ веб сайтов

Рейтинг блогов

  • 09Feb

    Наконец-то разобрался со своим провайдером и доказал тех.поддержке, что проблемы с загрузкой Google из-за их глюков в DNS. Теперь все работает как полагается и можно спокойно приступать к работе с Google API.

    Начать решил с Google Celendar API по двум причинам.

    Во-первых, потому что сам частенько пользую этот сервис как напоминалку о ближайших событиях и встречах, когда необходимо получить уведомление в виде SMS (к сожалению, в Хронометре такой функции не предусмотрено).

    Во-вторых, думаю в свободное время начать работу над новой версией Хронометра и скрестить его функционал с этим он-лайн сервисом Google. Думаю, получиться должно неплохо. На данный момент в руках уже есть класс, используемый для авторизации в аккаунте, поэтому сразу приступил к работе с целевым API. Итак, что можно сделать, используя Celendar API:

    1. Получить список календарей (весь или только тех, владельцами которых Вы являетесь)
    2. Добавить новый, редактировать или удалить календарь (основной календарь удалить нельзя, судя по описанию API)
    3. Добавить, удалить или редактировать подписчиков
    4. Работать с событиями в календарях
    5. и ещё много чего

    В общем работы навалом и это только с одним из списка доступных API Google для использования в настольных приложениях.

    Первое о чем стоит в данном случае позаботиться (если планировать разработку модулей для нескольких API) – это предусмотреть единый класс для работы с запросами. Можно было бы пойти более простым путем и взять, например, Indy или Synapse в качестве дополнительных библиотек и спокойно толкать туда-сюда запросы, анализировать результат и т.д. Но, почему-то мне кажется, что это не вариант.  Лучше, наверное, создать свой небольшой класс на основе WinInet для отправки запросов, необходимых для работы с API и не волочь каждый раз за собой библиотеку на 1,5 Mb.

    Именно с этого я и начал – с создания своего класса для работы с запросами. При этом необходимо было учесть, что в API Google используются как простые http- так и https-протоколы, а запросы не ограничиваются простыми GET и POST. В том же Celendar API используются дополнительно PUT и DELETE.

    Какие функции из WinInet нам нужны? Для того, чтобы подключиться к серверу и, например, скачать информацию нам необходимо последовательно вызвать ряд функций:

    1. InternetOpen - получить дескриптор и использовать его в следующей функции
    2. InternetConnect - создать подключение к серверу и получить дескриптор этого подключения для использовании в следующей функции
    3. HttpOpenRequest - создать новый запрос, опять же получив дескриптор, на этот раз HTTP-запроса, который использовать в функции
    4. HttpSendRequest - отправить запрос, используя дескриптор HTTP-запроса и читать данные функцией InternetReadFile.

    Как видите, в самом простом случае надо использовать пять функций. При этом не учитывается то, что нередко (а в случае работы с Google API – постоянно) приходится включать в запрос специфические заголовки, наподобие версии API и т.д., использовать различные параметры, читать ответные заголовки и т.д. В этом случае каждый раз писать по-новой функцию на отправку/получение данных, мягко говоря может вызвать геморрой и несварение желудка.

    На данный момент класс THTTPSender имеет следующее описание:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    
    type
    THTTPSender = class
    private
      FHeaders: TStringList;
      FAgent: string;
      FMethod: string;
      FProtocol: string;
      FPort: integer; //порт
      FDomain: string;   //домен без http
      FParamStr: string; //всё, что стоит в адресе после / кроме параметров
      FParameters: string;
      procedure ParseURL(var cURL:string);
    public
      constructor Create;
      function SendRequest(fURL:string):WideString;
      property Headers: TStringList read FHeaders write FHeaders;
      property Agent: string read FAgent write FAgent;
      property Method: string read FMethod write FMethod;
      property Protocol:string read FProtocol write FProtocol;
    end;

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

    GData-Version: 2

    Заголовок, указывающий какую версию API мы используем.

    Authorization: GoogleLogin auth=yourAuthToken

    Заголовок для доступа к календарям, где yourAuthToken – это параметр Auth, полученный при авторизации с использованием GoogleLogin.

    Отправляем запрос и получаем ответ в виде строки:

    1
    2
    3
    4
    5
    6
    7
    
    with THTTPSender.Create do
    begin
      Method:='GET';
      Headers.Add('GData-Version: 2');
      Headers.Add('Authorization: GoogleLogin auth='+FAccount.Auth);
      ResponseText:=SendRequest('http://www.google.com/calendar/feeds/default/allcalendars/full');
    end;

    Функция SendRequest работает следующим образом:

    Анализируем URL:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    
    procedure THTTPSender.ParseURL(var cURL: string);
    var lencurl: cardinal;
        aURL: string;
        aURLc: TURLComponents;
    begin
      lencurl:=INTERNET_MAX_URL_LENGTH;
      if pos('http',cURL)<=0 then
      cURL:='http://'+cURL;
      SetLength(aURL, lencurl);
    //каноникализируем URL
      InternetCanonicalizeUrl(PChar(cURL),PChar(aURL),lencurl,ICU_BROWSER_MODE);
    //разбиваем УРЛ на составные части
      with aURLc do
        begin
          lpszscheme := nil;
          dwschemelength := internet_max_scheme_length;
          lpszhostname := nil;
          dwhostnamelength := internet_max_host_name_length;
          lpszusername := nil;
          dwusernamelength := internet_max_user_name_length;
          lpszpassword := nil;
          dwpasswordlength := internet_max_password_length;
          lpszurlpath := nil;
          dwurlpathlength := internet_max_path_length;
          lpszextrainfo := nil;
          dwextrainfolength := internet_max_path_length;
          dwstructsize := sizeof(aurl);
      end;
      if InternetCrackUrl(PChar(aURL), Length(aURL), 0, aURLC) then
        begin
          if aURLc.lpszUrlPath='/' then
            FDomain:=ReplaceStr(aURLC.lpszHostName,'/','')
          else
            begin
              FDomain:=copy(aURLC.lpszHostName,1,length(aURLC.lpszHostName)-length(aURLC.lpszUrlPath));
            end;
      FPort:=aURLc.nPort;
      FParamStr:=aURLc.lpszUrlPath;
      FParameters:=aURLc.lpszExtraInfo;
      case aURLc.nScheme of
        INTERNET_SCHEME_DEFAULT:FProtocol:='HTTP';
        INTERNET_SCHEME_FTP:FProtocol:='FTP';
        INTERNET_SCHEME_HTTP:FProtocol:='HTTP';
        INTERNET_SCHEME_HTTPS:FProtocol:='HTTPS';
        INTERNET_SCHEME_FILE:FProtocol:='FILE';
        INTERNET_SCHEME_MAILTO:FProtocol:='MAILTO';
      end;
    end
    else
      MessageBox(0, PChar('Ошибка WinInet #'+IntToStr(GetLastError)),'Ошибка', MB_OK);
    end;

    Все данные, необходимые для осуществления запроса сохраняются в соответствующих полях класса. Далее последовательно выполняем функции:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
    if FProtocol='HTTP' then
      hConnect:=InternetConnect(hInet, PChar(FDomain), INTERNET_DEFAULT_HTTP_PORT,'anonymous', nil, INTERNET_SERVICE_HTTP, 0, 0)
    else
      if FProtocol='HTTPS' then
        hConnect:=InternetConnect(hInet, PChar(FDomain), INTERNET_DEFAULT_HTTPS_PORT,'anonymous', nil, INTERNET_SERVICE_HTTP, 0, 0)
      else
        //уничтожаем дескриптор и выходим
      if hConnect<>nil then
        hRequest:=HttpOpenRequest(hConnect,PChar(FMethod),PChar(FParamStr),nil,nil,nil,INTERNET_FLAG_PRAGMA_NOCACHE or INTERNET_FLAG_RELOAD,0)
      else
       //уничтожаем дескрипторы и выходим

    Получили дескриптор hRequest, необходимы для отправки запроса. При этом если протокол не HTTP и не HTTPS, то завершаем работу предварительно убив дескрипторы.

    Далее, нам необходимо добавить в запрос свои заголовки. Для того, чтобы добавить в запрос свои заголовки можно воспользоваться функцией HttpAddRequestHeaders или указать свои заголовки непосредственно в функции HttpSendRequest.

    В первом случае, в функцию HttpAddRequestHeaders необходимо передать следующие параметры:
    hConnect - дескриптор, полученный при вызове HttpOpenRequest
    lpszHeaders -указатель на строку, содержащую заголовки, при это каждый заголовок отделяется символами #10#13
    dwHeadersLength - общая длина заголовков (в символах)
    dwModifiers - флаги-модификаторы. Для нас достаточно использование флага HTTP_ADDREQ_FLAG_ADD.

    Ну, а дальше используем уже известный, наверное каждому, кто имел дело с WinInet алгоритм чтения данных:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    
    if HttpSendRequest(hRequest,nil,0,PChar(FParameters),length(PChar(FParameters)))then
    begin
      SetLength(Buff, 1);
      B:=false;
      I:=1;
      while true do
        begin
          DataAvailable(hRequest, L);
          if L = 0 then break;
          SetLength(Buff, I + L);
          B:=InternetReadFile(hRequest, @Buff[I], L, ReadedSize);
          if NOT B then break;
          inc(I, ReadedSize);
        end;
      Buff[I] := #0;
      if B then
        begin
         result := WideString(Buff);
        end
      else 
        result := '';
    end;

    Пока использовал класс в работе с запросами GET к календарям Google. Вроде бы видимых косяков не нашел. Как доработаю полностью – выложу в доступ.

    Теперь, что касается непосредственно API. На данный момент класс для работы с календарями умеет совсем немного – получать доступ к аккаунту и получать информацию по всем календарям. Выглядит он, соответственно, проще простого:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    
    type
      TCelendar = record
        id: string;
        etag:string;
        Author:string;
        Title: string;
        Description: string;
        timezone:string;
        hidden:boolean;
        Color:string;
        selected:boolean;
        accesslevel:string;
        where:string;
    end;
     
    type
     TCelendarList = array of TCelendar;
     
    type
      TGoogleCalendar = class
    private
      FAccount: TGoogleLogin;
      FCelendars: TCelendarList; //календари пользователя
      function GetCelendars: TCelendarList;
    public
      constructor Create(const email, password: string);
      function Login: boolean;
      property Account: TGoogleLogin read FAccount write FAccount;
      property Celendars:TCelendarList read GetCelendars;
    end;

    Те, кто читал заметку про ClientLogin, поймут без проблем, что такое TGoogleLogin. При этом вызов функции Login возвращает нам только true или false в зависимости от результата авторизации, а вся информация, в т.ч. и по каптче (если её ввод требуется) спокойно хранятся в поле FAccount.

    При чтении списка календарей происходит анализ небольшой XML-документа и заполнение массива TCelendarList. Сейчас буду дорабатывать класс для работы с запросами и сразу его испытывать при работе с API Google.

    Кстати, инфрмация для тех, кто скачивал модуль GoogleLogin. В модуле есть небольшой косяк -  строку 155 необходимо изменить на

    1
    
    if pos('AUTH',UpperCase(List[i]))>0 then

    иначе параметр Auth не читается. А в целом вроде бы больше проблем не наблюдалось.

    На сегодня все. Ждите продолжения :) .

    --------------------------
    Устали искать нужный Вам софт? Скачать софт бесплатно можно на soft-personal.ru. Тут же можно и посмотреть подробные обзоры по софту.

    Хотите узнать, что такое остеопатическое тестирование и лечение диафрагмы? Почитайте. Я про такие болезни услышал впервые...чего только не случается с нами - человеками....

    Интересно, что может произойти с человеком, если он будет долго и громко петь в караоке-клубе? Кстати, вот сайт караоке-клуба в центре Москвы. Тем кто обожает петь - в самый раз подойдет.
    --------------------------
    Закладки:
    • Print
    • Digg
    • Sphinn
    • del.icio.us
    • Facebook
    • Mixx
    • Google Bookmarks
    • RSS
    • FriendFeed
    • LinkedIn
    • Live
    • Twitter
    • Twitthis

    Похожие записи:

    1. Google Celendar API в Delphi. Работа с календарями.
    2. Google API. Интерфейс ClientLogin для Delphi.
    3. Google API на Delphi. Вспомогательный модуль.
    4. Google Data Protocol. Разбор элементов Entry.
    5. Пара слов об InternetCrackURL.

    Автор Vlad в 12:15 am

    Метки: , , ,

8 Comments

WP_Cloudy
  • Гимаев Наиль пишет:

    Вот уж не проблема прикрутить к Хронометру отправку SMS. Хотя бы вызывать SimpleSMS через ShellExecute. Проблема – где достать второй сотовый, чтобы присоединить его к компьютеру, и который будет отправлять SMS.
    C ГуглКалендарь удобно работать через файлы календарей (ics), это позволяет синхронизировать совместную работу множества календарных программ.
    ссылка: http://www.simplesms.ru/

  • Алексей Тимохин пишет:

    Класс, спасибо! =)

  • Vlad пишет:

    Да пока, вроде, не за что. Я только начал, а к чему это приведет…пока трудно предположить, но API довольно объемное, только константы часовых поясов чего стоят – 309 штук :)

  • Максим Коробов пишет:

    А с Google Translate API вы собираетесь разобраться? :)

  • Vlad пишет:

    Есть такая задумка. Последовательно пройтись по нескольким API, в том числе и по переводчику, правда у переводчика только AJAX API поэтому придётся делать “финт ушами”….

  • dobrovestnik ru пишет:

    Автор, опять велосипед изобретаем. на питоне это в разы меньше и проще.
    А Google Translate API в Делфи уже реализован http://code.google.com/p/imadering/source/browse/branches/to_delphi_2010/GtransUnit.pas
    Спасибо Эдуарду Толмачёву.
     

  • Vlad пишет:

    За ссылку спасибо. А зачем мне питон? :) API Google и на С++ реализован и на питоне и даже на JavaScript есть. Получается тот, кто разрабатывал библиотеки, например под С++ велосипед изобретал?

  • Максим Коробов пишет:

    dobrovestnik ru,

    Cпасибо за то, что сообщили насчёт готового модуля. Было бы просто не удобно перед Vlad’ом, если бы это обнаружилось после того, как он приступил бы к исследованиям.
    Насчёт велосипеда – данный труд однозначно не зря. Как минимум по той причине, что перенос с языка на язык иногда бывает довольно трудным процессом. Я лично столкнулся с проблемами в адресной арифметике, когда писал код на CryptoAPI с упрощёным примером на C++. И это было не сладко :)

Ваш ответ

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

Пожалуйста, заключайте программный код в теги [code][/code].


Protected by Copyscape Duplicate Content Detector