Вообще этот пост первоначально задумывался как пост про новый 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:
- Безопасность. Это первый и главный плюс OAuth, который легко перечеркнет все его недостатки по сравнению с тем же ClientLogin. Если использовать OAuth именно так как того требует тот же Google, то пользователь никогда и ни при каких обстоятельствах не предоставит вашей программе свои логин и пароль от аккаунта.
- Контроль со стороны разработчика. Используя OAuth я всегда, в любой момент времени могу сказать с какими API я могу работать, а с какими нет, сколько времени осталось «жить» моему ключу доступа и т.д. Могу программно «отключить» пользователя от использования API и т.д.
- Единый ключ на доступ ко всем API. Используя ClientLogin для доступа к нескольким API мне приходилось каждый раз авторизовывать пользователя по новой. Конечно, сам процесс простой как три копейки, но тем не менее. С OAuth я имею возможность за один запрос получить доступ хоть к сотне API — лишь бы они поддерживали OAuth.
- «Вечный» ключ доступа к API. Один раз получив разрешение на доступ к данным я могу практически бесконечно обновлять ключ доступ без участия пользователя. До тех пор пока пользователь не зайдет на специальную страничку с разрешениями и не «отрубит» приложение, либо пока я сам не обнулю ключ.
Вместе со всеми нововведениями Google также ненавязчиво приучает разработчиков платить за услуги доступа к API. Уже сейчас в Консоле API можно найти несколько API доступ к которым осуществляется только за деньги. О том хорошо ли это или плохо (монетизация Гуглом своих API) можно спорить, но от этого никуда не деться.
В итоге, относительно использования Google API вырисовывается следующая картина:
- XML уходит в прошлое (и пёс с ним), а на его место выходит JSON
- ClientLogin также выводится из работы, а на его место выходит OAuth
- Часть API частично или полностью монетизируются.
На последний пункт мне пока пофиг. Мое дело написать код, а кто будет платить за доступ — это уже дело второе. Другое дело, что мне дает Delphi для того, чтобы разработать необходимые классы для работы с API? А дает нам Delphi…все, что необходимо и даже чуть больше.
Delphi для работы с Google API
Итак, судя по тому, что сказано выше, для работы с Google API нам потребуется не так уж и много:
- Библиотека для работы с HTTP/HTTPS
- Библиотека для работы с JSON
Как вы понимаете, обе библиотеки уже давным давно присутствуют в Delphi и вполне хорошо работают: Indy и модули для работы с JSON — DBXJson, DBXJsonReflect и т.д.
К Indy можно относиться по разному (сам я, где возможно, использую Synapse), но сейчас, по крайней мере, использование Indy дает одно неплохое такое преимущество по сравнению с другими аналогичными библиотеками, а именно: с Indy код будет прекрасно собираться и работать как под Windows, так и под Mac OS и, по-моему даже в iOS (могу ошибаться т.к. проверить сейчас негде).
В итоге, сегодня, используя только «родные» библиотеки и классы Delphi мы можем без проблем разработать компонент который:
- Будет прекрасно работать как с JSON, так и с XML (если потребуется)
- Будет работать под любой из поддерживаемых ОС: Windows, Mac OS, iOS
И на этой оптимистической ноте можно переходить к основному содержанию статьи.
Компонент OAuthClient для работы с API Google
Итак, для чего можно использовать компонент? За основу для компонента был взят класс для работы с Google OAuth о котором я рассказывал в блоге некоторое время назад, но со следующими изменениями:
- Теперь это стал полноценный компонент, который можно установить в палитру компонентов в IDE
- Вместо Synapse для работы с HTTP(S) теперь используется Indy
- В случае, если сервер Google вернул ошибку (например, вы не указали важный параметр запроса или пропустили заголовок авторизации и т.д.) генерируется свой тип исключений EOauthException, с помощью которого можно более детально проанализировать ошибку — определить местоположение ошибки в запросе (заголовок, параметр URL и т.д.), какое сообщение прислал Google и т.д.
- Компонент поддерживает все методы работы с протоколом OAuth — получение ключа доступа, обновление, проверка и обнуление ключа.
- Компонент может сохранять/загружать данные сессии в файл. Данные сохраняются в JSON-формате.
Теперь рассмотрим этот компонент более подробно.
Свойства компонента
property RedirectURI: string;
Это свойство определяет URI на который сервер Google будет осуществлять редирект после того как пользователь подтвердит доступ к данным в аккаунте для нашего приложения. Вообще, это свойство может содержать любой URI, но для desktop-приложений Google рекомендует использовать одно из двух значений:
urn:ietf:wg:oauth:2.0:oob
Это значение следует использовать, если Вы хотите, чтобы Google делал редирект на свою страницу с кодом подтверждения для пользователя. Как только пользователь подтвердит доступ к данным своего аккаунта его «перебросит» вот на такую страничку в браузере:
После чего пользователь должен скопировать код со странички в нашу программу и завершить процесс авторизации.
Это значение URI удобно использовать в том случае, если вы будете использовать браузер по-умолчанию, например, Chrome для открытия страницы с информацией о нашем приложении.
http://localhost
Это значение указывает на то, что Google должен вернуть код подтверждения в параметре запроса, не открывая при этом свою страницу. То есть, после подтверждения доступа, пользователь увидит примерно такую картину:
Это значение можно использовать, например, в случае, если ваше приложение использует свой браузер (тот же TWebBrowser) и Вы можете отловить момент редиректа, чтобы скопировать из URL необходимый код автоматически без участия пользователя.
property ClientID: string; property ClientSecret: string;
Эти свойства должны содержать ключи ClientID и ClientSecret вашего приложения, которые можно получить в консоли Google. Для того, чтобы получить ClientID и ClientSecret необходимо выполнить следующие действия:
1. Заходим в Консоль, создаем новый проект и включаем необходимые API на странице Services:
2. Переходим на вкладку «API Access» Для нового проекта эта страничка будет выглядеть вот так:
Здесь мы можем создать всякие разные ключи доступа — для iOS-приложений Android-приложений, для Web-приложений и т.д.
3. Жмем большую синюю кнопку «Create an OAuth 2.0. ID…»
При этом откроется небольшое окно в котором мы должны ввести информацию о нашем будущем приложении: название, адрес домашней страницы, и лого. Я ввел следующую информацию:
Жмем «Next» и Google попросит нас выбрать тип приложения. Здесь ВАЖНО указать следующие параметры:
- Application Type — Installed
- Installed application type — other
То есть в браузере окно должно выглядеть вот так:
Теперь остается нажать кнопку «Create client ID» и Google создаст нам новый идентификатор, а страница «API Access» станет выглядеть вот так:
4. Копируем полученные значения ClientID и ClientSecret в наше приложение
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.
Вначале напишем две вспомогательные процедуры: первая будет задавать свойства компонента 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 нам передал следующую информацию:
- Ошибка HTTP с кодом 401 — не авторизованный запрос («HTTP/1.1 401 Unauthorized«)
- У нас не хватает прав для доступа к ресурсу API («Invalid Credentials»)
- Причина нехватки прав — отсутствие или неправильное значение заголовка запроса (LocationType: header)
- Имя заголовка с ошибкой — Authorization (Location: Authorization)
Как видите, используя эту информацию мы можем легко определить причину по которой произошла ошибка доступа к API.
Теперь посмотрим на работу программы в целом. Снова запускаем приложения, но теперь уже на первой вкладке задаем необходимые свойства компонента. У меня получился такой вид вкладки:
То есть я хочу получить полный доступ к четырем API: Drive API, Calendar API, Tasks API и сервису сокращения ссылок от Google.
Жмем кнопку «Начать процесс авторизации» и в браузере открывается страничка для подтверждения права доступа нашему приложению:
Жмем на этой страничке кнопку «Разрешить» — Google перебросит нас на страницу с кодом подтверждения. Копируем этот код в программу, закрываем браузер и жмем в программе кнопку «Завершить авторизацию». В итоге получим сообщение о том, что доступ получен:
Теперь можем перейти снова на вкладку «Работа с API» и повторить запрос на получение списка файлов — ошибок не будет, а Google вернет нам в ответ большой JSON-объект, который выведется в Memo «Ответ сервера»:
Таким образом, получив ключ доступа мы можем тестировать и другие запросы к API. Например, можно выполнить POST запрос к API сокращения ссылок, запросить или обновить мета-данные документов в Google.Drive и т.д.
Книжная полка
Описание: Рассмотрены практические вопросы по разработке клиент-серверных приложений в среде Delphi 7 и Delphi 2005 с использованием СУБД MS SQL Server 2000, InterBase и Firebird. Приведена информация о теории построения реляционных баз данных и языке SQL. Освещены вопросы эксплуатации и администрирования СУБД.
|
||
Название: О чем не пишут в книгах по Delphi
Описание: Рассмотрены малоосвещенные вопросы программирования в Delphi. Описаны методы интеграции VCL и API. Показаны внутренние механизмы VCL и приведены примеры вмешательства в эти механизмы. Рассмотрено использование сокетов в Delphi: различные режимы их работы, особенности для протоколов TCP и UDP и др.
|
В Indy есть очень огромный минус (не знаю как во всем), но в почте он совсем не умеет работать в заголовках с кириллицей… Именно поэтому ушел с него в свое время на synapse и ics.
Кирилл Краснов, было дело фиксили Indy по части работы с почтой. Но для Google API Indy вполне годится, т.к. по большому счёту требуется всего 4-5 методов от TidHTTP, которые уже вылизаны разработчиками до почти идеальной чистоты :)
Это было давно и срывается сказать неправда, хотя это правда. Но все поменялось и уже, опять-же давно, в лучшую сторону к кириллице… Другой вопрос, что не цепляет Delphi, новые версии, ну ни чем :( Раньше, превосходя все невзгоды (особенно касаемо доп. компонент) не мог дождаться новых версий, после 2010, XE как-то… ну чистое ИМХО, не судите строго, все… стоп… не цепляет. Ну остановился на XE2, 3 и 4 даже нет желания и ставить :(
Влад, извини за офтоп, нахлынуло что-то :)
Бывает :) Я и сам на XE4 не спешу особенно, хотя уже давно есть ключ
Интересно а с API SkyDrive работать будет? Там вроде тоже OAuth 2.0
Этот компонент точно нет. А вот REST Client Library из Delphi XE5 — точно будет. Проверял
Спасибо за Ваши статьи, очень полезно. У меня вопрос про описываемый здесь компонент.
Пытаюсь с помощью его общаться с Google Drive. Аутентификация прошла, список файлов получил и разобрал, для нужного файла нашел downloadUrl. Пробую получить сам файл так:
Response:=TStream.Create;
OAuthClient1.Get(‘здесь downloadURL’, Response);
Получаю ошибку «TStream.Seek not implemented». Что я делаю не так? Какие-то свойства OAuthClient не правильно установил?
Сорри! Ошибка-то детская, аж стыдно — какой к чёрту TStream! Исправил.
Есть ещё вопросы, уже более теоретические:
1) downloadUrl значится в описании как «Short lived download URL for the file.» Насколько буквально следует понимать этот «short»? Следует ли обновлять метаданные файла непосредственно перед попыткой скачать файл?
2) Как в реальном приложении следует реализовывать RefreshToken? По таймеру (как часто?) проверять жив/нет и вызывать если не жив? И вообще RefreshToken следует вызывать когда токен уже стал недействителен или заранее?
По поводу того на сколько downloadUrl «short lived» подсказать не могу, т.к. специально не проверяли. Могу только сказать, что за уже почти год использования этого типа url не было ни одной претензии от пользователей на то, что какой-либо файл не скачался или недокачался. RefreshToken можно использовать по всякому. Мы его используем каждый раз как нам необходимо провести синхронизацию данных, т.е. перед каждой синхронизацией используем RefreshToken для обновления основного токена доступа к API. Если по какой-либо причине токен не обновится (хотя такого ещё ни разу за год использования API не было), то на этот случай имеется возможность повторной авторизации. А можно… Подробнее »
OAuth 2.0…
Хорошо, что продумана защита. Только вот есть одно «но».
Я делаю программу для себя, для работы с гугл диском. Как же мне сделать, чтобы постоянно не открывалась страничка в браузере?
Следуя в точности требованиям протокола — никак. Иначе — качать страничку в фоне, парсить, ручками формировать необходимые запросы к сереверу и т.д. В общем, я бы за такое не взялся :) Ну и, кроме этого, Access Token можно _обновлять_, не прибегая к загрузке страницы в браузере. Если не ошибаюсь, в API этот метод назывался RefreshToken.
Спасибо за ответ, Влад! Я уже думал, что посты 3-летней давности забыты:) Хотя данная тема актуальна всегда.
Не за что) Хотя я уже больше года не касался Google API, всё-таки что-то помню по работе с ним.
Просто мне нужен был гугл диск для проверки пользователей. То есть, моя программа при запуске должна обращаться на диск, где хранится список с пользователями и их параметрами. Сначала я использовал Яндекс диск WebDav. Но обращение к диску можно легко увидеть в снифере и узнать данные авторизации. Я думал, что в гугл диск как-то иначе авторизация проходит. Но что гугл, что яндекс перешли на OAuth 2…
Сейчас я думаю создавать сайт для программы и уже через скрипты получать данные с базы данных сайта. Думаю, так будет и безопаснее, и удобнее:)
У Яндекса, по-моему, вообще бессрочный Access Token для Диска выдается. Один раз авторизуешься, запоминаешь ключ и все.
Доброго времени суток, Влад. Хотел спросить у Вас, как у эксперта по GDrive.. Задавал на форуме, но пока тишина.. (( Если я к примеру открыл общий доступ к некому текстовому конфиг. файлику на GoogleDisk, может ли кто либо из пользователей моей программы/игры не просто качнуть этот файл, но и как-то подправить его и выгрузить обратно на сервер, то есть — обновить? Другими словами, можно ли использовать Google Drive (или может аналогичные) как некий общий буфер данных/настроек для многопольз. сетевого взаимодействия, скажем в игре? ( в этом случае вроде как отпадает нужда в отдельном онлайн-сервере, разумеется в простейшем случае.. ) То… Подробнее »
Приветствую. Я с Google Disk уже давно не работа, поэтому о последних изменениях не в курсе, но, насколько помню, файл открытый на чтение/запись мог менять любой, имеющий к файлу доступ пользователь