Удобство работы с любым API во многом зависит от того, как написана и оформлена его документация. Cейчас мы ведём работу по стандартизации и унификации описания всех наших API, и вопросы документирования для нас особенно актуальны.
После долгих поисков мы решили оформлять документацию в формате RAML. Так называется специализированный язык для описания REST API. О его возможностях и преимуществах мы расскажем в этой статье.
Почему RAML
Как нужно документировать API? Вопрос этот не так прост, как может показаться на первый взгляд.
Первый и самый простой вариант, который приходит на ум — представить описание к API в виде обычного текстового документа. Так делают очень многие (в том числе и очень известные компании). Мы тоже не раз пользовались этим способом. При всей своей простоте он обладает следующими недостатками:
- текстовую документацию сложно поддерживать в актуальном состоянии;
- зачастую словесные описания API оказываются недостаточно наглядными;
- сфера использования «словесной» документации очень ограничена (например, на её основе нельзя сгенерировать интерактивную тестовую страницу).
Чтобы упростить процесс документирования, можно использовать специализированные инструменты и сервисы. Как правило, они генерируют документацию на основе описания в некотором стандартизованном формате — обычно это JSON или Markdown.
Ни один из этих форматов для написания документации не подходит. JSОN был изначально создан для обмена данными в вебе. При использовании его для других целей поневоле приходится прибегать к помощи «костылей» — например, кастомных полей, начинающихся со знака $. Кроме того, составлять описания в формате JSON вручную — дело достаточно рутинное и утомительное (в особенности если речь идёт об описаниях большого размера).
На описанные выше трудности обращали внимание многие пользователи популярного инструмента Swagger. Вскоре разработчики Swagger решили упростить работу по написанию спецификаций и создали фирменный редактор с поддержкой формата YAML.
Конечно, YAML гораздо удобнее, чем JSON. Но и его использование сопряжено с определёнными трудностями. Дело в том, что в описаниях API всегда имеются повторяющиеся элементы (например, схема ответа, которая может быть одинаковой для разных типов HTTP-запросов), которые приходится всякий раз прописывать вручную. Вот если бы можно их было раз и навсегда прописать в отдельном файле и ссылаться на него в случае небходимости… Но, увы, пока что такой возможности нет.
Что касается формата Markdown (он используется, например, в API BluePrint), то предназначен в первую очередь для оформления текста, а не для использования в качестве основы для генерирования. Приспособить его под документирование API очень сложно. По этой же причине не привели к каким-либо заметным результатам попытки cоздать формат описания API на базе XML — например, язык WADL (Web Application Desription Language), разработанный компанией Sun Microsystems ещё в 2009 году, но так и не получивший широкого распространения.
Создатели проекта RAML (эта аббревиатура означает RESTful API Modeling Language — язык для моделирования REST API ) предприняли попытку разработать язык, предназначенный исключительно для описания API и исправить недочёты, свойственные другим форматам. Первая версия спецификации RAML была опубликована в 2013 году. Основным разработчиком RAML является компания MuleSoft; в проекте также принимают участие представители таких известных компаний, как Cisco, PayPal, ВoxInc. и других.
Несомненными преимуществами RAML являются:
- простой и логичный синтаксис, основанный на формате YAML;
- поддержка наследования и возможность подключения внешних файлов спецификаций.
Дополнительным плюсом является наличие большого количества конвертеров, парсеров и генераторов интерактивной документации. О некоторых из них мы расскажем ниже, а пока перейдём к обзору особенностей синтаксиса RAML.
Краткое введение в RAML
Структура документа
Файл спецификаций на RAML состоит из следующих структурных элементов:
- вводная часть («шапка»);
- схема безопасности;
- описание профилей;
- описание объектов и методов;
- описание ответов.
Рассмотрим эти элементы подробнее.
Вводная часть
Каждый документ на RAML начинается с вводной части, которая включает четыре обязательных элемента:
- версия RAML;
- имя документа;
- URI, по которому доступен API;
- версия API, описываемая в документации.
Выглядит это так:
#% RAML 0.8 title: Example API baseUri: http://api.example.com/{version} version: v1
Вводная часть может также включать различную дополнительную информацию — например, сведения об используемом протоколе для связи с API:
protocols: [http, https]
Можно во вводной части прописать и метаданные файла документации:
documentation - title: Home content: | API Test Documentation
Схемы безопасности
Чтобы начать работать с любым API, нужно пройти процедуру авторизации. Она может осуществляться разными способами: через OAuth, с помощью токенов, либо посредством простой HTTP-аутентификации. Для описания этой процедуры в RAML используются схемы безопасности (security schemes).
Рассмотрим в качестве примера, как описывается авторизация с использованием протокола OAuth2:
#%RAML 0.8 title: Example API version: 1 baseUri: https://api.example.com/{version} securedBy: [oauth_2_0] securitySchemes: - oauth_2_0: type: OAuth 2.0 describedBy: headers: Authorization: type: string queryParameters: access_token: type: string responses: 401: description: | Bad or expired token. 403: description: | Bad OAuth request settings: authorizationUri: https://example.com/oauth/authorize accessTokenUri: https://example.com/oauth/token authorizationGrants: [ code, token ]
Приведённый фрагмент содержит следующую информацию:
- в параметре type указывается, что в API используется авторизация по протоколу OAuth2;
- далее указывается, что авторизационные данные можно передавать либо в заголовке Authorization, либо в query-параметре access_token;
- после этого следуют возможные коды ответов и их описания;
- в конце раздела, в секции settings указываются URL для авторизации, URL для получения токена, а также необходимые для аутентификации параметры (authorization grants).
Для удобства схемы безопасности можно сохранять в отдельных файлах .raml или .yml, и затем обращаться к ним в случае необходимости:
#%RAML 0.8 title: Example API version: 1 baseUri: https://api.example.com/{version} securedBy: [oauth_2_0] securitySchemes: - oauth_2_0: !include oauth_2_0.yml
Это помогает ускорить процесс документирования, избежать лишних повторений и сделать документацию менее громоздкой.
Почитать более подробно о схемах безопасности и ознакомиться с конкретными примерами можно здесь(раздел Security).
Объекты и методы
Далее перечисляются основные объекты и пути к ним, а также HTTP-методы, которые используются с этими объектами:
/document get: put: post: /{documentId} get: delete:
В приведённом примере описывается API, с помощью которого можно работать с документами. Мы можем скачивать документы на локальную машину (GET), изменять cуществующие документы (PUT) и загружать новые (POST). С каждым отдельным документом ({documentId}) мы можем также выполнять следующие операции: загрузка на локальную машину (GET) и удаление (DELETE).
HTTP-заголовки, используемые с тем или иным методом, описываются при помощи свойства headers, например:
/documents get headers: X-Auth-Token: required: true
Обратите внимание на свойство required: оно указывает, является ли заголовок обязательным (true) или необязательным (false).
В описании объектов и методов могут использоваться многочисленные дополнительные параметры. Рассмотрим следующий пример:
/document /{documentId} uriParameters: id: description: document identification number type: string pattern: ^[a-zA-Z]{2}\-[0-9a-zA-Z]{3}\-\d{2}\-\d{5}$
Здесь мы указываем, что каждый из документов, доступ к которым можно получить через API, имеет собственный идентификационный код в виде строки (type: string), а также описываем формат этого кода с помощью регулярных выражений.
Свойства description, type и pattern можно использовать и в описаниях методов, например:
/documents get: description: Retrieve a list of documents post: description: Add a new document body: application/x-www-form-urlencoded formParameters: id: description: document identification number type: string pattern: [a-zA-Z]{2}\-[0-9a-zA-Z]{3}\-\d{2}\-\d{5}$ name: description: the name of the document type: string required: true author: description: The name of the author type: string required: true
В описании метода POST мы указываем параметры, которые нужно передать в теле запроса для добавления нового документа: ID, имя и имя автора. Каждый из этих параметров является строкой (type: string). Обратите внимание на свойство required: оно указывает, является ли параметр обязательным (true) или необязательным (false).
Для каждого метода можно прописать индивидуальную схему безопасности:
/documents get [securedBy: oauth_2_0]
Query-параметры
Для каждого метода в документации можно указать query-параметры, которые будут использоваться в запросе. Для каждого query-параметра указываются следующие характеристики: имя, тип, описание и пример:
/document: get: queryParameters: author: displayName: Author type: string description: The author's full name example: Ivan Petrov required: false name: displayName: Document Name type: string description: The name of the document example: Delivery contract required: false signingDate: displayName: signingDate type: date description: The date when the document was signed example: 2015-07-15 required: false
Профили
Чтобы избежать лишних повторений в описаниях, в RAML используются профили (traits), которые прописываются во вводной части документа:
#% RAML 0.8 title: Example API baseUri: http://api.example.com/{version} version: v1 traits: - searchable: queryParameters: author: displayName: Author type: string description: The author's full name example: Ivan Petrov required: false name: displayName: Document Name type: string description: The name of the document example: Delivery contract required: false signingDate: displayName: signingDate type: date description: The date when the document was signed example: 2015-07-15 required: false
В дальнейшем к профилю можно обращаться при описании любых методов:
/document: get: is: [searchable]
Более подробно о профилях и особенностях их использования можно почитать в официальной документации (раздел Traits).
Описание ответа
В описании ответа обязательно указывается его код. Также в описание можно добавить схему (schema) — перечисление входящих в ответ параметров и их типов. Можно также привести пример конкретного ответа (example).
/documents: /{documentId}: get: description: Retrieve document information responses: 200: body: application/json: schema | {"$schema": “http://json-schema.org/schema”, "type":"object" "description": "a document" "properties": { "id":{"type":"string"} "name":{"type":"string"} "author":{"type":"string"} "signingDate": {"type":"date"} example: | {"data:" { "id": "DOC3456" "name": "New Delivery Contract" "author": "Ivan Petrov" "signingDate": "2015-05-20" }, "success": true "status": 200 }
Cхемы ответов можно сохранять в отдельных файлах формата .yml или .raml и обращаться к ним в других частях документации:
schemas: - !include document-schema.yaml /articles: get: responses: 200: body: application/json: schema: document
Визуализация и генерация документации
RAML2HTML и другие конвертеры
Несмотря на то, что RAML — формат относительно новый, для него уже разработано достаточное количество конвертеров и парсеров. В качестве примера можно привести ram2htmtl, генерирующий на основе описания в формате RAML статическую HTML-страницу.
Устанавливается он при помощи менеджера пакетов npm:
$ npm -i g raml2html
Чтобы сконвертировать RAML-файл в HTML, достаточно выполнить простую команду:
$ raml2html api.raml > index.html
Поддерживается возможность создания собственных шаблонов для HTML-файлов (подробнее об этом см. в документации на GitHub по ссылке выше).
Из других конвертеров рекомендуем также обратить внимание на RAML2Wiki и RAML2Swagger.
API Designer
Компания Mulesoft (один из активных участников проекта RAML) создала специальный онлайн-инструмент, с помощью которого можно упростить работу по написанию документации и последующему тестированию API. Называется он API Designer.
Чтобы начать им пользоваться, нужно сначала зарегистрироваться на сайте. После этого можно приступать к работе. API designer предоставляет, во-первых, удобный интерактивный редактор для написания документации онлайн, а во-вторых — платформу для тестирования.
Выглядит всё это так:
В правой части страницы автоматически генерируется интерактивная документация. Здесь же можно и протестировать API: для этого достаточно просто развернуть описание нужного запроса и нажать на кнопку Try it.
API Designer позволяет также загружать RAML-файлы с локальной машины. Поддерживается импорт файлов описаний API для Swagger.
Кроме того, API Designer хранит статистику тестовыx запросов к API.
API console
API console — полезный инструмент, разработанный всё той же компанией MuleSoft. С его помощью можно прямо в браузере генерировать документацию к API. Файлы спецификаций можно как загрузить с локальной машины, так и указать ссылку на внешний источник:
В состав API Console входит несколько файлов-образцов, представляющих собой описания API популярных веб-сервисов: Twitter, Instagram, Box.Com, LinkedIn. Мы попробовали сгенерировать на основе одного из низ документацию — выглядит вполне симпатично:
Документация, получаемая на выходе, является интерактивной: в ней можно не только прочитать описание API, но и выполнить тестовые запросы.
Заключение
В этой статье мы рассмотрели основные возможности RAML. Его несомненными преимуществами являются простота и логичность. Надеемся, что в скором будущем RAML получить ещё более широкое распространение и займёт достойное место в ряду инструментов для документирования API.
Если у вас есть вопросы — добро пожаловать в комментарии. Будем также рады, если вы поделитесь собственным опытом использования RAML на практике.
Если вы по тем или иным причинам не может оставлять комментарии здесь — добро пожаловать в наш блог.
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Комментарии (37)
lair
24.08.2015 15:14+1Swagger/Swashbuckle
И в связи с этим возникает вопрос: так чем же, все-таки, RAML принципиально лучше/удобнее?AndreiYemelianov
24.08.2015 15:35+2Чем лучше? Отвечаю по пунктам?
1. Гораздо более простым и логичным синтаксисом описаний.
2. Возможностью расписать в документации гораздо большее количество параметров, чем в том же swagger и, самое главное, сделать это при помощи более простых и удобных конструкций (сравните те же описания схем безопасности или query-параметров).
3. Расширенными (по сравнению с тем же swagger) возможностями использования include'ов и наследования, благодаря чему документация получается более компактной и элегантной.
Единственное, в чём RAML пока что проигрывает Swagger — недостаточное количество разрабатываемых сообществом инструментов. Но это — дело наживное.lair
24.08.2015 15:36-1Для RAML есть инструменты, позволяющие автоматически генерить документацию в этом формате для уже существующего API (желательно — на лету)?
AndreiYemelianov
24.08.2015 15:51+2Насколько мне известно, пока что нет.
lair
24.08.2015 15:55Угу, потому что RAML — это «сначала контракт, потом все остальное». А я вот совершенно не хочу так писать, я хочу иметь (аннотированное) серверное API, по которому генерятся описания в произвольных форматах. И вот тут Swagger работает прекрасно (потому что для него есть соответствующий инструментарий под мою платформу).
(хотя, конечно, в нем встречаются вещи, которые сложно выразить)clickfreak
24.08.2015 22:17+2Т.е. вы не проектируете, а сразу пишете код?
lair
24.08.2015 22:40+2Почему же, я проектирую, просто код — это и есть проект. Code is the documentation, я не хочу думать о том, как содержать их в соответствии друг с другом.
romychs
25.08.2015 12:29+1Согласен. Для проектов на JAX-RS использую maven-javadoc-plugin вместе с swagger-doclet, Он генерирует swagger-документацию по JavaDoc. Помимо обычных аннотаций используются специфические дополнительные аннотации, которые не мешают читать код, но помогают уточнить документацию. Дополнительный бонус — приучает писать полноценные комментарии :)
centur
24.08.2015 16:51+1Учитывая что эти форматы чаще употребляются сриптами чем читаются человеком (зачем это читать если можно посмотреть что в UI нагенерили для каждого endpoint) то логичность синтаксиса — это вопрос второй.
А вот коммунити — это наживается непросто. За RAML стоит одна компания, как и за API Blueprint. Swagger старается вовлечь сообщество в стандарт.
Ну и набирает обороты он быстрее и дальше это будет только ускоряться, как снежный ком.
Например и Microsoft (Azure API Management, API Apps) и Amazon ( AWS API Management import) используют Swagger, а не RAML или API Blueprint.
Для RAML есть генераторы клиентов, как например AutoRest для .NET?AndreiYemelianov
24.08.2015 17:48>>>>Учитывая что эти форматы чаще употребляются сриптами чем читаются человеком (зачем это читать если можно посмотреть что в UI нагенерили для каждого endpoint) то логичность синтаксиса — это вопрос второй. >>>>
Даже для скриптов лучше писать в простом формате и без лишних «костылей».
>>>>Для RAML есть генераторы клиентов, как например AutoRest для .NET?>>>>
Есть, но пока что они находятся в очень «сыром» состоянии.
>>>>А вот коммунити — это наживается непросто. За RAML стоит одна компания, как и за API Blueprint. Swagger старается вовлечь сообщество в стандарт.>>>>>
За RAML стоит не одна компания. В разработке спецификаций принимали участие представители Cisco, PayPal, BoxInc. Насчёт коммьюнити — оно уже формируется (см., например, здесь: raml.org/projects.html)lair
24.08.2015 17:50+2Даже для скриптов лучше писать в простом формате и без лишних «костылей».
Вот только скриптам совершенно все равно на, скажем, дублирование, и поэтому наследование и трейты им уже не так нужны. А если исходить из того, что описание и генерится автоматически — тогда и вопрос противоречивости не встает.
catanfa
24.08.2015 23:54+2активно использую формат blueprint, всё, что описано в статье для RAML есть и в блупринте. Результат получается красивый и для пользователя удобный, но вот для писателя формат api blueprint немного корявенький, очень неудобно, например, следовать через чур строгим требованиям по количеству пробелов и табуляций: список параметров — значит два таба, ещё что-то — четыре таба и т.д. Это кажется немного излишним, ведь распарсить явно и так всё получится.
mnv
25.08.2015 00:08+1У нас на PHP проекте API описано на WSDL, это описание работает и для первичной валидации. Документацию получаем с помощью XSLT преобразования WSDL-ки. Подход хорошо себя зарекомендовал. Но приведенный в статье набор инструментов вокруг RAML впечатляет.
lair
25.08.2015 00:43А у вас REST API или SOAP API?
mnv
25.08.2015 00:57Именно REST
lair
25.08.2015 01:05+2А как вы в wsdl описали rest-ресурсы и, особенно, http-семантику?
mnv
25.08.2015 09:10Пример, входные параметры:
<xs:element name="accountRequestData"> <xs:complexType> <xs:sequence> <xs:annotation> <xs:documentation source="id" xml:lang="ru">Идентификатор записи</xs:documentation> <xs:documentation source="inn" xml:lang="ru">ИНН</xs:documentation> <xs:documentation source="is_active" xml:lang="ru">Счет активен</xs:documentation> <xs:documentation source="user_id" xml:lang="ru">Пользователь</xs:documentation> </xs:annotation> <xs:element name="id" type="xs:int" /> <xs:element name="inn" type="xs:string" minOccurs="0" /> <xs:element name="is_active" type="ns:BOOL" /> <xs:element name="user_id" type="xs:int" /> </xs:sequence> </xs:complexType> </xs:element>
Пример части с документацией:
<operation name="methodAccount"> <documentation type="map"> <name>Вставка и изменение счета</name> <status>works</status> <speed_level>fast</speed_level> <quota_per_second>20</quota_per_second> <rest call="account" httpMethod="POST"/> </documentation> </operation>
lair
25.08.2015 10:00Это ваше собственное расширение или какой-то публичный фреймворк?
mnv
25.08.2015 10:27Фрейморк-то Yii2, но он тут ни при чем. Работа с WSDL через \DOMDocument.
mnv
25.08.2015 10:30Методы для работы с \DOMDocument, кстати, вынесены в отдельную библиотеку, или скорее даже фреймворк, его у нас соседняя команда сделала, но он к сожалению не публичный.
lair
25.08.2015 11:06+1Мне достаточно того, что это ваше расширение, со всеми (отсутствующими) перспективами стандартизации и широкого распространения.
(btw, на глаз в нем есть ощутимое количество ошибок и неполноты, но, поскольку он ваш внутренний, это не принципиально)
AndreiYemelianov
25.08.2015 10:45Слишком сложно…
Кстати, а существуют ли какие-нибудь генераторы интерактивной документации для WSDL?lair
25.08.2015 11:06Не знаю про интеграктивную документацию, а вот клиенты на основании wsdl строятся, в том числе и песочницы.
mnv
25.08.2015 11:11Не сказал бы, что слишком сложно. По сути, за валидацию отвечает несколько строк. Если упрощенно, то примерно так:
$validator = new \DOMDocument( '1.0', 'UTF-8' ); $validator->loadXML( $xml ); if (!$validator->schemaValidateSource($xsd)) { ... }
Т.е. в PHP для этого готовые инструменты есть.
Или громоздко описания выглядят? Ну, возможно. Меня не напрягает, думаю, дело привычки.
Автоматические генераторы есть, и тут уже больше философский вопрос, что первично — документация или код. Мне близок подход, когда сперва составляется осмысленная документация.lair
25.08.2015 11:23А если у вас данные не в xml гуляют?
mnv
25.08.2015 12:10Бывает и такое. В этом случае просто JSON преобразуем в XML и направляем на валидацию
lair
25.08.2015 12:12И вас не смущает, что вы в этом случае требуете, чтобы JSON имел конкретную последовательность элементов, чего он гарантировать не может и не будет?
(ну и вообще это не вполне однозначное преобразование, если вы только не накладываете на xml и xsd дополнительных ограничений)mnv
25.08.2015 13:29+1Не смущает, на этот случай был придуман велосипед — переданные параметры сортируются в алфавитном порядке. В WSDL тоже параметры описаны в алфавитном порядке. Но, как оказалось, на этот случай есть готовое простое решение — вместо sequence можно использовать all в WSDL.
Некоторые ограничения на формат есть, но по практике скажу, что трудностей это не создает.
AndreiYemelianov
25.08.2015 12:24Да, сами описания выглядят несколько громоздко, что немного напрягает.
>>>Автоматические генераторы есть, и тут уже больше философский вопрос, что первично — документация или код. Мне близок подход, когда сперва составляется осмысленная документация.>>>>
Я ничего не имею против этого подхода. Задавая свой вопрос, я никаких философских аспектов затрагивать не хотел. Мне действительно интересно, насколько сейчас распространён WSDL, как он поддерживается сообществом, какие есть вспомогательные инструменты.
roces
25.08.2015 01:29+2Вообще RAML спецификация как и инструменты для работы с ним, мягко говоря, не особо обновляются в последнее время (около года).
Его удобно использовать, чтобы быстро спроектировать REST API и окинуть взглядом как все вместе выглядит.
Из того что не хватает в версии 0.8:
1. Поддержка массивов: some_path/1,2,3,5.json Никак это нельзя описать, только если отдельное описание вставлять.
2. Поддерживает только '=' в query string. Т.е. нельзя описать что-то типа: path?star<2015-08-27T08:30:00.000Z
iSage
>Вот если бы можно их было раз и навсегда прописать в отдельном файле и ссылаться на него в случае небходимости… Но, увы, пока что такой возможности нет.
Как нет? А схемы и $ref? (сотая строка в примере editor.swagger.io )
И в догонку: документация это хорошо, а валидация данных по RAML-схемам есть?
AndreiYemelianov
>>>>валидация данных по RAML-схемам есть?>>>>
Есть. Вот ссылка на инструмент для валидации: www.npmjs.com/package/raml-validate