這篇文章是系列的一部分
• 內存總線簡介 – RESTful API 建模語言 (當前文章)
• 使用資源類型和特性消除 RAML 中的冗餘
• 使用包含、庫、覆蓋和擴展模塊化 RAML
• 使用註釋定義自定義 RAML 屬性
• 使用包含、庫、覆蓋和擴展模塊化 RAML
• 使用註釋定義自定義 RAML 屬性
在本文中,我們將介紹 RESTful API 建模語言 (RAML),這是一種與供應商無關、開放規範的語言,基於 YAML 1.2 和 JSON,用於描述 RESTful API。
我們將涵蓋基本的 RAML 1.0 語法和文件結構,同時演示如何定義一個簡單的基於 JSON 的 API。 我們還將展示如何通過使用 includes 來簡化 RAML 文件維護。 如果您有使用 JSON 模式的遺留 API,我們將演示如何將模式集成到 RAML 中。
然後,我們將介紹一些可以增強您進入 RAML 旅程的工具,包括作者工具、文檔生成器等。
最後,我們將總結 RAML 規範的當前狀態。
The API we’ll define is fairly simple: given the entity types Foo, define basic CRUD operations and a couple of query operations. Here are the resources that we will define for our API:
And let’s define our API to be stateless, using HTTP Basic authentication, and to be delivered encrypted over HTTPS. Finally, let’s choose JSON for our data transport format (XML is also supported).
We’ll start by creating a simple text file named api.raml (the .raml prefix is recommended; the name is arbitrary) and add the RAML version header on line one. At the root level of the file, we define settings that apply to the entire API:
#%RAML 1.0
title: Baeldung Foo REST Services API using Data Types
version: v1
protocols: [ HTTPS ]
baseUri: http://myapi.mysite.com/api/{version}
mediaType: application/json
Notice on line 3 the use of braces { } around the word “version“. This is how we tell RAML that “version” refers to a property and is to be expanded. Therefore the actual baseUri will be: http://myapi.mysite.com/v1
[Note: the version property is optional and need not be a part of the baseUri.]
Security is also defined at the root level of the .raml file. So let’s add our HTTP basic security scheme definition:
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.
Next, we’ll define the data types that our API will use:
types:
Foo:
type: object
properties:
id:
required: true
type: integer
name:
required: true
type: string
ownerName:
required: false
type: string
The above example uses expanded syntax for defining our data types. RAML provides some syntactical shortcuts to make our type definitions less verbose. Here is the equivalent data types section using these shortcuts:
types:
Foo:
properties:
id: integer
name: string
ownerName?: string
Error:
properties:
code: integer
message: string
The ‘?’ character following a property name declares that the property is not required.
Now, we’ll define the top-level resource (URI) of our API:
/foos:
Next, we’ll expand the list of resources, building from our top-level resource:
/foos:
/{id}:
/name/{name}:
Here, the braces { } around property names define URI parameters. They represent placeholders in each URI and do not reference root-level RAML file properties as we saw above in the baseUri declaration. The added lines represent the resources //foos/{id} and //foos/name/{name}.
The next step is to define the HTTP methods that apply to each resource:
/foos:
...
/{id}:
get:
put:
delete:
/name/{name}:
get:
Now we’ll define a way to query the foos collection using query parameters. Note that query parameters are defined using the same syntax that we used above for data types:
/foos:
...
get:
description: List all Foos matching query criteria, if provided;
otherwise list all Foos
queryParameters:
name?: string
ownerName?: string
Now that we’ve defined all of the resources for our API, including URI parameters, HTTP methods, and query parameters, it is time to define the expected responses and status codes. Response formats are typically defined regarding data types and examples.
JSON schema can be used instead of data types for backward compatibility with an earlier version of RAML. We’ll introduce JSON schema in section 3.
[Note: In the code snippets below, a line containing only three dots (…) indicates that some lines are being skipped for brevity.]
Let’s start with the simple GET request on /foos/{id}:
/foos:
...
/{id}:
get:
description: Get a Foo by id
responses:
200:
body:
application/json:
type: Foo
example: { "id" : 1, "name" : "First Foo" }
This example shows that by performing a GET request on the resource //foos/{id}, we should get back the matching Foo in the form of a JSON object and an HTTP status code of 200.
Here is how we’d define the GET request on the /foos resource:
/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" }
]
Note the use of square brackets [] appended to the Foo type. This demonstrates how we would define a response body containing an array of Foo objects, with the example being an array of JSON objects.
Next, we’ll define the request bodies that correspond to each POST and PUT request. Let’s begin with creating a new Foo object:
/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" }
Note in the above example that when creating a new object, we return an HTTP status of 201. The PUT operation for updating an object will return an HTTP status of 200, utilizing the same request and response bodies as the POST operation.
In addition to the expected responses and status codes that we return when a request is successful, we can define the kind of response and a status code to expect when an error occurs.
Let’s see how we would define the expected response for the GET request on /foos/{id} when no resource is found with the given id:
404:
body:
application/json:
type: Error
example: { "message" : "Not found", "code" : 1001 }
使用數據類型非常強大,但仍然存在一些需要使用 JSON Schema 的情況。在 RAML 0.8 中,您使用頂層schemas部分定義您的 schema。
這仍然有效,但建議使用types部分,因為在未來版本中,使用schemas可能被棄用。types、schemas、type 和 schema 都是同義詞。
以下是如何在 .raml 文件的頂層使用 JSON schema 定義 Foo 對象類型的示例:
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} 資源定義中引用 schema 的示例:
/foos:
...
/{id}:
get:
description: 獲取一個 Foo 對象,通過其 id
responses:
200:
body:
application/json:
type: foo
...
RAML規範提供了一個包含機制,允許我們外部化重複且冗長的代碼片段。
我們可以使用包含文件來重構我們的API定義,使其更簡潔,並降低由於“複製/粘貼/在所有地方修復”方法所導致的一類錯誤。
例如,我們可以將Foo對象的數據類型放在types/Foo.raml文件中,將Error對象的數據類型放在types/Error.raml文件中。 那麼我們的types部分將如下所示:
types:
Foo: !include types/Foo.raml
Error: !include types/Error.raml
如果使用JSON Schema,那麼我們的types部分可能如下所示:
types:
foo: !include schemas/foo.json
error: !include schemas/error.json
外部化所有數據類型和示例到其文件後,我們可以使用 include 功能來重構我們的 API。
#%RAML 1.0
title: Baeldung Foo REST Services API
version: v1
protocols: [ HTTPS ]
baseUri: http://rest-api.baeldung.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
RAML 的一大優勢在於其工具支持。
有用於解析、驗證和編寫 RAML API 的工具;有用於生成客户端代碼的工具;有用於生成 HTML 和 PDF 格式的 API 文檔工具;以及有幫助我們測試與 RAML API 規範的工具。
甚至有一個工具可以將 Swagger JSON API 轉換為 RAML。
以下是一些可用的工具示例:
要查看 RAML 工具和相關項目的完整列表,請訪問 RAML 項目 頁面。
RAML 1.0 (RC) 規範於 2015 年 11 月 3 日獲得候選發佈狀態,在此撰寫時,預計 1.0 版本將在本月完成最終定稿。
它的前身,RAML 0.8 於 2014 年秋季首次發佈,並且仍然被眾多工具支持。
以下是一些在學習 RAML 過程中可能對我們有用的鏈接。
本文介紹了 RESTful API 建模語言 (RAML)。我們演示了使用 RAML 1.0 (RC) 規範編寫簡單 API 規範的基本語法。
我們還看到了通過使用語法快捷方式和將示例、數據類型和模式外部化到“include”文件來使定義更簡潔的方法。
然後,我們介紹了可以與 RAML 規範一起使用,以協助日常 API 設計、開發、測試和文檔任務的一系列強大工具。
隨着規範 1.0 正式發佈的計劃,以及開發者工具的支持,RAML 似乎已經確立了地位。