company_banner

Новый REST API Яндекс.Диска и Полигон. А также зачем Диску ещё один API и как мы его делали

    Многие знают, что у Диска уже достаточно давно есть API на WebDAV. Он достаточно узко заточен под работу с файловой структурой, а у его реализации на разных платформах часто бывают некритичные, но не очень приятные недостатки. Поэтому в дополнение к WebDAV мы запускаем REST API, который позволит разработчикам делать всё то же и немного больше.

    Например, при использовании нового API все приложения, которым просто нужно хранить свои файлы в Диске, смогут получать доступ только к своей папке, лежащей в Диске пользователя в папке «Приложения». В WebDAV API сервису для этого придётся получать у пользователя разрешение на запись/чтение всего Диска, а не только конкретной папки.



    В этом посте я хочу рассказывать не о структуре или об операциях, которые умеет выполнять наш API — там всё довольно очевидно — а сразу перейду к интересным вещам: что такое Hypermedia и Machine-readable and Self-describing API, и как мы все это реализовали.

    Hypermedia API


    Мы снабдили наш API гиперссылками, которые связывают его ресурсы между собой. Они позволяют превратить работу клиента из дёрганья захардкоденных URL в перемещение по ссылкам, которые предоставляет API в теле возвращаемых объектов. Мы взяли за основу стандарт HAL, как один из наиболее простых и зрелых стандартов в этой области. В настоящее время HAL имеет драфт RFC-стандарта, и его уже можно встретить в API некоторых крупных компаний.

    Благодаря поддержке HAL клиент понимает, что можно делать с каждым объектом, знает готовый или шаблонизированный в соответствии с RFC 6570 URL и HTTP-метод действия. В свою очередь, разработчики клиентских приложений могут писать меньше кода, тратя на это меньше времени, а этот код становится проще и легче для восприятия. Например, код, выполняющий базовые операции с папками в Диске, не использующий гиперссылки, будет выглядеть примерно так:

    Скрытый текст
    # -*- coding: utf-8 -*-
    import urllib
    import httplib
    import json
    import uritemplate
    
    headers = {'Authorization': '<OAuth токен>'}
    connection = httplib.HTTPSConnection('cloud-api.yandex.net')
    resource_url = '/v1/disk/resources'
    
    def request(method, url, query=None):
        if query:
            qs = urllib.urlencode(query)
            url = '%s?%s' % (url, qs)
        connection.request(method, url, headers=headers)
        resp = connection.getresponse()
        content = resp.read()
        obj = json.loads(content) if content else None
        status = resp.status
        if status == 201:
            # получаем созданный объект
            obj = request(obj['method'], obj['href'])
        return obj
    
    if __name__ == '__main__':
        # создаём папку
        path = '/foo'
        folder = request('PUT', resource_url, {'path': path})
    
        # перемещаем папку и получаем перемещённую
        new_path = '/bar'
        folder = request('POST', '%s/move' % resource_url, {'path': new_path, 'from': path})
    
        # копируем папку и получаем новую папку
        copy_path = '/foobar'
        folder_copy = request('POST', '%s/copy' % resource_url, {'path': copy_path, 'from': new_path})
    
        # удаляем папки
        request('DELETE', resource_url, {'path': new_path})
        request('DELETE', resource_url, {'path': copy_path})
    

    При использовании гиперссылок пропадает необходимость вручную собирать URL и беспокоиться о параметрах запроса и без того известных объекту, над которым выполняется операция:

    Скрытый текст
    # -*- coding: utf-8 -*-
    import urllib
    import httplib
    import json
    import uritemplate
    
    headers = {'Authorization': '<OAuth токен>', 'Accept': 'application/hal+json'}
    connection = httplib.HTTPSConnection('cloud-api.yandex.net')
    resource_url = '/v1/disk/resources?path={path}'
    
    def request(method, url, params=None):
        url = uritemplate.expand(url, params or {})
        connection.request(method, url, headers=headers)
        resp = connection.getresponse()
        content = resp.read()
        obj = json.loads(content) if content else None
        status = resp.status
        if status == 201:
            # получаем созданный объект
            status, obj = request(obj['method'], obj['href'])
        return status, obj
    
    def do(resource, action, params=None):
        link = resource['_links'][action]
        _, obj = request(link['method'], link['href'], params)
        return obj
    
    if __name__ == '__main__':
        # создаём папку
        _, folder = request('PUT', uritemplate.expand(resource_url, {'path': '/foo'}))
    
        # перемещаем папку и получаем перемещённую
        folder = do(folder, 'move', {'path': '/bar'})
    
        # копируем папку и получаем новую папку
        folder_copy = do(folder, 'copy', {'path': '/foobar'})
    
        # удаляем папки
        do(folder, 'delete')
        do(folder_copy, 'delete')
    


    Machine-readable & Self-describing API


    Кроме гипермедиа, мы решили сделать наш API самоописываемым и машиночитаемым. В процессе подготовки мы изучили различные стандарты описания REST API, такие как RAML, WADL, JSON Schema+JSON HyperSchema, IO Docs, Apiary Blueprints, однако окончательный выбор пал на Swagger. Одно из основных преимуществ Swagger заключается в том, что он развивается как стандарт (API Яндекс.Диска поддерживает версию спецификации 1.2, но сейчас уже ведётся разработка версии стандарта 2.0). Он описывает REST API с помощью JSON, достаточно прост для понимания и имеет неплохую экосистему инструментов для работы с описаниями API.

    Swagger-документация охватывает все доступные в API ресурсы и методы их вызова. Для каждого метода ресурса есть описание принимаемых им параметров и структуры возвращаемых объектов.

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

    Полигон


    Благодаря наличию Swagger-документации API мы запустили проект Полигон, который даёт разработчикам возможность, не написав ни строчки кода, отправлять боевые запросы в API. Кроме того, Полигон может служить примером универсального клиента для любого API, поддерживающего Swagger-описание. Наш разработчик Рома Акинфеев подготовил скринкаст, в котором рассказал немного о возможностях Полигона.

    Больше приложений хороших и разных!


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

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

    Мы старались сделать новый API Диска таким, чтобы вам было легко и приятно им пользоваться, меньше штудируя документацию и больше интуитивно осваивая его на практике. Поэтому без лишних слов предлагаем воспользоваться Полигоном и самостоятельно познакомиться с новым REST API Яндекс.Диска.
    Яндекс
    524.92
    Как мы делаем Яндекс
    Share post

    Comments 27

      +11
      Я почти 2 недели назад обращался в поддержку Яндекс.Диска с вопросом, ответили ждите отправиили вопрос специалистам — и тишина. Как в новом API узнать квоту пользователя и его логин? Или вы предлагаете файлы заливать на авось, а если выдалась ошибка после заливки, ну значит места нет.

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

      При тестировании сделал доступ только к каталогу приложения. В результате API выдавало ошибку при попытке просмотреть содержимое корневого каталога, либо создать каталог в корне. Также не создался каталог с именем приложения (по аналогии с dropbox), и нигде в API не написано, как переключать контекст, по аналогии с дропбоксовыми root/sandbox.
        +2
        Добрый день! Коллеги просили принести свои извинения, произошел сбой в коммуникации.

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

        На данный момент квоту и логин пользователя узнать нельзя, однако мы планируем заняться этим вопросом в ближайшее время.

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

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

        При тестировании сделал доступ только к каталогу приложения. В результате API выдавало ошибку при попытке просмотреть содержимое корневого каталога, либо создать каталог в корне. Также не создался каталог с именем приложения (по аналогии с dropbox), и нигде в API не написано, как переключать контекст, по аналогии с дропбоксовыми root/sandbox.

        Шапка приложения создастся при первом обращении к ней по алиасу «app:/». Если ваше приложение получило разрешения только на доступ к папке приложения, то корень Диска пользователя «disk:/» оно просматривать не сможет. В ближайшее время мы опишем это в документации.
          0
          Спасибо за ответ.

          Через app:/ действительно работает, и создается каталог. Но сразу бы хотелось сделать замечание, что если запросить содержимое каталога, то ответ возвращается с полными путями, что имхо не очень правильно. Т.е. например каталог «app:/test» будет выдан, как «disk:/Приложения/Название приложения/test».

          Если уж запросили на уровне приложение (и тем более если приложение имеет доступ только к каталогу приложения), то и не нужно ему показывать полный путь.
          ИМХО было бы удобнее если бы просто менялся путь, типа

          https://cloud-api.yandex.net/v1/disk/resources — работаем со всем диском
          https://cloud-api.yandex.net/v1/app/resources — работаем с каталогом приложения


          В общем ждем тогда квоты и в принципе можно использовать.

          И кстати, раз уж вы делаете аплоад в 2 запроса, то почему бы в первом запросе не указывать размер файла, и опционально MD5/SHA256 (как это сделано в WebDAV), тогда вы сразу сможете проверить есть ли место для файла перед заливкой, а также не является ли он дубликатом.
            0
            Вы абсолютно правы. Сейчас прорабатывается вопрос о том, как и когда мы это сможем сделать.

            У нас ещё очень много всего запланировано, но мы решили сначала запустить базовую версию API, а затем наращивать количество доступных функций. Ваши отзывы и предложения нам очень помогут в планировании этих задач :)
        +4
        И еще добавлю несколько пожеланий. Я использую API Яндекс.Диска для бэкапа, поэтому меня интересует специфика работы с большими файлами. И в принципе WebDAV API всем устраивает кроме отсутствия возможности заливать файлы на Яндекс.Диск по частям (что есть и у Dropbox, и в Google Drive и т.п.), что в случае с большими файлами (а это могут быть гигабайты) доставляет определенный дискомфорт.
        В REST API даже не указано есть ли какие-то ограничения на размер файла?

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

        При использовании гиперссылок пропадает необходимость вручную собирать URL

        А, это чесно говоря вообще непонятно, вы что рассчитываете, что ваш API только вручную используют? Всё равно для работы с API будет делаться какой-то класс в котором все пути прописаны в соответствующих методах. И собрать ссылку, будет занимать значительно меньше времени, чем получить и распарсить ответ API с кучей гиперссылок (представьте себе ответ с содержимым каталога, в котором для каждого файла будет куча однотипных ссылок), и при этом для того чтобы скачать или залить файл на Диск нужно всё равно делать дополнительный запрос, чтобы получить временную ссылку.

        Вот ссылки для одного файла
        Это ссылки для одного файла, а если файлов несколько сотен или тысяч?
        "_links": {
                  "move": {
                    "href": "https://cloud-api.yandex.net/v1/disk/resources/move?from=disk%3A%2F%D0%9E%D0%B1%D0%BE%D0%B8+%D0%B4%D0%BB%D1%8F+%D1%80%D0%B0%D0%B1%D0%BE%D1%87%D0%B5%D0%B3%D0%BE+%D1%81%D1%82%D0%BE%D0%BB%D0%B0.jpg&path={path}",
                    "method": "POST",
                    "templated": true
                  },
                  "trash": {
                    "href": "https://cloud-api.yandex.net/v1/disk/resources?path=disk%3A%2F%D0%9E%D0%B1%D0%BE%D0%B8+%D0%B4%D0%BB%D1%8F+%D1%80%D0%B0%D0%B1%D0%BE%D1%87%D0%B5%D0%B3%D0%BE+%D1%81%D1%82%D0%BE%D0%BB%D0%B0.jpg",
                    "method": "DELETE"
                  },
                  "download": {
                    "href": "https://cloud-api.yandex.net/v1/disk/resources/download?path=disk%3A%2F%D0%9E%D0%B1%D0%BE%D0%B8+%D0%B4%D0%BB%D1%8F+%D1%80%D0%B0%D0%B1%D0%BE%D1%87%D0%B5%D0%B3%D0%BE+%D1%81%D1%82%D0%BE%D0%BB%D0%B0.jpg",
                    "method": "GET"
                  },
                  "self": {
                    "href": "https://cloud-api.yandex.net/v1/disk/resources?path=disk%3A%2F%D0%9E%D0%B1%D0%BE%D0%B8+%D0%B4%D0%BB%D1%8F+%D1%80%D0%B0%D0%B1%D0%BE%D1%87%D0%B5%D0%B3%D0%BE+%D1%81%D1%82%D0%BE%D0%BB%D0%B0.jpg",
                    "method": "GET"
                  },
                  "copy": {
                    "href": "https://cloud-api.yandex.net/v1/disk/resources/copy?from=disk%3A%2F%D0%9E%D0%B1%D0%BE%D0%B8+%D0%B4%D0%BB%D1%8F+%D1%80%D0%B0%D0%B1%D0%BE%D1%87%D0%B5%D0%B3%D0%BE+%D1%81%D1%82%D0%BE%D0%BB%D0%B0.jpg&path={path}",
                    "method": "POST",
                    "templated": true
                  },
                  "upload": {
                    "href": "https://cloud-api.yandex.net/v1/disk/resources/upload?path=disk%3A%2F%D0%9E%D0%B1%D0%BE%D0%B8+%D0%B4%D0%BB%D1%8F+%D1%80%D0%B0%D0%B1%D0%BE%D1%87%D0%B5%D0%B3%D0%BE+%D1%81%D1%82%D0%BE%D0%BB%D0%B0.jpg",
                    "method": "GET"
                  },
                  "delete": {
                    "href": "https://cloud-api.yandex.net/v1/disk/resources?path=disk%3A%2F%D0%9E%D0%B1%D0%BE%D0%B8+%D0%B4%D0%BB%D1%8F+%D1%80%D0%B0%D0%B1%D0%BE%D1%87%D0%B5%D0%B3%D0%BE+%D1%81%D1%82%D0%BE%D0%BB%D0%B0.jpg&permanently=True",
                    "method": "DELETE"
                  }
        

        Но хорошо хоть это сделали опциональным.
          0
          Добрый день, гипермедиа контроллы это, как вы правильно заметили, дополнительная опция, которая может быть полезна, но пользоваться ей совершенно не обязательно.
          +1
          Вопрос, который наверное интересует многих — когда будет клиент то под linux?
            –1
            Добрый день, он уже есть disk.yandex.ru/download/
              0
              Ну как то gentoo и rpm плохо сочетаются. хотя бы исходники для сборки
                0
                Мы прорабатываем с командой вопрос о том, чтобы выложить tgz, из которого вы сможете собрать пакеты для gentoo
                0
                Если бинарный пакет, то уже сделали
            0
            Есть ли возможность получить список файлов в директории отфильтровав по типу на стороне API? Например, получить только *.jpg или только *.mov?
              0
              Добрый день, в данный момент REST API не поддерживает данную функциональность. Спасибо за предложение, мы обсудим этот вопрос с командой.
              +1
              А можно полюбопытствовать, почему клиент Я.Диска для Macos весит в *десятки* раз больше, чем для Win/Lin? Что туда засунули?
                +1
                В MacOSX всегда так было. Там же в приложение пихают практически все зависимости. Ну и наверное hi-res изображения, без которых в MacOSX лучше не запускаться)
                0
                А еще хотелось бы хуки на внешние урлы, по настраиваимым (через api) событиям.
                  0
                  Спасибо, это хорошая идея, мы подумаем с командой как ее реализовать
                  0
                  Очень круто, что к пользователям Swagger добавился Яндекс. Было бы совсем замечательно, если бы кто-то из ваших разработчиков API добавился в рабочую группу Swagger 2.0 — docs.google.com/forms/d/1vV-PkgkoRD7vASlPolbRH7W7fkllHG4Ce65TIlC43hI/viewform :) Думаю, вам есть, что сказать.
                    0
                    Спасибо за информацию:) Мы были в рабочей группе с самого начала. Но пока не принимаем активного участия в разработке, а лишь следим за процессом.
                    0
                    По поводу Полигона: интересно узнать, как вы локализовали (хоть и частично) Swagger UI, ибо нативной поддержки переводов там нет. Есть задача на гитхабе — github.com/wordnik/swagger-ui/issues/472, там пишут, что поддержка планируется, а если вы уже что-то сделали в этом направлении, то здорово было бы этим поделиться :)
                      0
                      Надеюсь вы всё ещё следите за этой публикацией.
                      Такой вопрос. Нужна возможность получить статический URL для расшаренной картинки которая хранится на диске. Не публичную ссылку, тут всё понятно, а URL самой картинки, чтобы встроить потом тэгом IMG в HTML.
                      Внятного способа это сделать не нашёл. Из хаков:
                      1) превью — не подходит потому что требует авторизации
                      2) ссылка на скачивание — в принципе подходит, но я смотрю они генерируются всё время разные, в связи с чем вопрос: эти ссылки на скачивание постоянные или перестают действовать через какое-то время?
                      Если ссылки на скачивание вечные, то вопрос закрыт, этот вариант меня в принципе устраивает. Если нет, то нет ли какого-то другого способа?
                      Или лучше вообще Яндекс.Диск для таких целей не использовать, а использовать Яндекс.Фотки например?
                        0
                        Привет!
                        Диск не предназначен для хостинга файлов, поэтому способов сделать это мы не предусматривали:) Да, можно попробовать использовать Яндекс.Фотки.
                          0
                          Алена, но это ведь вполне рабочий usecase, появится ли метод получения списка ссылок для скачивания всех файлов расположенных по входному пути? У вас же работают лучшие из лучших в России, неужели ничего нельзя придумать?
                        0
                        Привет, такой вопрос:

                        Как узнать есть ли ограничение на одновременный запрос к диску на закачку и скачивание файлов по ссылке downloader.disk.yandex.ru/disk*
                        Спасибо
                          0
                          Добрый день! Изучаю API Диска на предмет показа внутри своей веб-системы потокового видео с диска. Вижу, что ваш веб-клиент использует некое, судя по адресам, публичное API, например, yadi.sk/public-api-desktop/get-video-streams. Но не могу найти документацию на него. Есть ли она?

                          Или может быть все-таки есть некий способ сделать встраиваемый виджет для видео с Я.Диска?
                            0
                            elcoyot
                            Каким образом можно обратится в поддержку с фичреквестом? Возможно ли это сделать в принципе?

                            Only users with full accounts can post comments. Log in, please.