Статьи

RAML, язык моделирования RESTful API

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

Но сначала, если вы будете на мгновение потакать мне, я хотел бы начать с цитаты из этой статьи;

[an] API хорош только как его документация

Я думаю, что это стоит повторить, так как об этом слишком часто забывают, и это одна из причин, побудивших эту короткую серию статей о некоторых инструментах, которые помогут вам написать отличную документацию.

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

RAML (язык моделирования RESTful API) предоставляет структурированный, однозначный формат для описания RESTful API. Это позволяет вам описать ваш API; конечные точки, методы HTTP, которые будут использоваться для каждого, любые параметры и их формат, что вы можете ожидать посредством ответа и многое другое.

Вы можете использовать RAML несколькими способами;

  • Его можно использовать на этапе проектирования и спецификации для описания API, который вы планируете создать
  • Вы можете использовать RAML для генерации документации
  • RAML можно использовать для реализации интерактивных консолей API
  • Вы можете проверить против RAML
  • Он будет использоваться для генерации ложных ответов API

В этой статье я прежде всего расскажу о создании документации; в следующей статье я расскажу, как можно интегрировать RAML в тестирование API. Но сначала давайте посмотрим, как написать документ RAML.

Написание RAML

Первое, что следует отметить, — это то, что RAML является производной от YAML . Вам будет проще работать с RAML, если вы уже писали YAML, но если нет, то это относительно просто, или здесь на Sitepoint есть учебник .

Поскольку это просто текстовые файлы, вы можете, конечно, использовать любой текстовый редактор или IDE для их создания и редактирования. Однако лучшим вариантом является использование платформы Anypoint. Он предоставляет редактор API, который позволяет редактировать файлы RAML с автоматическим заполнением, проверкой по мере ввода и интерактивным предварительным просмотром некоторой сгенерированной интерактивной документации (позже мы увидим, как ее сгенерировать).

Чтобы использовать Anypoint, сначала нужно зарегистрироваться — это бесплатно — затем добавить API, а затем нажать «Определить API в API Designer».

Если вы предпочитаете Sublime, есть плагин, который вы можете использовать. Это доступно через управление пакетами; просто выполните поиск «RAML Syntax Highlighter».

Давайте посмотрим на структуру файла RAML.

Основная информация

Файл RAML начинается с объявления с указанием формата:

#%RAML 0.8 

Обычно вы предоставляете мета-информацию о вашем API, например:

 title: My Example API version: v1 

title — обязательное поле; version не обязательна

Затем вы должны предоставить базовый URI для вашего API. В простейшем случае это может быть в следующем формате:

 baseUri: http://api.acme.com/ 

Вы также можете использовать параметры URI. Например, обычной практикой является включение идентификатора версии в URL-адреса API; если вы придерживаетесь этого подхода, вы можете сделать это:

 baseUri: http://api.acme.com/{version}/ 

Позже мы рассмотрим параметры URI более подробно, поскольку они становятся жизненно важными при описании ресурсов. Но если вам нужно, вы можете включить дополнительные параметры в ваш базовый URI. Например, Amazon S3 включает название корзины, с которой вы работаете, в базовый URI.

Вы также можете указать, доступен ли ваш API через HTTPS, установив поле protocols , например:

 protocols: [ HTTP, HTTPS ] 

Вы можете указать тип носителя по умолчанию, который будет возвращать ваш API, например:

 mediaType: application/json 

Позже, когда мы посмотрим на ответы, мы рассмотрим, как вы можете описать несколько типов медиа-ответов и что от них ожидать.

Вы также можете включить дополнительную документацию — или страницы — в ваш файл RAML. Например:

 documentation - title: Home content: | This example documentation forms part of a tutorial for a [Sitepoint](http://www.sitepoint.com/) article. 

Обратите внимание, как вы можете включить Markdown в свою метаинформацию.

Здесь вы можете включить некоторые другие вещи, такие как, как реализована защита, а также определение характеристик . Мы рассмотрим эти концепции чуть позже.

Это большая часть базовой информации, так что теперь давайте перейдем к ресурсам.

Описание ресурсов

Продолжая на верхнем уровне вашего RAML-файла, следующим шагом является описание доступных ресурсов. Вот пример:

 /albums: displayName: Album /artists: displayName: Artist 

Свойство displayName является необязательным, но может помочь описать, что ресурсы представляют «в реальной жизни».

Здесь мы определили два ресурса верхнего уровня, (музыкальные) альбомы и ресурсы. То, что мы еще не сделали, — это описание методов HTTP, которые можно использовать для взаимодействия с ними, но сначала давайте немного углубимся в детали и посмотрим на URI ресурсов и подресурсы.

Давайте предположим, что определенные альбомы представлены их ISRC (Международный стандартный код записи); Итак, давайте расширим наш ресурс /albums чтобы описать это:

 /albums: /{isrc}: uriParameters: isrc: description: The International Standard Recording Code which uniquely identifies this album type: string pattern: ^[a-zA-Z]{2}\-[0-9a-zA-Z]{3}\-\d{2}\-\d{5}$ 

Как видите, параметры URI обозначены фигурными скобками. Свойство uriParameters может использоваться для предоставления дополнительной информации об этих параметрах, как метаинформации (в данном случае, description ), так и конкретного определения их формата — в этом примере типа данных и регулярного выражения, которое определяет конкретный формат.

Используя вложение, мы можем описать подресурсы. Например, предположим, что мы предоставляем конечную точку для извлечения трек-листа отдельного альбома:

 /albums: /{isrc}: uriParameters: isrc: description: The International Standard Recording Code which uniquely identifies this album type: string pattern: ^[a-zA-Z]{2}\-[0-9a-zA-Z]{3}\-\d{2}\-\d{5}$ /tracks: displayName: Tracklisting 

Вы можете иметь столько вложений ресурсов, сколько пожелаете; просто используйте отступ в стиле YAML.

Теперь давайте перейдем к определению методов, которые можно использовать на этих ресурсах.

Методы HTTP

Большинство API REST_ful_ имеют тенденцию реализовывать четыре наиболее распространенных метода HTTP; GET для поиска, PUT для обновлений, POST для создания и DELETE для, ну, в общем, удаления ресурсов. Наш пример API ничем не отличается. Итак, давайте изменим ресурс наших альбомов, чтобы отразить это:

 /albums: get: description: Retrieve a list of albums post: description: Create a new album /{isrc}: uriParameters: isrc: description: The International Standard Recording Code which uniquely identifies this album type: string pattern: ^[a-zA-Z]{2}\-[0-9a-zA-Z]{3}\-\d{2}\-\d{5}$ get: description: Retrieve the specified album put: description: Update an album delete: description: Delete this album 

Здесь мы указываем, что запрос GET для /albums извлечет список альбомов, и для его создания используется POST для того же URI. При детализации GET к /albums/{isrc} будет извлекать информацию о конкретном альбоме, заданную с помощью параметра URI isrc . запрос PUT используется для обновления альбома, запрос DELETE для его удаления.

Описание ответов

Теперь, когда мы определили наши ресурсы и доступные методы, мы должны иметь возможность описать ответы, которые может ожидать пользователь.

Чтобы продемонстрировать это, мы предоставим некоторые дополнительные сведения о конкретных альбомах.

Чтобы описать ответы, нам нужно детализировать еще несколько уровней. Во-первых, нам нужно описать возможные коды ответов, а затем нам нужно разбить ответы на их компоненты — как правило, тело ответа — а затем возможные типы носителей ответа. Вот пример, чтобы сделать это немного яснее:

 /albums: /{isrc}: get: description: Retrieve the specified album responses: 200: body: application/json: schema: | { "$schema": "http://json-schema.org/schema", "type": "object", "description": "An album", "properties": { "title": { "type": "string" }, "artist": { "type": "string" }, "label": { "type": "string" }, "year": { "type": "integer" } }, "required": [ "title", "artist" ] } example: | { "title": "Dubnobasswithmyheadman", "artist": "Underworld", "label": "Junior Boy's Own", "year": 1994 } 

Здесь мы демонстрируем успешный ответ, т. Е. Ответ с HTTP-кодом ответа 200. Что нас особенно интересует, так это тело ответа, хотя здесь вы также можете определить любые заголовки ответа. Затем мы углубимся в доступные типы ответов; в этом случае мы просто представляем JSON — хотя вы можете определять несколько типов ответов, если ваш API их поддерживает.

Как только мы переходим к телу успешного ответа, мы указываем два свойства; schema и example .

Свойство schema содержит схему JSON, которая определяет структуру ожидаемого JSON. Очень скоро я расскажу о схеме JSON в другой статье. Свойство example содержит именно это, давая понять, какой ответ может ожидать кто-то, вызывающий ваш API.

Параметры запроса

Мы можем определить параметры запроса (обычно для запросов GET) очень похожим образом, в котором мы определили параметры URI.

Чтобы проиллюстрировать это, давайте еще раз посмотрим на конечную точку нашего /albums album, предположив, что мы хотим реализовать нумерацию страниц; поэтому мы, вероятно, хотим предоставить параметр запроса с именем page который позволяет запрашивающей стороне указать, какую страницу они хотят получить.

Вот как мы можем это сделать:

 /albums: get: description: Retrieve a list of albums queryParameters: page: description: Specify the page that you want to retrieve type: integer example: 1 

Опять же, определение параметра запроса представляет собой смесь метаинформации — такой как description и example — и некоторые свойства, которые помогают явно определить, что ожидает API; здесь мы проясняем, что параметр page должен быть целым числом.

Запрос данных

Давайте вернемся к нашей конечной точке «Создать альбом», которая, как вы помните, включает в себя запрос POST к URI /albums album.

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

Вот пример:

 /albums: get: description: Retrieve a list of albums post: description: Create a new album body: application/x-www-form-urlencoded: formParameters: isrc: description: The International Standard Recording Code which uniquely identifies this album type: string pattern: ^[a-zA-Z]{2}\-[0-9a-zA-Z]{3}\-\d{2}\-\d{5}$ required: true name: description: The name of the album type: string required: true artist: description: The name of the artist type: string required: true label: description: The label it was released under type: string required: false year: description: The year the album was released type: integer required: false minimum: 1900 maximum: 3000 

Здесь мы определяем ожидаемое тело запроса при отправке сообщения в эту конечную точку. Мы указываем, что запрос должен иметь тип application/x-www-form-urlencoded .

Далее мы разбиваем ожидаемое тело запроса на параметры с formParameters свойства formParameters . Затем мы перечисляем возможные поля, предоставляем некоторые метаданные о каждом из них, а также ожидаемый тип. Мы также можем указать, какие поля являются обязательными, а какие — необязательными, а также некоторые правила проверки — в этом случае мы используем регулярное выражение для определения формата ISRC и некоторые относительно разумные границы года выпуска.

Безопасность

Скорее всего, ваш API защищен каким-то образом — будь то OAuth, токены доступа или просто обычная HTTP-аутентификация.

В RAML вы можете определить свои схемы безопасности в верхней части вашего файла, наряду с основной информацией. Некоторые примеры вы найдете в документе спецификации , но в качестве примера давайте рассмотрим OAuth2. Вот как может выглядеть определение схемы безопасности:

 securitySchemes: - oauth_2_0: description: | Acme uses OAuth2 to authenticate most requests type: OAuth 2.0 describedBy: headers: Authorization: description: | Used to send a valid OAuth 2 access token. Do not use with the "access_token" query string parameter. type: string queryParameters: access_token: description: | Used to send a valid OAuth 2 access token. Do not use together with the "Authorization" header type: string responses: 400: description: | Bad OAuth request (eg wrong consumer key, bad nonce, expired timestamp, etc.) 401: description: | Bad or expired token. To fix it, re-authenticate the user. settings: authorizationUri: https://acme.com/oauth/authorize accessTokenUri: https://acme.com/oauth/token authorizationGrants: [ code, token ] 

Если вы посмотрите на это, вы увидите, что он предоставляет ряд ключевых элементов информации;

  • type указывает, что мы реализуем OAuth 2.0
  • Для аутентификации рассматриваемый API ожидает либо заголовок Authorization либо параметр запроса access_token
  • В нем перечислены возможные ответы, что они означают и как их исправить
  • settings специфичны для OAuth, но тем не менее жизненно важны он сообщает пользователям, как авторизоваться, где получить токен доступа и типы грантов OAuth, поддерживаемые этим API.

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

Один из способов — добавить следующее в начало вашего файла RAML:

 securedBy: [oauth_2_0] 

Обратите внимание, что oauth_2_0 соответствует элементу непосредственно под securitySchemes

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

 /albums: get: securedBy: [null, oauth_2_0] post: securedBy: [oauth_2_0] 

Здесь мы указываем, что аутентификация является необязательной для получения списка альбомов, но что пользователь должен аутентифицироваться, чтобы создать его.

Черты

Вы, вероятно, будете иметь определенные поведения, политики или характеристики, которые являются общими для разных конечных точек. Хороший пример — нумерация страниц; различные коллекции, которые поддерживают разбиение на страницы, без сомнения, будут использовать один и тот же подход, чтобы поддерживать согласованность API. Или, как мы видели в разделе «Безопасность», у вас может быть разная степень безопасности, например общедоступная или «требуется авторизация».

Вместо того, чтобы повторять одну и ту же конфигурацию для нескольких конечных точек, вы можете определить черты . В целом, эта концепция является синонимом черт в PHP.

Вот простой пример создания признака, указывающего, что результаты с данной конечной точки поддерживают простую нумерацию страниц:

 traits: - paged: queryParameters: page: description: Specify the page that you want to retrieve type: integer required: true example: 1 

Теперь вы можете применить это к конечной точке, используя свойство is , например так:

 /albums: get: is: [ paged ] 

Вы также можете передать переменные. В качестве примера, давайте расширим нашу paged черту, чтобы включить количество результатов на страницу. Мы определим максимальное количество результатов на странице, которое может быть переопределено для каждой конечной точки. Вот определение черты:

 traits: - paged: queryParameters: page: description: Specify the page that you want to retrieve type: integer required: true example: 1 perPage: type: integer description: The number of results per page, not to exceed <<maxPerPage>> maximum: <<maxPerPage>> 

Обратите внимание, как мы можем использовать переменную <<maxPerPage>> как максимальное ограничение, а также подставить ее в нашу сгенерированную документацию.

Чтобы использовать это, измените определение конечной точки GET /albums album на следующее:

 /albums: get: is: [ paged : { maxPerPage : 50 } ] 

В спецификации вы найдете больше примеров характеристик, а также типов ресурсов, которые имеют определенные характеристики с характеристиками. Вы также найдете множество другой документации и примеры некоторых более сложных сценариев, которые вам, возможно, понадобится описать. Но давайте сейчас перейдем к рассмотрению некоторых практических применений RAML.

RAML на практике

Теперь, когда мы рассмотрели, как написать простой RAML для описания нашего RESTful API, пришло время посмотреть, что мы на самом деле можем с ним сделать.

Создание документации API

RAML — это очень подробный, однозначный обзор API, но он не очень удобен для пользователей нашего API.

Однако, поскольку это четко определенный открытый формат, существуют инструменты, которые могут помочь нам использовать эту информацию более удобным для пользователя способом. В частности, существуют инструменты, которые берут наши файлы RAML и создают документацию на основе HTML, готовую для публикации в Интернете.

Давайте посмотрим на несколько.

Преобразование RAML в HTML

raml2html

Инструмент raml2html , как следует из названия, может конвертировать ваши файлы RAML в документацию HTML.

Вот скриншот того, как это выглядит «из коробки»:

An Example set of documentation from the raml2html tool

Чтобы использовать его, сначала установите его через npm :

 npm i -g raml2html 

Затем, чтобы преобразовать файл RAML в HTML, вы просто делаете это:

 raml2html api.raml > index.html 

Результатом является довольно разумный набор документации по API. У него есть определенные зависимости — такие как JQuery и Bootstrap — которые он извлекает из CDN, но, конечно, вы можете изменить полученный HTML-код по своему усмотрению. Вы можете создавать свои собственные шаблоны, если хотите, и просто указывать их через командную строку:

 raml2html -t custom-template.handlebars -r custom-resource.handlebars -m custom-item.handlebars -i example.raml -o example.html 

Есть также плагин Grunt и плагин Gulp, чтобы еще проще создавать документацию.

Есть определенные ограничения. Информация о безопасности не копируется в HTML, поэтому, если вы используете, например, OAuth, вам, возможно, придется документировать это отдельно.

PHP-raml2html

Есть также PHP-эквивалент php-raml2html .

Вот скриншот вывода инструмента:

An example screenshot of documentation produced using php-raml2html

Есть также онлайн демо , и вы найдете документацию здесь .

Лично я предпочитаю raml2html по двум причинам. Во-первых, вывод по умолчанию немного чище. Во-вторых, он генерирует статический HTML — тогда как php-raml2html требует PHP для работы — это означает, что он идеально подходит для размещения на страницах Github и т.п. Однако, если вы в первую очередь разработчик PHP, вам может показаться, что этот инструмент проще в настройке.

Консоль API

Вы можете создать более сложную документацию из файла RAML с помощью веб-компонента API Console . Результирующий HTML похож по стилю на raml2html, но у него есть особенность убийцы; он предоставляет консоль, которая позволяет людям взаимодействовать с вашим API из документации. Он заботится о создании форм для вас для любых доступных параметров, проверке их в соответствии с вашими определениями, а также о том, как определить, как выполнить аутентификацию.

Чтобы увидеть это в действии, есть демоверсии онлайн для Twitter и Github . Если вы использовали платформу Anypoint, вы уже видели ее; это то, что используется для генерации интерактивной документации в правом столбце.

Консоль API реализована с использованием Angular, но для ее использования не обязательно иметь какой-либо опыт работы с ней.

Чтобы быстро приступить к работе, просто выполните следующие действия:

  1. Клонировать репозиторий: git clone git@github.com:mulesoft/api-console.git

  2. Скопируйте папку dist в пустой каталог для хранения вашей документации

  3. Скопируйте файл .raml куда-нибудь в эту папку

  4. Создайте HTML-файл следующим образом:

 <head> <link rel="stylesheet" href="dist/styles/app.css" type="text/css" /> </head> <body ng-app="ramlConsoleApp" ng-cloak id="raml-console-unembedded"> <raml-console src="api.raml"></raml-console> <script src="dist/scripts/vendor.js"></script> <script src="dist/scripts/app.js"></script> </body> 

Измените атрибут src директивы <raml-console> чтобы он указывал на правильный файл

  1. Откройте файл через веб-сервер какого-либо описания; будь то Apache, Ngnix или Connect

Это все, что нужно сделать.

Вы также можете встроить консоль API в iframe; обратитесь к README проекта для деталей.

Ноутбуки

Блокноты API — это еще один способ взаимодействия людей с вашим API. Вы можете встраивать работающие примеры в свои веб-страницы, используя RAML для описания базового API.

Это, возможно, лучше всего иллюстрируется на примере; нажмите здесь для демонстрации использования записной книжки API для взаимодействия с API Github.

Возможно, вы захотите начать здесь , так как эта конкретная часть документации относится к созданию ноутбука, который работает на RAML.

Другие инструменты

Для работы с файлами RAML существует ряд других инструментов. Например;

Вы можете конвертировать RAML в формат Markdown, используя этот инструмент командной строки .

Инструмент RAML to Wiki позволяет вам конвертировать RAML в контент, подходящий для вики в Confluence или JIRA.

На этой странице проектов вы найдете полный список инструментов, доступных для работы с RAML.

Создайте свой собственный инструмент

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

Хорошее место для начала, вероятно, это PHP-анализатор RAML . Это позволяет преобразовывать схемы в объект проверки, извлекать список маршрутов и многое другое.

Также есть парсеры для JavaScript , Java , Python и Ruby .

Вывод

В этой статье я подробно рассмотрел RAML, язык моделирования RESTful API. Он предоставляет способ описания RESTful API в ясных, исчерпывающих и недвусмысленных терминах.

Возможно, наиболее очевидное и, возможно, наиболее полезное использование RAML — это создание документации. Мы рассмотрели несколько подходов к этому, особенно проект API Console, который не только генерирует документацию на основе HTML, но и предоставляет интерактивную консоль, которая позволяет читателям опробовать ваш API из документации.

Мы также рассмотрели ряд других инструментов. Будучи относительно новым стандартом, ожидайте увидеть больше в ближайшем будущем.

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