Многие знают, что у Диска уже достаточно давно есть 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 токен>', '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')
При использовании гиперссылок пропадает необходимость вручную собирать URL и беспокоиться о параметрах запроса и без того известных объекту, над которым выполняется операция:
# -*- 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})
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 Яндекс.Диска.
Автор: vladimirrusinov