RAML是Restful API Modeling Language的缩写,是用来描述API信息的文档。
创建一个.raml后缀的文件,用Atom打开。
基本用法
#%RAML 0.8
title: Book API
baseUri: http://api.book.com/{version}
version: v1
/users:
/authors:
/{authorname}:
/books:
get:
queryParameters:
author:
displayName: Author
type: string
description: An author's full name
example: Mary
required: false
publicationYear:
displayName: Pub Year
type: number
description: The year released for the first time
example: 1984
required: false
rating:
displayName: Rating
type: number
description: Average rating
required: false
isBn:
displayName: ISBN
type: string
minLength: 10
example: 0321736079
put:
queryParameters:
access_token:
displayName: Access Token
type: string
description: Token giving you permission to make call
required: true
post:
/{bookTitle}:
get:
description: Retrieve a specific book title
responses:
200:
body:
application/json:
example: |
{
"data":{
"id": "SbBGk",
"title": "its the title",
"descritpion": null
},
"success": true,
"status": 200
} put:
delete:
/author:
get:
/publiser:
get:
以上,
● 类型/users:看作是resource,也就是以/开始,:结尾,而且resource是嵌套存在的
● queryParameters描述查询字符串
接下来把.raml转换成html格式,有一个开源的项目。
→ 参考:https://github.com/raml2html/raml2html
→ 全局安装:npm i -g raml2html
→ .raml文件所在文件夹内打开命令窗口,输入:raml2html example.raml > example.html
就是这么个效果:
Body Parameters
POST请求,通常把参数放到body中传递,在RAML中如何描述呢?
在Body中的参数传递有很多方式,需要在Headers下的Content-Type中设置。Content-Type这个key可能的值包括:multipart/form-data, application/json, application/x-www-form-urlencoded等等。
1、通过multipart/form-data
/file-content:
description: The file to be reproduced by the client
get:
description: Get the file content
responses:
200:
body:
binary/octet-stream:
example:
!include heybulldog.mp3
post:
description: |
Enters the file content for an existing song entity. Use the "binary/octet-stream" content type to specify the content from any consumer (excepting web-browsers).
Use the "multipart-form/data" content type to upload a file which content will become the file-content
body:
binary/octet-stream:
multipart/form-data:
formParameters:
file:
description: The file to be uploaded
required: true
type: file
以上,
● Content-Type能接受的类型是binary/octet-stream或multipart/form-data
● 对于multipart/form-data类型,键值数据放在了formParameters中
● !include heybulldog.mp3表示把heybulldog.mp3文件引入进来
2、通过JSON Shema
JSON Shema用来描述JSON格式。
为什么需要JSON Schema呢?
举个例子:
{
"id":1,
"name":"a green door",
"price":12.50,
"tags":["home", "green"]
}
我们可能会问:
● 什么是id
● name字段必须吗
● price的值可以是0吗
● tags所代表的数组元素是string类型吗?
JSON Schema就是解决这些问题的。
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Product",
"description": "A product from Acme's catalog",
"type": "object"
}
以上,
● $schema表示当前JSON Shema所采用的版本
● type字段是必须的,是object类型
接着,对id字段约束。
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Product",
"description": "A product from Acme's catalog",
"type": "object",
"properties": {
"id": {
"description": "The unique identifier for a product",
"type": "integer"
}
},
"required": ["id"]
}
对name字段约束。
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Product",
"description": "A product from Acme's catalog",
"type": "object",
"properties": {
"id": {
"description": "The unique identifier for a product",
"type": "integer"
},
"name": {
"description": "Name of the product",
"type": "string"
}
},
"required": ["id", "name"]
}
对price字段约束。
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Product",
"description": "A product from Acme's catalog",
"type": "object",
"properties": {
"id": {
"description": "The unique identifier for a product",
"type": "integer"
},
"name": {
"description": "Name of the product",
"type": "string"
},
"price": {
"type": "number",
"minimum": 0,
"exclusiveMinimum": true
}
},
"required": ["id", "name", "price"]
}
对tags字段约束。
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Product",
"description": "A product from Acme's catalog",
"type": "object",
"properties": {
"id": {
"description": "The unique identifier for a product",
"type": "integer"
},
"name": {
"description": "Name of the product",
"type": "string"
},
"price": {
"type": "number",
"minimum": 0,
"exclusiveMinimum": true
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"minItems": 1,
"uniqueItems": true
}
},
"required": ["id", "name", "price"]
}
数组如何用JSON Schema描述呢?
[
{
"id": 2,
"name": "An ice sculpture",
"price": 12.50,
"tags": ["cold", "ice"],
"dimensions": {
"length": 7.0,
"width": 12.0,
"height": 9.5
},
"warehouseLocation": {
"latitude": -78.75,
"longitude": 20.4
}
},
{
"id": 3,
"name": "A blue mouse",
"price": 25.50,
"dimensions": {
"length": 3.1,
"width": 1.0,
"height": 1.0
},
"warehouseLocation": {
"latitude": 54.4,
"longitude": -32.7
}
}
]
用JSON Shema描述就是这样:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Product set",
"type": "array",
"items": {
"title": "Product",
"type": "object",
"properties": {
"id": {
"description": "The unique identifier for a product",
"type": "number"
},
"name": {
"type": "string"
},
"price": {
"type": "number",
"minimum": 0,
"exclusiveMinimum": true
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"minItems": 1,
"uniqueItems": true
},
"dimensions": {
"type": "object",
"properties": {
"length": {"type": "number"},
"width": {"type": "number"},
"height": {"type": "number"}
},
"required": ["length", "width", "height"]
},
"warehouseLocation": {
"description": "Coordinates of the warehouse with the product",
"$ref": "http://json-schema.org/geo"
}
},
"required": ["id", "name", "price"]
}
}
RAML也用到了JSON Shema,就像这样:
body:
application/json:
schema: |
{
"type": "object",
"$schema": "http://json-schema.org/draft-03/schema",
"id": "http://jsonschema.net",
"required": true,
"properties": {
"songTitle": {
"type": "string",
"required": true
},
"albumId": {
"type": "string",
"required": true,
"minLength": 36,
"maxLength": 36
}
}
}
example: |
{
"songId": "550e8400-e29b-41d4-a716-446655440000",
"songTitle": "Get Lucky",
"albumId": "183100e3-0e2b-4404-a716-66104d440550"
}
schemas
每个资源都有自己的schema,是否可以把所有资源的schema合并到同一个地方呢?
RAML提供了schemas字段。
schemas:
- song: |
{
"type": "object",
"$schema": "http://json-schema.org/draft-03/schema",
"id": "http://jsonschema.net",
"required": true,
"properties": {
"songTitle": {
"type": "string",
"required": true
},
"albumId": {
"type": "string",
"required": true,
"minLength": 36,
"maxLength": 36
}
}
}
按如下引用:
body:
application/json:
schema: song
example: |
{
"songId": "550e8400-e29b-41d4-a716-446655440000",
"songTitle": "Get Lucky",
"albumId": "183100e3-0e2b-4404-a716-66104d440550"
}
Resource Types资源类型
每个资源通有相似的部分,能否把这些相似的部分提取抽象出来呢?
假设有2个资源:/resources和/{resourceId}
#%RAML 0.8
title: /resources:
get:
post:
/{resourceId}:
get:
put:
delete:
以上,resource大致可以分成针对集合和针对个体的,所以,在RAML中通过resourceTypes对资源进行分类,有这样的表达方式:
resourceTypes:
- collection:
get:
post:
- collection-item:
get:
于是collection类型可以写成这样:
resourceTypes:
- collection:
description: Collection of available <<resourcePathName>> in Jukebox.
get:
description: Get a list of <<resourcePathName>>.
responses:
200:
body:
application/json:
post:
description: |
Add a new <<resourcePathName|!singularize>> to Jukebox.
queryParameters:
access_token:
description: "The access token provided by the authentication application"
example: AABBCCDD
required: true
type: string
body:
application/json:
schema: <<resourcePathName|!singularize>>
responses:
200:
body:
application/json:
example: |
{ "message": "The <<resourcePathName|!singularize>> has been properly entered" }
以上,
● <<resourcePathName>>是占位符,类似表示songs
● 另外<<resourcePath>>是占位符,类似表示/songs
● <<resourcePathName|!singularize>>是占位符,类似表示song
● <<resourcePathName|!pluralize>>是占位符,类似表示songs
然后这样使用:
/songs:
type: collection
get:
queryParameters:
songTitle:
description: "The title of the song to search (it is case insensitive and doesn't need to match the whole title)"
required: true
minLength: 3
type: string
example: "Get L"
responses:
200:
body:
application/json:
example: |
"songs": [
{
"songId": "550e8400-e29b-41d4-a716-446655440000",
"songTitle": "Get Lucky"
},
{
"songId": "550e8400-e29b-41d4-a716-446655440111",
"songTitle": "Loose yourself to dance"
},
{
"songId": "550e8400-e29b-41d4-a716-446655440222",
"songTitle": "Gio sorgio by Moroder"
}
]
post:
body:
application/json:
example: |
{
"songId": "550e8400-e29b-41d4-a716-446655440000",
"songTitle": "Get Lucky",
"albumId": "183100e3-0e2b-4404-a716-66104d440550"
}
collection-item类型可以写成这样:
resourceTypes:
- collection:
... - collection-item:
description: Entity representing a <<resourcePathName|!singularize>>
get:
description: |
Get the <<resourcePathName|!singularize>>
with <<resourcePathName|!singularize>>Id =
{<<resourcePathName|!singularize>>Id}
responses:
200:
body:
application/json:
404:
body:
application/json:
example: |
{"message": "<<resourcePathName|!singularize>> not found" }
然后这样使用:
/songs:
...
/{songId}:
type: collection-item
get:
responses:
200:
body:
application/json:
example: |
{
"songId": "550e8400-e29b-41d4-a716-446655440000",
"songTitle": "Get Lucky",
"duration": "6:07",
"artist": {
"artistId": "110e8300-e32b-41d4-a716-664400445500"
"artistName": "Daft Punk",
"imageURL": "http://travelhymns.com/wp-content/uploads/2013/06/random-access-memories1.jpg"
},
"album": {
"albumId": "183100e3-0e2b-4404-a716-66104d440550",
"albumName": "Random Access Memories",
"imageURL": "http://upload.wikimedia.org/wikipedia/en/a/a7/Random_Access_Memories.jpg"
}
}
以上,
● 在resourceTypes中的谓词get,post等,可以在具体的resource中进行重新定义
● 在resrouce级别,通过type: collection-item或type: collection与resourceTypes对应
Parameters
以上,resourceTypes字段所代表的是一个对集合和个体类型相同操作的一个封装,在这些操作中,在这些请求响应中,有时需要通过example字段来举例,通常这样写:
example: |
{
...
} 或者 example: |
[
{ },
{ }
]
但在RAML中,为我们提供了<<exampleCollection>>和<<exampleItem>>占位符分别表示集合和个体。
resourceTypes:
- collection:
description: Collection of available <<resourcePathName>> in Jukebox.
get:
description: Get a list of <<resourcePathName>>.
responses:
200:
body:
application/json:
example: |
<<exampleCollection>>
post:
description: |
Add a new <<resourcePathName|!singularize>> to Jukebox.
queryParameters:
access_token:
description: "The access token provided by the authentication application"
example: AABBCCDD
required: true
type: string
body:
application/json:
schema: <<resourcePathName|!singularize>>
example: |
<<exampleItem>>
responses:
200:
body:
application/json:
example: |
{ "message": "The <<resourcePathName|!singularize>> has been properly entered" }
- collection-item:
description: Entity representing a <<resourcePathName|!singularize>>
get:
description: |
Get the <<resourcePathName|!singularize>>
with <<resourcePathName|!singularize>>Id =
{<<resourcePathName|!singularize>>Id}
responses:
200:
body:
application/json:
example: |
<<exampleItem>>
404:
body:
application/json:
example: |
{"message": "<<resourcePathName|!singularize>> not found" }
在资源resource部分通常这样调用:
/songs:
type:
collection:
exampleCollection: |
[
...
]
/{songId}:
type:
collection-item:
exampleItem: |
{
...
}
具体来说,类似这样:
/songs:
type:
collection:
exampleCollection: |
[
{
"songId": "550e8400-e29b-41d4-a716-446655440000",
"songTitle": "Get Lucky"
},
{
"songId": "550e8400-e29b-41d4-a716-446655440111",
"songTitle": "Loose yourself to dance"
},
{
"songId": "550e8400-e29b-41d4-a716-446655440222",
"songTitle": "Gio sorgio by Morodera"
}
]
exampleItem: |
{
"songId": "550e8400-e29b-41d4-a716-446655440000",
"songTitle": "Get Lucky",
"albumId": "183100e3-0e2b-4404-a716-66104d440550"
}
get:
queryParameters:
songTitle:
description: "The title of the song to search (it is case insensitive and doesn't need to match the whole title)"
required: true
minLength: 3
type: string
example: "Get L"
/{songId}:
type:
collection-item:
exampleItem: |
{
"songId": "550e8400-e29b-41d4-a716-446655440000",
"songTitle": "Get Lucky",
"duration": "6:07",
"artist": {
"artistId": "110e8300-e32b-41d4-a716-664400445500"
"artistName": "Daft Punk",
"imageURL": "http://travelhymns.com/wp-content/uploads/2013/06/random-access-memories1.jpg"
},
"album": {
"albumId": "183100e3-0e2b-4404-a716-66104d440550",
"albumName": "Random Access Memories",
"imageURL": "http://upload.wikimedia.org/wikipedia/en/a/a7/Random_Access_Memories.jpg"
}
}
Includes
可以把一些example放到单独的文件,然后通过!include关键字来引用这些文件。
/songs:
type:
collection:
exampleCollection: !include jukebox-include-songs.sample
exampleItem: !include jukebox-include-song-new.sample
/{songId}:
type:
collection-item:
exampleItem: !include jukebox-include-song-retrieve.sample
traits
如何描述查询、排序、分页呢?
traits:
- searchable:
queryParameters:
query:
description: |
JSON array [{"field1","value1","operator1"},{"field2","value2","operator2"},...,{"fieldN","valueN","operatorN"}] <<description>>
example: |
<<example>>aml - orderable:
queryParameters:
orderBy:
description: |
Order by field: <<fieldsList>>
type: string
required: false
order:
description: Order
enum: [desc, asc]
default: desc
required: false
- pageable:
queryParameters:
offset:
description: Skip over a number of elements by specifying an offset value for the query
type: integer
required: false
example: 20
default: 0
limit:
description: Limit the number of elements on the response
type: integer
required: false
example: 80
default: 10
按如下使用这些trait。
/songs:
type:
collection:
exampleCollection: !include jukebox-include-songs.sample
exampleItem: !include jukebox-include-song-new.sample
get:
is: [
searchable: {description: "with valid searchable fields: songTitle", example: "[\"songTitle\", \"Get L\", \"like\"]"},
orderable: {fieldsList: "songTitle"},
pageable
]
另外,schema也可以放到单独的文件中,然后通过!include引用。
schemas:
- song: !include jukebox-include-song.schema
- artist: !include jukebox-include-artist.schema
- album: !include jukebox-include-album.schema
resourceTypes也可以放到单独的文件中:
resourceTypes: !include jukebox-includes-resourceTypes.inc