Перейти к основному содержимому

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

· 11 мин. чтения

1. Обзор

В этой статье мы представляем RESTful API Modeling Language (RAML) — независимый от поставщика язык с открытой спецификацией, построенный на YAML 1.2 и JSON для описания RESTful API.

Мы рассмотрим базовый синтаксис и файловую структуру RAML 1.0, а также продемонстрируем, как определить простой API на основе JSON. Мы также покажем, как упростить обслуживание RAML-файлов с помощью include . А если у вас есть устаревшие API, использующие схему JSON, мы покажем, как включить схемы в RAML.

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

Наконец, мы закончим описанием текущего состояния спецификации RAML.

2. Определение вашего API (создание файла .raml )

API, который мы определим, довольно прост: учитывая типы сущностей Foo , определите основные операции CRUD и пару операций запроса. Вот ресурсы, которые мы определим для нашего API:

  • ПОЛУЧИТЬ /api/v1/foos
  • ПОСТ /api/v1/foos
  • ПОЛУЧИТЬ /api/v1/foos/{id}
  • ПОСТАВЬТЕ /api/v1/foos/{id}
  • УДАЛИТЬ /api/v1/foos/{id}
  • ПОЛУЧИТЬ /api/v1/foos/имя/{имя}
  • ПОЛУЧИТЬ /api/v1/foos?name={name}&ownerName={ownerName}

И давайте определим, что наш API не имеет состояния, использует базовую аутентификацию HTTP и доставляется в зашифрованном виде по HTTPS. Наконец, давайте выберем JSON в качестве нашего формата передачи данных (XML также поддерживается).

2.1. Настройки корневого уровня

Мы начнем с создания простого текстового файла с именем api.raml ( рекомендуется префикс .raml ; имя произвольное) и добавим заголовок версии RAML в первой строке. На корневом уровне файла мы определяем настройки, которые применяются ко всему API:

#%RAML 1.0
title: ForEach Foo REST Services API using Data Types
version: v1
protocols: [ HTTPS ]
baseUri: http://myapi.mysite.com/api/{version}
mediaType: application/json

Обратите внимание на использование фигурных скобок {} вокруг слова « версия » в строке 3. Так мы сообщаем RAML, что « версия» относится к свойству и должна быть расширена. Поэтому фактический baseUri будет: http://myapi.mysite.com/v1 .

[Примечание: свойство версии является необязательным и не обязательно должно быть частью baseUri .]

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

Безопасность также определяется на корневом уровне файла .raml . Итак, давайте добавим определение нашей базовой схемы безопасности HTTP:

securitySchemes:
basicAuth:
description: Each request must contain the headers necessary for
basic authentication
type: Basic Authentication
describedBy:
headers:
Authorization:
description: Used to send the Base64-encoded "username:password"
credentials
type: string
responses:
401:
description: |
Unauthorized. Either the provided username and password
combination is invalid, or the user is not allowed to access
the content provided by the requested URL.

2.3. Типы данных

Далее мы определим типы данных, которые будет использовать наш API:

types:
Foo:
type: object
properties:
id:
required: true
type: integer
name:
required: true
type: string
ownerName:
required: false
type: string

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

types:
Foo:
properties:
id: integer
name: string
ownerName?: string
Error:
properties:
code: integer
message: string

'?' символ, следующий за именем свойства, объявляет, что свойство не требуется.

2.4. Ресурсы

Теперь мы определим ресурс верхнего уровня (URI) нашего API:

/foos:

2.5. Параметры URI

Далее мы расширим список ресурсов на основе нашего ресурса верхнего уровня:

/foos:
/{id}:
/name/{name}:

Здесь фигурные скобки { } вокруг имен свойств определяют параметры URI. Они представляют собой заполнители в каждом URI и не ссылаются на свойства файла RAML корневого уровня, как мы видели выше в объявлении baseUri . Добавленные строки представляют ресурсы /foos/{id} и /foos/name/{name} .

2.6. Методы

Следующим шагом является определение методов HTTP, которые применяются к каждому ресурсу:

/foos:
get:
post:
/{id}:
get:
put:
delete:
/name/{name}:
get:

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

Теперь мы определим способ запроса коллекции foos с использованием параметров запроса. Обратите внимание, что параметры запроса определяются с использованием того же синтаксиса, который мы использовали выше для типов данных:

/foos:
get:
description: List all Foos matching query criteria, if provided;
otherwise list all Foos
queryParameters:
name?: string
ownerName?: string

2.8. Ответы

Теперь, когда мы определили все ресурсы для нашего API, включая параметры URI, методы HTTP и параметры запросов, пришло время определить ожидаемые ответы и коды состояния. Форматы ответов обычно определяются в отношении типов данных и примеров.

Вместо типов данных можно использовать схему JSON для обратной совместимости с более ранней версией RAML. Мы представим схему JSON в разделе 3.

[Примечание: в приведенных ниже фрагментах кода строка, содержащая только три точки (…), указывает на то, что некоторые строки для краткости пропущены.]

Начнем с простой операции GET для /foos/{id}:

/foos:
...
/{id}:
get:
description: Get a Foo by id
responses:
200:
body:
application/json:
type: Foo
example: { "id" : 1, "name" : "First Foo" }

Этот пример показывает, что, выполняя запрос GET к ресурсу /foos/{id} , мы должны получить соответствующий Foo в виде объекта JSON и код состояния HTTP 200.

Вот как мы определяем запрос GET для ресурса /foos :

/foos:
get:
description: List all Foos matching query criteria, if provided;
otherwise list all Foos
queryParameters:
name?: string
ownerName?: string
responses:
200:
body:
application/json:
type: Foo[]
example: |
[
{ "id" : 1, "name" : "First Foo" },
{ "id" : 2, "name" : "Second Foo" }
]

Обратите внимание на использование квадратных скобок [], добавленных к типу Foo . Это демонстрирует, как мы могли бы определить тело ответа, содержащее массив объектов Foo , в примере это массив объектов JSON.

2.9. Тело запроса

Далее мы определим тела запросов, соответствующие каждому запросу POST и PUT. Начнем с создания нового объекта Foo :

/foos:
...
post:
description: Create a new Foo
body:
application/json:
type: Foo
example: { "id" : 5, "name" : "Another foo" }
responses:
201:
body:
application/json:
type: Foo
example: { "id" : 5, "name" : "Another foo" }

2.10. Коды состояния

Обратите внимание, что в приведенном выше примере при создании нового объекта мы возвращаем статус HTTP 201. Операция PUT для обновления объекта возвращает статус HTTP 200, используя те же тела запроса и ответа, что и операция POST.

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

Давайте посмотрим, как бы мы определили ожидаемый ответ на запрос GET для ресурса /foos/{id} , когда ресурс с заданным идентификатором не найден:

404:
body:
application/json:
type: Error
example: { "message" : "Not found", "code" : 1001 }

3. RAML со схемой JSON

До того , как в RAML 1.0 были введены типы данных , объекты, тела запросов и тела ответов определялись с помощью схемы JSON.

Использование типов данных может быть очень эффективным, но есть случаи, когда вы все еще хотите использовать схему JSON. В RAML 0.8 вы определяли свои схемы, используя раздел схем корневого уровня .

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

Вот как вы могли бы определить тип объекта Foo на корневом уровне файла .raml , используя схему JSON:

types:
foo: |
{ "$schema": "http://json-schema.org/schema",
"type": "object",
"description": "Foo details",
"properties": {
"id": { "type": integer },
"name": { "type": "string" },
"ownerName": { "type": "string" }
},
"required": [ "id", "name" ]
}

А вот как вы могли бы сослаться на схему в определении ресурса GET /foos/{id} :

/foos:
...
/{id}:
get:
description: Get a Foo by its id
responses:
200:
body:
application/json:
type: foo
...

4. Рефакторинг с включением

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

Спецификация RAML предоставляет механизм включения, который позволяет нам выделять повторяющиеся и длинные участки кода.

Мы можем реорганизовать наше определение API, используя include, сделав его более кратким и менее вероятным, чтобы содержать типы ошибок, возникающие в результате методологии «копировать/вставить/исправить везде».

Например, мы можем поместить тип данных для объекта Foo в файл types/Foo.raml, а тип для объекта Error — в types/Error.raml . Тогда наш раздел типов будет выглядеть так:

types:
Foo: !include types/Foo.raml
Error: !include types/Error.raml

И если вместо этого мы используем схему JSON, наш раздел типов может выглядеть так:

types:
foo: !include schemas/foo.json
error: !include schemas/error.json

5. Завершение API

После переноса всех типов данных и примеров в их файлы мы можем реорганизовать наш API, используя средство включения:

#%RAML 1.0
title: ForEach Foo REST Services API
version: v1
protocols: [ HTTPS ]
baseUri: http://rest-api.foreach.com/api/{version}
mediaType: application/json
securedBy: basicAuth
securitySchemes:
basicAuth:
description: Each request must contain the headers necessary for
basic authentication
type: Basic Authentication
describedBy:
headers:
Authorization:
description: Used to send the Base64 encoded "username:password"
credentials
type: string
responses:
401:
description: |
Unauthorized. Either the provided username and password
combination is invalid, or the user is not allowed to access
the content provided by the requested URL.
types:
Foo: !include types/Foo.raml
Error: !include types/Error.raml
/foos:
get:
description: List all Foos matching query criteria, if provided;
otherwise list all Foos
queryParameters:
name?: string
ownerName?: string
responses:
200:
body:
application/json:
type: Foo[]
example: !include examples/Foos.json
post:
description: Create a new Foo
body:
application/json:
type: Foo
example: !include examples/Foo.json
responses:
201:
body:
application/json:
type: Foo
example: !include examples/Foo.json
/{id}:
get:
description: Get a Foo by id
responses:
200:
body:
application/json:
type: Foo
example: !include examples/Foo.json
404:
body:
application/json:
type: Error
example: !include examples/Error.json
put:
description: Update a Foo by id
body:
application/json:
type: Foo
example: !include examples/Foo.json
responses:
200:
body:
application/json:
type: Foo
example: !include examples/Foo.json
404:
body:
application/json:
type: Error
example: !include examples/Error.json
delete:
description: Delete a Foo by id
responses:
204:
404:
body:
application/json:
type: Error
example: !include examples/Error.json
/name/{name}:
get:
description: List all Foos with a certain name
responses:
200:
body:
application/json:
type: Foo[]
example: !include examples/Foos.json

6. Инструменты RAML

Одной из замечательных особенностей RAML является поддержка инструментов.

Существуют инструменты для синтаксического анализа, проверки и создания API-интерфейсов RAML; инструменты для генерации клиентского кода; инструменты для создания документации API в форматах HTML и PDF; и инструменты, помогающие нам в тестировании на соответствие спецификации RAML API.

Существует даже инструмент, который преобразует Swagger JSON API в RAML.

Вот пример доступных инструментов:

  • API Designer — веб-инструмент, предназначенный для быстрого и эффективного проектирования API.
  • API Workbench — интегрированная среда разработки для проектирования, создания, тестирования и документирования API RESTful, поддерживающая как RAML 0.8, так и 1.0.
  • RAML Cop — инструмент для проверки файлов RAML.
  • RAML для JAX-RS - набор инструментов для создания скелета кода приложения Java + JAX-RS из спецификации RAML или для создания спецификации RAML из существующего приложения JAX-RS.
  • RAML Sublime Plugin — плагин подсветки синтаксиса для текстового редактора Sublime.
  • RAML to HTML — инструмент для создания HTML-документации из RAML.
  • raml2pdf — инструмент для создания PDF-документации из RAML.
  • RAML2Wiki — инструмент для создания вики-документации (с использованием разметки Confluence/JIRA)
  • Плагин SoapUI RAML — плагин RAML для популярного набора функциональных тестов API SoapUI .
  • Vigia - набор интеграционных тестов, способный генерировать тестовые примеры на основе определения RAML.

Полный список инструментов RAML и связанных с ними проектов см . на странице проектов RAML .

7. Текущее состояние RAML

Спецификация RAML 1.0 (RC) получила статус релиз-кандидата 3 ноября 2015 г., и на момент написания этой статьи ожидалось, что версия 1.0 будет завершена в течение месяца.

Его предшественник, RAML 0.8, был первоначально выпущен осенью 2014 года и до сих пор поддерживается множеством инструментов.

8. Дополнительная литература

Вот несколько ссылок, которые могут оказаться полезными в нашем путешествии с RAML.

9. Заключение

В этой статье представлен язык моделирования RESTful API (RAML). Мы продемонстрировали базовый синтаксис для написания простой спецификации API с использованием спецификации RAML 1.0 (RC).

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

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

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