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

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

С апреля 2013 года старый добрый Documents List API, о котором я рассказывал много и часто на страницах этого блога, практически перестал  существовать. Точнее, доступ к API есть и ClientLogin для авторизации пользователей тоже пока работает, но уже отчетливо видно, что API уже «не торт» — то загрузка документа не проходит, то работа со свойствами документа зависнет внезапно…И это 100% не баг в программе. В общем медленно, но верно Documents List API уходит в небытие, а на его место, как знают поклонники Гугла, выходит Google.Drive со своим Drive SDK и Drive API. В связи с этим пришлось бросать все, что нажито непосильным трудом, а именно текущие разработки и снова с головой лезть в документацию на Google Developers и практически с нуля создавать систему синхронизации документов для программы над которой я и ещё несколько человек трудимся уже несколько лет. Ну, а пока трудился над коммерческим продуктом, кое какие идеи родились и для блога. И именно про них сегодня и пойдет речь.

Google API сегодня

На сегодняшний день Google, по моим (и не только моим) скромным наблюдениям значительно улучшил, усовершенствовал или вообще переделал API своих основных продуктов (календаря, контактов, документов и т.д.) И, наверное, самый большой и жирный плюс среди всех усовершенствований — это повсеместная поддержка JSON. Запросы, которые раньше «весили» по 300-400 Кб, теперь можно спокойно «упаковать» в JSON и получить общий размер запроса чуть больше 100 Кб, а это, как вы понимаете, напрямую влияет на скорость работы программы.

Наряду с этим, Google медленно, но верно, «пересаживает» всех разработчиков на авторизацию по протоколу OAuth 2.0. Будучи разработчиком desktop-приложений можно долго и с оттягом говорить о том, как  этот протокол «мешает жить простым пользователям» этих самых приложений, т.к. требует открывать страничку в браузере (да, некоторых разработчиков это жуть как бесит), но я укажу только положительные моменты использования OAuth:

  1. Безопасность. Это первый и главный плюс OAuth, который легко перечеркнет все его недостатки по сравнению с тем же ClientLogin. Если использовать OAuth именно так как того требует тот же Google, то пользователь никогда и ни при каких обстоятельствах не предоставит вашей программе свои логин и пароль от аккаунта.
  2. Контроль со стороны разработчика. Используя OAuth я всегда, в любой момент времени могу сказать с какими API я могу работать, а с какими нет, сколько времени осталось «жить» моему ключу доступа и т.д. Могу программно «отключить» пользователя от использования API и т.д.
  3. Единый ключ на доступ ко всем API. Используя ClientLogin для доступа к нескольким API мне приходилось каждый раз авторизовывать пользователя по новой. Конечно, сам процесс простой как три копейки, но тем не менее. С OAuth я имею возможность за один запрос получить доступ хоть к сотне API — лишь бы они поддерживали OAuth.
  4. «Вечный» ключ доступа к API. Один раз получив разрешение на доступ к данным я могу практически бесконечно обновлять ключ доступ без участия пользователя. До тех пор пока пользователь не зайдет на специальную страничку с разрешениями и не «отрубит» приложение, либо пока я сам не обнулю ключ.

Вместе со всеми нововведениями Google также ненавязчиво приучает разработчиков платить за услуги доступа к API. Уже сейчас в Консоле API можно найти несколько API доступ к которым осуществляется только за деньги. О том хорошо ли это или плохо (монетизация Гуглом своих API) можно спорить, но от этого никуда не деться.

В итоге, относительно использования Google API вырисовывается следующая картина:

  1. XML уходит в прошлое (и пёс с ним), а на его место выходит JSON
  2. ClientLogin также выводится из работы, а на его место выходит OAuth
  3. Часть API частично или полностью монетизируются.

На последний пункт мне пока пофиг. Мое дело написать код, а кто будет платить за доступ — это уже дело второе. Другое дело, что мне дает Delphi для того, чтобы разработать необходимые классы для работы с API? А дает нам Delphi…все, что необходимо и даже чуть больше.

Delphi для работы с Google API

Итак, судя по тому, что сказано выше, для работы с Google API нам потребуется не так уж и много:

  1. Библиотека для работы с HTTP/HTTPS
  2. Библиотека для работы с JSON

Как вы понимаете, обе библиотеки уже давным давно присутствуют в Delphi и вполне хорошо работают: Indy и модули для работы с JSON — DBXJson, DBXJsonReflect и т.д.

В последних версиях Delphi все функции работы с JSON вынесены в модули System.JSON, System.JSON.BSON, System.JSON.Builders, System.JSON.Readers, System.JSON.Types, System.JSON.Utils, System.JSON.Writers, System.JSONConsts.

К Indy можно относиться по разному (сам я, где возможно, использую Synapse), но сейчас, по крайней мере, использование Indy дает одно неплохое такое преимущество по сравнению с другими аналогичными библиотеками, а именно: с Indy код будет прекрасно собираться и работать как под Windows, так и под Mac OS и, по-моему даже в iOS (могу ошибаться т.к. проверить сейчас негде).

В итоге, сегодня, используя только «родные» библиотеки и классы Delphi мы можем без проблем разработать компонент который:

  1. Будет прекрасно работать как с JSON, так и с XML (если потребуется)
  2. Будет работать под любой из поддерживаемых ОС: Windows, Mac OS, iOS

И на этой оптимистической ноте можно переходить к основному содержанию статьи.

Компонент OAuthClient для работы с API Google

Итак, для чего можно использовать компонент? За основу для компонента был взят класс для работы с Google OAuth о котором я рассказывал в блоге некоторое время назад, но со следующими изменениями:

  1. Теперь это стал полноценный компонент, который можно установить в палитру компонентов в IDE
  2. Вместо Synapse для работы с HTTP(S) теперь используется Indy
  3. В случае, если сервер Google вернул ошибку (например, вы не указали важный параметр запроса или пропустили заголовок авторизации и т.д.) генерируется свой тип исключений EOauthException, с помощью которого можно более детально проанализировать ошибку — определить местоположение ошибки в запросе (заголовок, параметр URL и т.д.), какое сообщение прислал Google и т.д.
  4. Компонент поддерживает все методы работы с протоколом OAuth — получение ключа доступа, обновление, проверка и обнуление ключа.
  5. Компонент может сохранять/загружать данные сессии в файл. Данные сохраняются в JSON-формате.

Теперь рассмотрим этот компонент более подробно.

Свойства компонента

property RedirectURI: string;

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

urn:ietf:wg:oauth:2.0:oob

Это значение следует использовать, если Вы хотите, чтобы Google делал редирект на свою страницу с кодом подтверждения для пользователя. Как только пользователь подтвердит доступ к данным своего аккаунта его «перебросит» вот на такую страничку в браузере:

webpage

После чего пользователь должен скопировать код со странички в нашу программу и завершить процесс авторизации.

Это значение URI удобно использовать в том случае, если вы будете использовать браузер по-умолчанию, например, Chrome для открытия страницы с информацией о нашем приложении.

http://localhost

Это значение указывает на то, что Google должен вернуть код подтверждения в параметре запроса, не открывая при этом свою страницу. То есть, после подтверждения доступа, пользователь увидит примерно такую картину:

localhostЭто значение можно использовать, например, в случае, если ваше приложение использует свой браузер (тот же TWebBrowser) и Вы можете отловить момент редиректа, чтобы скопировать из URL необходимый код автоматически без участия пользователя.

property ClientID: string;
property ClientSecret: string;

Эти свойства должны содержать ключи ClientID и ClientSecret вашего приложения, которые можно получить в консоли Google. Для того, чтобы получить ClientID и ClientSecret необходимо выполнить следующие действия:

1. Заходим в Консоль, создаем новый проект и включаем необходимые API на странице Services:

Console

 

2. Переходим на вкладку «API Access» Для нового проекта эта страничка будет выглядеть вот так:

Console_2Здесь мы можем создать всякие разные ключи доступа — для iOS-приложений Android-приложений, для Web-приложений и т.д.

3. Жмем большую синюю кнопку «Create an OAuth 2.0. ID…»

При этом откроется небольшое окно в котором мы должны ввести информацию о нашем будущем приложении: название, адрес домашней страницы, и лого. Я ввел следующую информацию:

Console_3

Жмем «Next» и Google попросит нас выбрать тип приложения. Здесь ВАЖНО указать следующие параметры:

  • Application Type — Installed
  • Installed application type — other

То есть в браузере окно должно выглядеть вот так:

Console_4

Теперь остается нажать кнопку «Create client ID» и Google создаст нам новый идентификатор, а страница «API Access» станет выглядеть вот так:

Console_5

 

4. Копируем полученные значения ClientID и ClientSecret в наше приложение

Важно: ключи для нашего приложения Google помечает в консоли как Client ID for installed applications
property State: string;

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

property LoginHint: string;

Это свойство определяет необязательный параметр в запросе к Google. Смысл этого параметра заключается в следующем: если пользователь не был авторизован в своем аккаунте на момент, когда наше приложение просит дать доступ к данным, то перед переходом на страницу с разрешениями для нашего приложения Google предварительно попросит ввести пользователя авторизоваться. Так вот, если LoginHint будет содержать правильный email или ID пользователя, то в этом случае Google самостоятельно подставит нужный логин в форму авторизации. Мелочь, а приятно :)

property TokenInfo: TTokenInfo;

Это свойство содержит всю информацию о ключе доступа к API Google, в т.ч. все точки доступа, время жизни ключа и т.д. Более подробно о классе TTokenInfo будет рассказано ниже.

property SaveFields: TSaveFieldsSet;

Множество, определяющее, какие данные (помимо сведений о ключе доступа) следует сохранять в файл после вызова метода SaveToFile.

Для этого свойства определены следующие значения:

TSaveFields = (sfClientSecret, sfRedirectURI, sfState, sfLoginHint);
 TSaveFieldsSet = set of TSaveFields;
  • sfClientSecret — будет сохраняться значение ClientSecret. Это значение стоит использовать только на этапе тестирования ваших приложений, когда никто кроме вас не имеет доступа к файлу.
  • sfRedirectURI — сохранять в файл значение свойства RedirectURI
  • sfState — сохранять в файл значение свойства State
  • sfLoginHint — сохранять в файл значение свойства LoginHint
property DefaultContentType: TDefaultContentType;

Определяет формат данных, используемый по умолчанию в запросах к Google. Может принимать одно из двух значений:

TDefaultContentType = (ctJSON, ctXML);
  • ctJSON — в заголовок Content-Type каждого POST- или PUT-запроса будет записано значение «application/json»
  • ctXML — в заголовок Content-Type каждого POST- или PUT-запроса будет записано значение «application/xml»
property ValidateOnLoad: boolean;

Если значение свойства равно True, то каждый раз после загрузке данных о ключе из файла компонент будет проводить online-проверку действительности этого ключа.

property OpenStartURL: boolean;

Если значение свойства равно True, то компонент будет самостоятельно открывать страничку с подтверждением доступа в браузере по умолчанию (вне зависимости от того под какой ОС будет работать ваше приложение — Windows или Mac OS).

Методы компонента

function StartConnect: string;

Этот метод начинает процесс авторизации. Результат выполнения функции содержит URL на который должен перейти пользователь, чтобы подтвердить доступ нашему приложению.

Если OpenStartURL = True, то вызов этого метода также откроет полученный URL в браузере по умолчанию.

procedure EndConnect(const ACode: string);

Завершает процесс авторизации. Параметр ACode должен содержать код подтверждения доступа, который пользователь копирует сам из окна браузера или вы получаете его автоматически при использовании в RedirectURI значения «http://localhost».

После успешного выполнения этого метода в свойстве TokenInfo будет содержаться значение ключа доступа и необходимые данные для его обновления (о TTokenInfo см. ниже)

procedure RefreshToken;

Выполняет обновление ключа доступа. Этот метод следует вызывать каждый раз после того, как время жизни ключа истекло (обычно, ключ «живет» в течение 1 часа).

procedure Disconnect;

Удаляет всю информацию о ключе доступа и проводит online-отключение нашего приложения из аккаунта пользователя.

function OnlineValidate:boolean;

Проводит online-проверку и обновляет сведения о ключе доступа. Если ключ ещё «живой», то метод вернет True.

procedure SaveToFile(const AFileName: string);

Сохраняет информацию о ключе доступа и свойства компонента в файл AFileName.

procedure LoadFromFile(const AFileName: string);

Выгружает данные о ключе доступа из файла AFileName. Если свойство ValidateOnLoad = True, то после выгрузки данных происходит Online-проверка ключа.

procedure Get(const AURL: String; AResponseStream: TStream);

Выполняет Get-запрос к Google API и возвращает результат в поток AResponseStream.

procedure Post(const AURL: String; ASourceStream: TStream; AResponseStream: TStream); overload;
 procedure Post(const AURL, AContentType: String; ASourceStream: TStream; AResponseStream: TStream); overload;

Выполняет POST-запрос к Google API. Тело запроса передается в потоке ASourceStream, а результат запроса возвращается в AResponseStream. В overload-версии метода также можно указать свое значение заголовка Content-Type, отличное от задаваемых в свойстве DefaultContentType.

Свой заголовок Content-Type приходится использовать, например, при загрузке файлов в Google.Диск средствами Drive API.

procedure Delete(const AURL: string); overload;

Выполняет DELETE-запрос к Google API.

procedure Put(const AURL: string; ASourceStream: TStream; AResponseStream: TStream); overload;
 procedure Put(const AURL, AContentType: string; ASourceStream: TStream; AResponseStream: TStream); overload;

Выполняет PUT-запрос к Google API. Также как и в методе POST в этом методе можно указать свое значение заголовка Content-Type.

События компонента

property OnGetToken: TOnGetToken;
TOnGetToken = procedure(const ATokenInfo: TTokenInfo) of object;

Событие вызывается каждый раз после получения нового значения ключа доступа к API.

property OnDisconnect: TNotifyEvent;

Вызывается сразу после обнуления ключа доступа (после успешного вызова метода Disconnect)

property OnSave: TNotifyEvent;

Вызывается после успешного вызова метода SaveToFile.

property OnLoad: TNotifyEvent;

Вызывается после успешного вызова метода LoadFromFile

property OnValidateError: TNotifyEvent;

Вызывается в случае, если online-проверка показала, что ключ доступа больше не действителен.

property OnValidateComplete: TNotifyEvent;

Вызывается в случае, если online-проверка ключа показала, что ключ ещё «жив» и может использоваться для доступа к ресурсам API.

Класс TTokenInfo — информация о ключе доступа

Класс TTokenInfo предназначен для хранения Всей информации о доступе к ресурсам Google API и определен следующим образом:

TTokenInfo = class(TCustomTokenInfo)
  public
    property AccessToken;
    property RefreshToken;
    property TokenType;
    property ExpiresTime;
    property ScopeParam;
    property Audience;
    property JSON;
  published
    property DriveScopes;
    property CalendarScopes;
    property TasksScopes;
    property CustomScopes;
  end;

Рассмотрим назначение свойств этого класса.

property AccessToken: string;

Содержит ключ доступа к ресурсам API. Свойство только для чтения.

property RefreshToken: string;

Содержит ключ, который используется для обновления AccessToken. Свойство только для чтения. Значение этого свойства лучше хранить как зеницу ока, т.к. потеря этого значения приведет к тому, что вам придётся просить пользователя снова проходить процедуру авторизации. Наличие же RefreshToken позволяет вам обновлять ключ автоматически вызовом одноименного метода RefreshToken у компонента OAuthClient.

property TokenType: string;

Тип ключа доступа. Это значение используется в заголовке авторизации при каждом доступе к ресурсам API.

property ExpiresTime: TDateTime read FExpiresTime;

Дата и время истечения срока жизни ключа доступа.

property ScopeParam: string read GetScopeStr;

Параметр для стартового URL, содержащая все точки доступа к ресурсам API, заданные в компоненте.

property Audience: string read FAudience write FAudience;

Это значение полностью совпадает с ClientID и возвращается сервером Google при проверке ключа доступа.

property JSON: string read GetJSON;

Строка, содержащая JSON-объект со всей информацией о ключе доступа. Используется для сохранения информации в файл.

property DriveScopes: TDriveScopes

Задает точки доступа к ресурсам Google Drive API.

property CalendarScopes: TCalendarScopes

Задает точки доступа к ресурсам Google Calendar API.

property TasksScopes: TTasksScopes

Задает точки доступа к ресурсам Google Tasks API.

property CustomScopes: TStrings

Содержит точки доступа к прочим API Google, например к Contacts API, Google Maps API и т.д.

Пример использования компонента OAuthClient

В качестве примера использования компонента я написал небольшую программку, с помощью которой Вы можете тестировать свои запросы к различным Google API.

Как устанавливаются компоненты в Delphi XE — XE3, думаю, рассказывать не стоит, поэтому сразу переходим к работе над нашей программкой. Итак, интерфейс получился следующий:

Вкладка задания свойств компонента

Вкладка задания свойств компонента

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

Вкладка "Работа с API"

Вкладка «Работа с API»

На этой вкладке мы можем просматривать основную информацию о ключе доступа, а также выполнять запросы к API.

Вначале напишем две вспомогательные процедуры: первая будет задавать свойства компонента OAuthClient, а вторая — чекать нужные чекбоксы на форме, заполнять edit’ы и т.д. (здесь бы использовать LiviBinding, но если начать ещё расписывать работу с LiveBinding, то пост точно будет ещё неделю писаться :)). Первая процедура для задания свойств компонента:

procedure TForm2.SetProperties;
begin
  with OAuthClient1 do
    begin
      ClientID:=edClientUD.Text;
      ClientSecret:=edClientSecret.Text;
      RedirectURI:=edRedirectURI.Text;
      State:=edState.Text;
      LoginHint:=edLoginHint.Text;
      OpenStartURL:=chkOpenStartURL.Checked;
      ValidateOnLoad:=chkOnlineValidate.Checked;
      SaveFields:=[];
      if chkSaveClientSecret.Checked then
        SaveFields:=SaveFields+[sfClientSecret];
      if chkSaveRedirect.Checked then
        SaveFields:=SaveFields+[sfRedirectURI];
      if chkSaveState.Checked then
        SaveFields:=SaveFields+[sfState];
      if chkSaveLoginHint.Checked then
        SaveFields:=SaveFields+[sfLoginHint];
      with TokenInfo do
        begin
          CalendarScopes.Readonly:=chkCalendarReadOnly.Checked;
          CalendarScopes.FullAccess:=chkCalendarFull.Checked;
 
          TasksScopes.Readonly:=chkTasksReadOnly.Checked;
          TasksScopes.FullAccess:=chkTasksFull.Checked;
 
          DriveScopes.Readonly:=chkDriveReadOnly.Checked;
          DriveScopes.FullAccess:=chkDriveFull.Checked;
          DriveScopes.FileAccess:=chkDriveFileAccess.Checked;
          DriveScopes.AppsReadonly:=chkDriveAppsReadOnly.Checked;
          DriveScopes.ReadonlyMetadata:=chkDriveReadOnlyMetaData.Checked;
          DriveScopes.Install:=chkDriveInstall.Checked;
          DriveScopes.Appdata:=chkDriveAppData.Checked;
 
          CustomScopes.Assign(memCustomScopes.Lines);
        end;
    end;
end;

Вторая — «рисуем» интерфейс по значениям свойств компонента:

procedure TForm2.PaintInterface;
begin
  edToken.Text:=OAuthClient1.TokenInfo.AccessToken;
  edRefreshToken.Text:=OAuthClient1.TokenInfo.RefreshToken;
  lbTokenLifiteme.Caption:=DateTimeToStr(OAuthClient1.TokenInfo.ExpiresTime);
 
  edClientUD.Text:=OAuthClient1.ClientID;
  edClientSecret.Text:=OAuthClient1.ClientSecret;
  edState.Text:=OAuthClient1.State;
  edLoginHint.Text:=OAuthClient1.LoginHint;
  edRedirectURI.Text:=OAuthClient1.RedirectURI;
 
  with OAuthClient1.TokenInfo do
    begin
      chkCalendarReadOnly.Checked:=CalendarScopes.Readonly;
      chkCalendarFull.Checked:=CalendarScopes.FullAccess;
 
      chkTasksReadOnly.Checked:=TasksScopes.Readonly;
      chkTasksFull.Checked:=TasksScopes.FullAccess;
 
      chkDriveReadOnly.Checked:=DriveScopes.Readonly;
      chkDriveFull.Checked:=DriveScopes.FullAccess;
      chkDriveFileAccess.Checked:=DriveScopes.FileAccess;
      chkDriveAppsReadOnly.Checked:=DriveScopes.AppsReadonly;
      chkDriveReadOnlyMetaData.Checked:=DriveScopes.ReadonlyMetadata;
      chkDriveInstall.Checked:=DriveScopes.Install;
      chkDriveAppData.Checked:=DriveScopes.Appdata;
 
      memCustomScopes.Lines.Assign(CustomScopes);
    end;
end;

Теперь переходим к обработчикам событий. Вначале распишем события компонента. Они используются в программе в основном для вывода небольших информационных сообщений о работе программы.

Событие OnGetToken

//rsNewKey = 'Был получен ключ доступа. Для просмотра информации о ключе перейдите на вкладку "Работа с API"';
procedure TForm2.OAuthClient1GetToken(const ATokenInfo: TTokenInfo);
begin
  PaintInterface;
  ShowMessage(rsNewKey);
end;

Событие OnDisconnect:

//rsRevokeKey = 'Ключ был аннулирован и больше не может использоваться для доступа к API';
procedure TForm2.OAuthClient1Disconnect(Sender: TObject);
begin
  ShowMessage(rsRevokeKey);
end;

Событие OnLoad

//rsLoadKey = 'Информация о ключе была загружена из файла';
procedure TForm2.OAuthClient1Load(Sender: TObject);
begin
  PaintInterface;
  ShowMessage(rsLoadKey);
end;

Событие OnSave

//rsSaveKey = 'Информация о ключе была сохранена в файл';
procedure TForm2.OAuthClient1Save(Sender: TObject);
begin
  ShowMessage(rsSaveKey);
end;

Событие OnValidateComplete

procedure TForm2.OAuthClient1ValidateComplete(Sender: TObject);
//rsValidKey = 'Проверка ключа прошла успешно.';
begin
  ShowMessage(rsValidKey);
end;

Событие OnValidateError

//rsErrorKey = 'Ключ не действителен. Обновите его или пройдите процедуру авторизации заново';
procedure TForm2.OAuthClient1ValidateError(Sender: TObject);
begin
  ShowMessage(rsErrorKey);
end;

Теперь напишем обработчики для кнопок. Обработчик OnClick кнопки  «Начать процесс авторизации»:

procedure TForm2.btnStartClick(Sender: TObject);
begin
  SetProperties;
  edStartURL.Text:=OAuthClient1.StartConnect;
end;

Обработчик кнопки «Завершить авторизацию»

procedure TForm2.btnEndConnectClick(Sender: TObject);
begin
  OAuthClient1.EndConnect(edCode.Text);
end;

Остальные обработчики расписывать не буду, т.к. как и в обработчиках выше, каждая кнопка выполняет свой метод компонента OAuthClient. Перейдем ко второй вкладке — «Работа с API» и напишем обработчик для кнопки «Выполнить запрос»:

procedure TForm2.btnSendRequestClick(Sender: TObject);
var Source, Response: TStringStream;
begin
try
  Source:=TStringStream.Create;
  try
    Response:=TStringStream.Create;
    try
      case cbMethod.ItemIndex of
       0:OAuthClient1.Get(edURL.Text,Response);
       1:begin
           memRequestBody.Lines.SaveToStream(Source);
           OAuthClient1.Post(edURL.Text,edContentType.Text,Source,Response);
         end;//POST
       2:begin
          memRequestBody.Lines.SaveToStream(Source);
          OAuthClient1.Put(edURL.Text,edContentType.Text,Source,Response);
         end;//PUT
       3:OAuthClient1.Delete(edURL.Text);
      end;
      memResponse.Clear;
      memResponse.Lines.Add(Response.DataString);
    finally
     Response.Free
    end;
  finally
    Source.Free;
  end;
except
  on E:EOAuthException do
    begin
      memResponse.Clear;
      memResponse.Lines.Add('-------------Error-------------');
      memResponse.Lines.Add('Exception: '+E.Message);
      memResponse.Lines.Add('ServerMessage: '+E.ServerMessage);
      memResponse.Lines.Add('Domain: '+E.Domain);
      memResponse.Lines.Add('Reason: '+E.Reason);
      memResponse.Lines.Add('LocationType: '+E.LocationType);
      memResponse.Lines.Add('Location: '+E.Location);
      memResponse.Lines.Add('-------------ErrorMessage-------------');
      memResponse.Lines.Add(E.ErrorMessage);
    end
  else
    raise;
end;
end;

Здесь, в целом, все просто — по значению, выбранному в ComboBox’е определяем какой из методов нам использовать — GET, POST, PUT или DELETE и выполняем этот запрос. Внимание стоит обратить на секцию except — здесь мы обрабатываем исключение. Если в момент выполнения запроса произошло исключение и это исключение имеет тип EOAuthException, то мы выводим в Memo информацию полученную от Google. Проверить какая информацию содержится в EOAuthException мы можем прямо сейчас. Запускаем приложение, сразу переходим на вкладку «Работа с API» и задаем следующие параметры запроса:

  • Метод запроса — GET
  • URL = https://www.googleapis.com/drive/v2/files

То есть пробуем получить список файлов в аккаунте Google.Диск. Жмем «Выполнить запрос» и получаем в ответ следующее содержимое в Memo:

-------------Error-------------
Exception: HTTP/1.1 401 Unauthorized
ServerMessage: Invalid Credentials
Domain: global
Reason: authError
LocationType: header
Location: Authorization
-------------ErrorMessage-------------
{"error": {"errors": [{"domain": "global","reason": "authError","message": "Invalid Credentials", "locationType": "header","location": "Authorization"},{"domain": "global",
 "reason": "authError", "message": "Invalid Credentials","locationType": "header",
 "location": "Authorization"}], "code": 401,"message": "Invalid Credentials"}}

О чем нам говорит эта информация? В сообщение об ошибке Google нам передал следующую информацию:

  1. Ошибка HTTP с кодом 401 — не авторизованный запрос («HTTP/1.1 401 Unauthorized«)
  2. У нас не хватает прав для доступа к ресурсу API («Invalid Credentials»)
  3. Причина нехватки прав — отсутствие или неправильное значение заголовка запроса (LocationType: header)
  4. Имя заголовка с ошибкой — Authorization (Location: Authorization)

Как видите, используя эту информацию мы можем легко определить причину по которой произошла ошибка доступа к API.

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

application_3
То есть я хочу получить полный доступ к четырем API: Drive API, Calendar API, Tasks API и сервису сокращения ссылок от Google.
Жмем кнопку «Начать процесс авторизации» и в браузере открывается страничка для подтверждения права доступа нашему приложению:

Auth_window

Жмем на этой страничке кнопку «Разрешить» — Google перебросит нас на страницу с кодом подтверждения. Копируем этот код в программу, закрываем браузер и жмем в программе кнопку «Завершить авторизацию». В итоге получим сообщение о том, что доступ получен:

Auth_complete

Теперь можем перейти снова на вкладку «Работа с API» и повторить запрос на получение списка файлов — ошибок не будет, а Google вернет нам в ответ большой JSON-объект, который выведется в Memo «Ответ сервера»:

request

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

Скачать исходник: Исходники —> API онлайн-сервисов —> Google API

Книжная полка

Описание: Рассмотрены практические вопросы по разработке клиент-серверных приложений в среде Delphi 7 и Delphi 2005 с использованием СУБД MS SQL Server 2000, InterBase и Firebird. Приведена информация о теории построения реляционных баз данных и языке SQL. Освещены вопросы эксплуатации и администрирования СУБД.
купить книгу delphi на ЛитРес
Описание: Рассмотрены малоосвещенные вопросы программирования в Delphi. Описаны методы интеграции VCL и API. Показаны внутренние механизмы VCL и приведены примеры вмешательства в эти механизмы. Рассмотрено использование сокетов в Delphi: различные режимы их работы, особенности для протоколов TCP и UDP и др.
купить книгу delphi на ЛитРес
5 1 голос
Рейтинг статьи
уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.
Подписаться
Уведомить о
18 Комментарий
Межтекстовые Отзывы
Посмотреть все комментарии
Кирилл Краснов
06/05/2013 04:05

В Indy есть очень огромный минус (не знаю как во всем), но в почте он совсем не умеет работать в заголовках с кириллицей… Именно поэтому ушел с него в свое время на synapse и ics.

zdm
zdm
02/06/2013 00:20
Ответить на  Кирилл Краснов

Это было давно и срывается сказать неправда, хотя это правда. Но все поменялось и уже, опять-же давно, в лучшую сторону к кириллице… Другой вопрос, что не цепляет Delphi, новые версии, ну ни чем :( Раньше, превосходя все невзгоды (особенно касаемо доп. компонент) не мог дождаться новых версий, после 2010, XE как-то… ну чистое ИМХО, не судите строго, все… стоп… не цепляет. Ну остановился на XE2, 3 и 4 даже нет желания и ставить :(

zdm
zdm
02/06/2013 00:23

Влад, извини за офтоп, нахлынуло что-то :)

Stark
Stark
15/10/2013 19:59

Интересно а с API SkyDrive работать будет? Там вроде тоже OAuth 2.0

Сергей
Сергей
10/03/2014 16:11

Спасибо за Ваши статьи, очень полезно. У меня вопрос про описываемый здесь компонент.
Пытаюсь с помощью его общаться с Google Drive. Аутентификация прошла, список файлов получил и разобрал, для нужного файла нашел downloadUrl. Пробую получить сам файл так:
Response:=TStream.Create;
OAuthClient1.Get(‘здесь downloadURL’, Response);
Получаю ошибку «TStream.Seek not implemented». Что я делаю не так? Какие-то свойства OAuthClient не правильно установил?

Сергей
Сергей
10/03/2014 18:16
Ответить на  Сергей

Сорри! Ошибка-то детская, аж стыдно — какой к чёрту TStream! Исправил.
Есть ещё вопросы, уже более теоретические:
1) downloadUrl значится в описании как «Short lived download URL for the file.» Насколько буквально следует понимать этот «short»? Следует ли обновлять метаданные файла непосредственно перед попыткой скачать файл?
2) Как в реальном приложении следует реализовывать RefreshToken? По таймеру (как часто?) проверять жив/нет и вызывать если не жив? И вообще RefreshToken следует вызывать когда токен уже стал недействителен или заранее?

Сергей Тарзан
29/01/2016 17:03

OAuth 2.0…
Хорошо, что продумана защита. Только вот есть одно «но».
Я делаю программу для себя, для работы с гугл диском. Как же мне сделать, чтобы постоянно не открывалась страничка в браузере?

Сергей Тарзан
29/01/2016 18:34
Ответить на  Vlad

Спасибо за ответ, Влад! Я уже думал, что посты 3-летней давности забыты:) Хотя данная тема актуальна всегда.

Сергей Тарзан
29/01/2016 19:43
Ответить на  Vlad

Просто мне нужен был гугл диск для проверки пользователей. То есть, моя программа при запуске должна обращаться на диск, где хранится список с пользователями и их параметрами. Сначала я использовал Яндекс диск WebDav. Но обращение к диску можно легко увидеть в снифере и узнать данные авторизации. Я думал, что в гугл диск как-то иначе авторизация проходит. Но что гугл, что яндекс перешли на OAuth 2…
Сейчас я думаю создавать сайт для программы и уже через скрипты получать данные с базы данных сайта. Думаю, так будет и безопаснее, и удобнее:)

Александр
Александр
30/12/2017 19:01

Доброго времени суток, Влад. Хотел спросить у Вас, как у эксперта по GDrive.. Задавал на форуме, но пока тишина.. (( Если я к примеру открыл общий доступ к некому текстовому конфиг. файлику на GoogleDisk, может ли кто либо из пользователей моей программы/игры не просто качнуть этот файл, но и как-то подправить его и выгрузить обратно на сервер, то есть — обновить? Другими словами, можно ли использовать Google Drive (или может аналогичные) как некий общий буфер данных/настроек для многопольз. сетевого взаимодействия, скажем в игре? ( в этом случае вроде как отпадает нужда в отдельном онлайн-сервере, разумеется в простейшем случае.. ) То… Подробнее »