Synapse: работа с WebDAV на примере Яндекс.Диска.Источник: webdelphi webdelphi
Недавно Яндекс объявил о запуске свого облачного сервиса под названием Яндекс.Диск. А буквально пару дней назад и Google запустил свой Google Drive. У меня была возможность поработать с обоими этими сервисами и оценить их удобство/полезность для себя. Надо сказать, что при выборе тех или иных онлайн-сервисов я, обычно, выбирал сервисы от Google (Календарь, Почта, соц.сеть и т.д.), но в случае с облачными сервисами я пока больше склоняюсь к использованию сервиса от Яндекс. По большей части такой выбор был сделан на основе изучения двух документов: API Яндекс.Диска и SDK Google Drive. Google в очередной раз "порадовал" ограничениями на использование API и набором методов API - есть методы добавления, редактирования и получения данных по файлам, но почему-то отсутствуют методы для удаления...странно как-то. Можно было бы привести ещё несколько причин моего выбора, но к статье эти причины не имеют никакого отношения. Яндекс в плане API оказался более дружелюбным к разработчикам - никаких ограничений, доступ к методам через Basic- или OAuth-аутентификацию (по выбору) и никаких "наворотов" в API - только методы и возможности WebDAV. Надеюсь, что Яндекс после окончания бетта-тестов Диска не переделает API и то, что будет рассказано и показано ниже будет работать ещё долго. Итак, сегодня продолжим речь о замечательной библиотеке Synapse и посмотрим как можно работать с протоколом WebDAV на примере API Яндекс.Диска.
А раз WebDAV - это надстройка над HTTP(S), то нам никто не мешает использовать для этого давно уже нам известный класс THTTPSend. Все, что от нас требуется при подготовке к работе - это немного вспомнить про работу с https в Synapse, ну и, если необходимо, освежить в памяти работу с GZip. Так как WebDAV передает всю мета-информацию по документам в виде XML, то дополнительно нам может потребоваться какая-нибудь библиотека для работы с XML - обычный MSXML, NativeXML и т.д.
Теперь приступим к работе. Создадим в Delphi проект VCL Application, подключим в uses модули Synapse:
И на главной форме разместим следующие компоненты (см. рисунок): Для доступа к методам API будем использовать простую Basic-аутентификацию. Так как в каждый запрос мы должны будем вставлять заголовок аутентификации, то напишем свой небольшой класс для работы с API. В принципе, можно было бы обойтись пока и без класса, но лишним, думаю, он не будет. Итак, заготовка класса для работы с WebDAV через THTTPSend будет такой: type TWebDAVSend = class private FHTTP : THTTPSend; FToken: AnsiString; FPassword: string; FLogin: string; procedure SetLogin(const Value: string); procedure SetPassword(const Value: string); procedure SetToken; public constructor Create; destructor Destroy; override; property Login: string read FLogin write SetLogin; property Password: string read FPassword write SetPassword; end; { TWebDAVSend } constructor TWebDAVSend.Create; begin inherited; FHTTP:=THTTPSend.Create; end; destructor TWebDAVSend.Destroy; begin FHTTP.Free; inherited; end; procedure TWebDAVSend.SetToken; begin FToken:=EncodeBase64(FLogin+':'+FPassword); end; procedure TWebDAVSend.SetLogin(const Value: string); begin FLogin := Value; SetToken; end; procedure TWebDAVSend.SetPassword(const Value: string); begin FPassword := Value; SetToken; end; Теперь попробуем реализовать несколько методов WebDAV. Первое, что нам необходимо - это определить какие каталоги имеются в Яндекс.Диске. Для этого нам надо реализовать в классе метод API PROPFIND. Согласно документации API, набор файлов и каталогов, свойства которых должны содержаться в ответе, определяется заголовком Depth со следующими поддерживаемыми значениями:
В результате запроса сервер ответит нам XML-документом, содержащим необходимые нам свойства. Реализация PROPFIND в нашем классе будет такой: function TWebDAVSend.PROPFIND(Depth: integer; const Element: String): string; begin with FHTTP do begin Headers.Clear; Document.Clear; Headers.Add('Authorization: Basic ' + FToken); Headers.Add('Depth: ' + IntToStr(Depth)); Headers.Add('Accept: */*'); if HTTPMethod('PROPFIND', GetRequestURL(Element)) then result := ReadStrFromStream(Document, Document.Size) else raise Exception.Create(rsPropfindError+' '+ResultString); end; end; Рассмотрим работы этой функции. Вначале мы очищаем заголовки и тело от данных, полученных в предыдущем запросе, если таковой был. После этого вставляем заголовок аутентификации, указываем "глубину просмотра" в заголовке Depth. Затем, выполняем запрос PROPFIND на сервер и здесь вам встречается неизвестная функция GetRequestURL.
И функция вернет мне URL, который гарантированно примет сервер. GetRequestURL выглядит следующим образом: const cWebDAVServer = 'https://webdav.yandex.ru/'; function TWebDAVSend.GetRequestURL(const Element: string): string; var URI: string; begin URI:=Element; if URI[1]='/' then Delete(URI,1,1); Result:=cWebDAVServer+EncodeUTF8URI(URI); end; Здесь, опять же, встречается ещё одна непонятная функция - EncodeUTF8URI. Эта функция проводит кодирование URI, который может содержать символы в кодировке UTF-8. Выглядит функция так: function TWebDAVSend.EncodeUTF8URI(const URI: string): string; var i: integer; Char: AnsiChar; begin result := ''; for i := 1 to length(URI) do begin if not(URI[i] in URLFullSpecialChar) then begin for Char in UTF8String(URI[i]) do Result:=Result+'%'+IntToHex(Ord(Char), 2) end else Result:=Result+URI[i]; end; end; URLFullSpecialChar - это множество, которое описано в модуле synacode.pas Synapse: URLFullSpecialChar: TSpecials = [';', '/', '?', ':', '@', '=', '&', '#', '+']; Вполне возможно, что можно было бы как-нибудь обойтись методами Synapse типа EncodeURLElement, но для строки "Библиотека" эта функция выдавала такую строку:
Пример успешно выполненного запроса PROPFIND показан на рисунке ниже: Пример с неправильно заданным элементом (сервер возвращает код 200 и описание ошибки): Про метод PROPFIND стоит также добавить, что имя каталога на сервере может не совпадать с представлением этого каталога в URL. Например, каталог "Музыка" в URL представляется как "Music". Чтобы понять как должен выглядеть URL для получения свойств какого-либо каталога необходимо смотреть на свойство 'href' в XML-документе. Список всех свойств, поддерживаемых в рамках протокола WebDAV приведен в разделе описания протокола DAV Properties. Аналогично методу PROPFIND можно реализовать и другие методы для работы с WebDAV в Synapse. Все методы рассматривать, думаю, смысла нет - алгоритм работы практически неизменный: вставили заголовки аутентификации, собрали URL, отправили запрос, обработали ответ. Но, для полноты картины, рассмотрим ещё один метод WebDAV - MKCOL. В отличие от предыдущего, этот метод не возвращает ничего в теле ответа, а об успешности выполнения запроса можно судить по ResulCode. Итак, чтобы создать на сервере новую коллекцию, мы будем использовать в нашем классе такую функцию: function TWebDAVSend.MKCOL(const ElementPath: string): boolean; begin Result:=False; with FHTTP do begin Headers.Clear; Document.Clear; Headers.Add('Authorization: Basic ' + FToken); Headers.Add('Accept: */*'); if HTTPMethod('MKCOL', GetRequestURL(ElementPath)) then begin Result:=ResultCode=201; if not Result then raise Exception.Create(IntToStr(ResultCode)+' '+ResultString); end else raise Exception.Create(rsPropfindError+' '+ResultString); end; end; Пример использования MKCOL: MKCOL('Documents/Новая папка с документами'); В результате в каталоге Documents будет создан новый с названием "Новая папка с документами". Следует отметить, что согласно протоколу WebDAV, в результате одного запроса может быть создан только один каталог. Если приложение отправляет запрос о создании каталога a/b/c/, а в каталоге a/ нет каталога b/, то сервис не создает каталог b/, а отвечает c кодом 409 Conflict. Вот, пожалуй, кратко о том как можно реализовать работу с WebDAV в Synapse и использовать API Яндекс.Диска в своих Delphi-приложениях. Исходник проекта, рассмотренного в статье Вы всегда сможете скачать со страницы с исходниками (раздел по Delphi XE2) и, при необходимости, дописать реализацию других методов API. |