RAML规范1.0(译文)

版本1.0

What's RAML ?

RAML: RESTful API Modeling Language即RESTful API建模语言,是对RESTful API的一种简单直接的描述。它是一种让人们易于阅读且能让机器对特定文档可以解析的语言。RAML是基于YAML,符合1.2版本规范。通过RAML定义,因为机器可以解析,所以RAML规范提供了一些机制,可以定义实际的RESTful API,创建client / server源代码,以及为用户生成API文档。

本文中一些关键字 "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" 是被描述为 IETF RFC 2119 在 RFCs 中的关键字指示需求级别。

RAML中包含以下内容:

  • Basic Information. How to describe core aspects of the API, such as its name, title, location (or URI), and defaults and how to include supporting documentation for the API.
  • Data Types. Modeling API data through a streamlined type system that encompasses JSON Schema and XML Schema (XSD).
  • Resources. How to specify API resources and nested resources, as well as URI parameters in any URI templates.
  • Methods. How to specify the methods on API resources and their request headers, query parameters, and request bodies.
  • Responses. The specification of API responses, including status codes, media types, response headers, and response bodies.
  • Resource Types and Traits. The optional use of RAML resource types and traits to characterize resources.
  • Security. Specifying an API security scheme in RAML.
  • Annotations. Extending a RAML specification by defining strongly-typed annotations and applying them throughout the specification.
  • Includes, Libraries, Overlays, and Extensions. How an API definition can consist of externalized definition documents, packaging collections of such definitions into libraries, separating and overlaying layers of metadata on a RAML document, and extending an API specification with additional functionality.

版本声明(MUST)

用 "#" 表示文档中行注释的开始,RAML API 定义 MUST 符合 YAML 1.2 的文档,以指示 RAML 版本的 REQUIRED YAML 注释行开头,RAML API 定义文档的第一行 MUST 以文本 #%RAML 开头,后跟一个空格,后跟版本号,在行尾之前没有其他内容。如下所示:

#%RAML 1.0
title: My API

文档根节点

RAML 文档的根节点描述了 API 的基本信息,例如它的标题和版本。 根部分还定义了 RAML 文档中其他地方使用的资产,例如类型和特征。处理器MUST保留节点在文档中的顺序。

#%RAML 1.0
title: GitHub API
version: v3
baseUri: https://api.github.com
mediaType:  application/json
securitySchemes:
  oauth_2_0: !include securitySchemes/oauth_2_0.raml
types:
  Gist:  !include types/gist.raml
  Gists: !include types/gists.raml
resourceTypes:
  collection: !include types/collection.raml
traits:
securedBy: [ oauth_2_0 ]
/users:
  type: collection
  get:

下表列举了 RAML 文档根目录的可能节点:

NameDescription
titleAPI 的简短纯文本标签。 它的值是一个字符串。
description?

对 API 的实质性、人性化的描述。 它的值是一个字符串,可以使用 markdown 进行格式化。

version?API 的版本,例如“v1”。 它的值是一个字符串。
baseUri?API访问的基础路径。
baseUriParameters?baseUri 中使用的命名参数。
protocols?API支持的访问协议 protocols,如果没有定义此属性值,默认为仅支持baseUri所采用的协议。
mediaType?API request和response bodies(payloads) 的媒体类型 default media types, 如 "application/json".
documentation?API 的其他整体文档。
schemas?(已弃用)

与 RAML 0.8 兼容的等效与“types”节点的别名。 已弃用 - API 定义 SHOULD 使用“types”节点,因为RAML 新版本可能会删除该节点的“schemas”别名。 “types”节点支持 XML 和 JSON 模式。

types?在 API 中使用的(数据)类型声明。
traits?在 API 中使用的方法特征 traits 声明.
resourceTypes?在 API 中使用的资源类型 resource types 声明.
annotationTypes?Declarations of annotation types for use by annotations.
(<annotationName>)?

应用于此 API 的注释。 注释是具有以“(”开头并以“)”结尾的键的映射,其中括号中的文本是注释名称,值是该注释的实例。

securitySchemes?在API中使用的安全方案 security schemes 声明.
securedBy?适用于 API 中每个资源和方法的安全方案security schemes.
uses?导入的外部库以在 API 中使用。
/<relativeUri>?

API 的资源,标识为以斜杠 (/) 开头的相对 URI。 资源节点是以斜杠开头的节点,它位于 API 定义的根节点或资源节点的子节点。 例如,/users 和/{userId}。

“schemas”和“types”节点是互斥和同义的:处理器不允许(MUST NOT)在 API 定义的根级别同时指定两者。 建议使用“types”节点而不是“schemas”,因为schemas别名已被弃用,并且可能在未来的 RAML 版本中被删除。

documentation(OPTIONAL)

文档节点是可选的,包括用作 API 的用户指南和参考文档。 此类文档可以阐明 API 的工作原理或提供技术和业务上下文。

每个文档都是一个映射,它必须(MUST)具有下表中描述的两个键值对:

NameDescription
title文档的标题。 它的值必须(MUST)是一个非空字符串。
content文档的内容。 它的值必须(MUST)是非空字符串,并且可以使用 markdown 进行格式化。​

下面示例显示了具有两个用户文档的 API 定义。

#%RAML 1.0
title: ZEncoder API
baseUri: https://app.zencoder.com/api
documentation:
 - title: Home
   content: |
     Welcome to the _Zencoder API_ Documentation. The _Zencoder API_
     allows you to connect your application to our encoding service
     and encode videos without going through the web  interface. You
     may also benefit from one of our
     [integration libraries](https://app.zencoder.com/docs/faq/basics/libraries)
     for different languages.
 - title: Legal
   content: !include docs/legal.markdown

baseUri 和 baseUriParameters(OPTIONAL)

baseUri是可选的节点,是API访问的基础路径,baseUri的值必须符合URI 规范 RFC2396 或是一个模板 URI。如果 baseUri 值是模板 URI,则以下保留的基本 URI 参数可用。

URI ParameterValue
version根节点version节点的值

模板 URI 是指 URI 参数,它是一个变量元素,包含在资源的相对 URI 内的大括号 ({}) 中。

以下示例 RAML API 定义使用模板 URI 作为基本 URI。

#%RAML 1.0
title: Salesforce Chatter REST API
version: v28.0
baseUri: https://na1.salesforce.com/services/data/{version}/chatter

以下示例声明了一个显式的baseUriParameters。

#%RAML 1.0
title: Amazon S3 REST API
version: 1
baseUri: https://{bucketName}.s3.amazonaws.com
baseUriParameters:
  bucketName:
    description: The name of the bucket

当baseUri 的值以一个或多个斜杠 (/) 结尾时,使用此 URI 绝对路径时将省略尾部斜杠。 例如,在以下代码段中,资源的绝对路径为 http://api.test.com/common/users/:userId 和 http://api.test.com/common/users/:userId/groups .

baseUri: http://api.test.com/common/
/users:
  /{userId}:
    /groups:

在以下更复杂的示例中,在多个位置有连续斜杠,只有 baseUri 中的尾部斜杠被折叠,导致这些资源的绝对路径变成://api.test.com//common/,//api.test.com//common//users//:userId// 和 //api.test.com//common//users//:userId//groups//。

baseUri: //api.test.com//common//
/:
  /users/:
    /{userId}/:
      /groups//:

protocols(OPTIONAL)

protocols 节点是可选的,用来指定API支持的访问协议。如果未明确指定协议节点,则应使用包含在 baseUri 节点中的一个或多个协议; 如果明确指定了协议节点,则此类节点规范应覆盖 baseUri 节点中包含的任何协议。 协议节点必须是一个非空的字符串数组,包含 HTTP 和/或 HTTPS 值,并且不区分大小写。

#%RAML 1.0
title: Salesforce Chatter REST API
version: v28.0
protocols: [ HTTP, HTTPS ]
baseUri: https://na1.salesforce.com/services/data/{version}/chatter

mediaType(OPTIONAL)

mediaType 节点是可选的,用来指定整个API request body和response的默认媒体类型,不需要在每个body定义中指定媒体类型。mediaType 可以配置一个或多个,如下所示:

如下示例 API 接受并返回 JSON 格式正文。 如果此 API 规范的其余部分未明确指定其他媒体类型,则此 API 仅接受并返回 JSON 格式的正文。

#%RAML 1.0
title: New API
mediaType: application/json

如下示例显示了一个用于接受和返回 JSON 或 XML 格式主体的 API 的 RAML 片段。

#%RAML 1.0
title: New API
mediaType: [ application/json, application/xml ]

在API request body或者response下显示定义mediaType节点会重写全文的默认mediaType,如下例所示,资源 /messages 通过显式定义 application/json 节点来覆盖默认媒体类型,因此资源 /messages 仅返回 JSON 格式的正文。

#%RAML 1.0
title: New API
mediaType: [ application/json, application/xml ]
types:
  Person:
  Another:
/people:
  get:
    responses:
      200:
        body: Person[]
/messages:
  post:
    body:
      application/json:
        type: Another

securedBy 和 securitySchemes(OPTIONAL)

通过这两个节点为API中每个资源的每个方法设置默认安全方案并保护它,securedBy节点的值必须是安全方案名称的数组,securitySchemes节点负责定义一个或多个安全方案。以下示例演示通过OAuth 2.0或 OAuth 1.0安全方案访问API。

#%RAML 1.0
title: Dropbox API
version: 1
baseUri: https://api.dropbox.com/{version}
securedBy: [ oauth_2_0, oauth_1_0 ]
securitySchemes:
  oauth_2_0: !include securitySchemes/oauth_2_0.raml
  oauth_1_0: !include securitySchemes/oauth_1_0.raml

RAML Data Types

RAML 1.0引入了数据类型概念,通过 types 节点来定义数据类型,数据类型添加了用于根据类型声明验证数据的规则。数据类型可以描述baseUri或resource Uri的参数、查询参数、request header或response header、request body或response body。数据类型可以是内置的或自定义的,内置类型可以在API需要数据的任何地方使用,自定义类型可以通过扩展内置类型来定义,也可以像内置类型一样命名和使用。但是扩展类型不能(MUST NOT)创建任何循环依赖,可以内联扩展类型。

下面示例自定义了一个User类型,包含firstname、lastname 和 age 属性,属性声明为内置类型string和number,后面User类型用来描述payload的类型。

#%RAML 1.0
title: API with Types
types:
  User:
    type: object
    properties:
      firstname: string
      lastname:  string
      age:       number
/users/{id}:
  get:
    responses:
      200:
        body:
          application/json:
            type: User

RAML 类型声明类似于 JSON 模式定义。 事实上,RAML 类型可以用来代替 JSON 和 XML 模式,或者与它们共存。 然而,RAML 类型语法被设计为比 JSON 和 XML 模式更简单、更简洁,同时保留了它们的灵活性和表现力。 以下代码段显示了一些类型声明的示例:

#%RAML 1.0
title: My API with Types
mediaType: application/json
types:
  Org:
    type: object
    properties:
      onCall: AlertableAdmin
      Head: Manager
  Person:
    type: object
    properties:
      firstname: string
      lastname:  string
      title?:    string
  Phone:
    type: string
    pattern: "[0-9|-]+"
  Manager:
    type: Person
    properties:
      reports: Person[]
      phone:  Phone
  Admin:
    type: Person
    properties:
      clearanceLevel:
        enum: [ low, high ]
  AlertableAdmin:
    type: Admin
    properties:
      phone: Phone
  Alertable: Manager | AlertableAdmin
/orgs/{orgId}:
  get:
    responses:
      200:
        body:
          application/json:
            type: Org

RAML 类型特征:

  • RAML类型类似于 Java 类,借鉴了面向对象语言、Json Schema和XML Schema (XSD)。
  • 你可以定义从其他类型继承的类型。并允许多重继承。
  • RAML类型可以被分为四种:external, object, array, scalar(标量).
  • RAML类型可以定义两种类型的成员:属性和构面。 两者都是继承的。属性是常规的、面向对象的属性。构面是特殊的配置。 你可以根据构面值的特征定制化类型。 示例:最小长度、最大长度。
  • 只有对象类型可以声明属性。 但所有类型都可以声明构面。
  • 要定制化标量类型,你需要实现构面,为已定义的构面赋予具体值。
  • 要定制化对象类型,你需要定义属性。

types可以用在RAML中的很多个地方:

  • Body ( JSON )
  • Body ( XML )
  • Body ( Web Form )
  • Headers
  • Query Parameters
  • URI Parameters

RAML类型定义

RAML类型应在 API 需要数据的地方内联声明,在 API 根types节点中,或在包含的库中。 要声明类型,您必须使用映射,其中键表示类型名称,值是类型声明。

types:
  Person: # key name
    # value is a type declaration

类型声明

类型声明引用另一种类型,或者通过添加功能方面(例如属性)或非功能方面(例如描述)来包装或扩展另一种类型,或者是使用其他类型的类型表达式。 以下是所有类型声明都可以具有的方面; 某些类型声明可能有其他方面:

FacetDescription
default?默认值
schema? (已弃用)与 RAML 0.8 兼容等效“type”的别名。 已弃用 - API 定义应该使用“type”,因为“schema”可能会在未来的 RAML 版本中被删除。 “type”支持 XML 和 JSON 模式。
type?当前类型扩展或引用的类型。type节点的值必须是:a) 自定义类型名称;b) 内置RAML数据类型(对象、数组、标量)的名称;c) 内联类型声明;
example?示例,如果examples节点已经定义,那么当前example节点不可用,二者不可共存
examples?示例,如果example节点已经定义,那么当前examples节点不可用,二者不可共存
displayName?类型的替代的、人性化的名称
description?对该类型的描述。 值是字符串,可以使用 markdown 进行格式化。
(<annotationName>)?要应用于此 API 的注释。 注释是具有以“(”开头并以“)”结尾的键的映射,其中括号中的文本是注释名称,值是该注释的实例。
facets?

由任何扩展子类型继承和应用的附加用户定义限制的映射。

xml?配置此类型实例的 XML 序列化的能力。 XML serialization of this type instance.
enum?此类型的所有可能值的枚举。

注意:“schema”和“type”是互斥和同义的:处理器不允许在同一类型声明中显式或隐式同时指定两者。 因此,以下示例无效:

types:
  Person:
    schema: # invalid as mutually exclusive with `type`
    type: # invalid as mutually exclusive with `schema`

/resource:
  get:
    responses:
      200:
        body:
          application/json: # start type declaration
            schema: # invalid as mutually exclusive with `type`
            type: # invalid as mutually exclusive with `schema`

内置类型

除了上面内置类型之外,RAML类型还允许JSON或XML的定义。

any类型

任何类型,无论是内置的还是用户定义的,在其继承树的根部都有 any 类型。 any 类型是一种不施加任何限制的类型,即任何数据实例都对其有效。但是any类型没有任何额外的facets。

Object类型

所有object类型都可以在其类型声明中使用以下方面:

FacetDescription
properties?对象的属性
minProperties?此类型实例允许的最小属性数。
maxProperties?此类型实例允许的最大属性数。
additionalProperties?一个布尔值,表示对象实例是否可以包含其他属性。Default: true
discriminator?

在运行时决定单个对象的具体类型,例如payloads中由于联合或继承包含不明确的类型。该值必须与当前类型的声明属性之一的名称匹配。 不支持的做法是内联类型声明和使用具有非标量属性的鉴别器。

discriminatorValue?标识声明类型。 需要在类型声明中包含discriminator。 有效值是可能标识单个对象类型的实际值,并且在类型的层次结构中是唯一的。 不支持内联类型声明。Default: The name of the type

示例如下:

#%RAML 1.0
title: My API With Types
types:
  Person:
    type: object
    properties:
      name:
        required: true
        type: string

properties声明(OPTIONAL)

properties是可选的,RAML规范将属性方面的值定义为属性声明。属性声明必须是键和值的映射。 键是用于声明类型实例的有效属性名称。 值必须是类型名称或内联类型声明。属性声明可以指定属性是必需的还是可选的。 或者,键名中的尾随问号 (?) 可用于指示属性是可选的。

FacetDescription
required?指定属性是否是必须的。Default: true.

以下示例声明一个对象有两个属性:

types:
  Person:
    properties:
      name:
        required: true
        type: string
      age:
        required: false
        type: number

上面的实例可以简写成:

types:
  Person:
    properties:
      name: string # equivalent to ->
                   # name:
                   #  type: string
      age?: number # optional property; equivalent to ->
                   # age:
                   #  type: number
                   #  required: false

如果在类型声明中明确指定了required,那么属性名称中的任何问号必须被视为属性名称的一部分,而不能作为该属性是否可选的指示符。例如profile有一个属性 preference? 包好了问号,那么有两种声明方式:a) 属性名称后追加?表示可选;b) 在属性声明中添加required来标识是否必须。示例如下:

types:
  profile:
    properties:
      preference??:
types:
  profile:
    properties:
      preference?:
        required: false

注意:当对象类型不包含“properties”节点时,假定对象不受约束,因此能够包含任何类型的任何属性。

Additional Properties

默认情况下,对象的任何实例都可以具有超出其数据类型属性方面指定的其他属性。 假设以下代码是上一节中描述的数据类型 Person 的一个实例。属性note没有在 Person 数据类型中声明,但它是有效的,因为默认情况下所有附加属性都是有效的。

Person:
  name: "John"
  age: 35
  note: "US" # valid additional property `note`

要限制属性的添加,可以将additionalProperties的值设置为 false,或者可以通过正则表达式指定匹配键集并限制其值。 后者称为“模式属性”。 模式由成对的开始和结束 / 字符描述,如下所示:

#%RAML 1.0
title: My API With Types
types:
  Person:
    properties:
      name:
        required: true
        type: string
      age:
        required: false
        type: number
      /^note\d+$/: # restrict any properties whose keys start with "note"
                   # followed by a string of one or more digits
        type: string

此模式属性限制属性键以“note”开头后跟一个或多个数字,值是字符串类型的任何其他属性。

Person:
  name: "John"
  age: 35
  note1: "US" # valid
  note2: 123 # not valid as it is not a string
  note: 123 # valid as it does not match the pattern

要强制所有附加属性为字符串类型,无论其属性名如何,请使用 // :

#%RAML 1.0
title: My API With Types
types:
  Person:
    properties:
      name:
        required: true
        type: string
      age:
        required: false
        type: number
      //: # force all additional properties to be a string
        type: string

如果模式属性正则表达式也匹配显式声明的属性,则显式声明的属性定义优先。 如果两个或多个模式属性正则表达式匹配数据类型实例中的属性名称,则以第一个为准。

此外,如果指定了additionalProperties为false(显式或通过继承),则不允许通过正则表达式显式设置模式属性。 如果指定了 additionalProperties 为true(或省略),则允许模式属性并进一步限制该类型中允许的附加属性。

object类型定制化

你可以声明继承自其他对象类型的对象类型(通过type指定继承的父类型)。 子类型继承其父类型的所有属性。 在下面的示例中,类型 Employee 继承了其父类型 Person 的所有属性。

#%RAML 1.0
title: My API With Types
types:
  Person:
    type: object
    properties:
      name:
        type: string
  Employee:
    type: Person
    properties:
      id:
        type: string

子类型可以覆盖其父类型的属性,但有以下限制:

1) 父类型中的必需属性不能更改为子类型中的可选属性;

2) 父类型中已定义属性的类型声明,只能在子类型中更改为更窄的类型(父类型的特化)。

array类型

数组类型必须通过数组限定符 [] 结尾来声明。 如果你正在定义顶级数组类型,例如下面示例中的Emails,你可以在前面描述的之外声明以下方面,以进一步限制数组类型的行为。

FacetDescription
uniqueItems?Boolean值,指示数组中的项目是否必须是唯一的。
items?指示数组中所有项的继承类型。 可以是对现有类型或内联类型声明的引用。
minItems?数组项目最小数。 值必须等于或大于 0。Default: 0.
maxItems?数组项目最大数。 值必须等于或大于 0。Default: 2147483647.

以下两个示例的写法都是正确的:

types:
  Email:
    type: object
    properties:
      subject: string
      body: string
  Emails:
    type: Email[]
    minItems: 1
    uniqueItems: true
    example: # example that contains array
      - # start item 1
        subject: My Email 1
        body: This is the text for email 1.
      - # start item 2
        subject: My Email 2
        body: This is the text for email 2.  
types:
  Email:
    type: object
    properties:
      name:
        type: string
  Emails:
    type: array
    items: Email
    minItems: 1
    uniqueItems: true

scalar类型

RAML 定义了一组内置的标量类型,每个类型都有一组预定义的限制。

String

FacetDescription
pattern?此字符串必须匹配的正则表达式。
minLength?字符串最小长度,值必选大于等于0. Default: 0
maxLength?字符串最大长度,值必选大于等于0. Default: 2147483647
types:
  EmailAddress:
    type: string
    pattern: ^.+@.+\..+$
    minLength: 3
    maxLength: 320

Number

FacetDescription
minimum?最小值
maximum?最大值
format?值的格式,值必须是其中之一: int, int8, int16, int32, int64, long, float, double.
multipleOf?如果将实例除以此关键字的值的结果是整数,则数字实例对“multipleOf”有效。
types:
  Weight:
    type: number
    minimum: -1.1
    maximum: 20.9
    format: float
    multipleOf: 1.1

Integer

1的正数或负数,继承自number类型

types:
  Age:
    type: integer
    minimum: -3
    maximum: 5
    format: int8

Boolean

没有任何附件方面描述

types:
  IsMarried:
    type: boolean

Date

必须支持以下日期类型的表示:

TypeDescription
date-onlyRFC3339 的“完整日期”表示法,即 yyyy-mm-dd。 不支持时间或时区偏移表示法。
time-onlyRFC3339 的“部分时间”表示法,即 hh:mm:ss[.ff...]。 不支持日期或时区偏移表示法。
datetime-only

将仅date-only和time-only通过“T”分隔符组合在一起,即 yyyy-mm-ddThh:mm:ss[.ff...]。 不支持时区偏移。

datetime

以下格式之一的时间戳:如果格式被省略或设置为 rfc3339,则使用 RFC3339 的“date-time”表示法; 如果格式设置为 rfc2616,则使用 RFC2616 中定义的格式。

仅当类型等于 datetime 时,附加方面 format 必须可用:

FacetDescription
format?datetime 类型的值的格式。 该值必须是 rfc3339 或 rfc2616。 任何其他值均无效。
types:
  birthday:
    type: date-only # no implications about time or offset
    example: 2015-05-23
  lunchtime:
    type: time-only # no implications about date or offset
    example: 12:30:00
  fireworks:
    type: datetime-only # no implications about offset
    example: 2015-07-04T21:00:00
  created:
    type: datetime
    example: 2016-02-28T16:41:41.090Z
    format: rfc3339 # the default, so no need to specify
  If-Modified-Since:
    type: datetime
    example: Sun, 28 Feb 2016 16:41:41 GMT
    format: rfc2616 # this time it's required, otherwise, the example format is invalid

File

文件类型可以限制内容通过表单发送。 当在 web 表单的上下文中使用此类型时,它应该表示为 JSON 格式的有效文件上传。 文件内容应该是 base64 编码的字符串。

FacetDescription
fileTypes?文件允许的content-type列表。 文件类型 */* 必须是有效值。
minLength?指定参数值的最小字节数。 该值必须等于或大于 0。Default: 0
maxLength?指定参数值的最大字节数。 该值必须等于或大于 0。Default: 2147483647
types:
  userPicture:
    type: file
    fileTypes: ['image/jpeg', 'image/png']
    maxLength: 307200
  customFile:
    type: file
    fileTypes: ['*/*'] # any file type allowed
    maxLength: 1048576

Nil类型

将属性的类型声明为 nil 表示类型实例中缺少值。 在需要 nil 类型的值(与仅类型声明相比)的 RAML 上下文中,通常使用 YAML 的null,例如当类型为 nil | number 时, 可以使用 enum: [ 1, 2, ~ ] 或更明确的 enum: [ 1, 2, !!null "" ]; 当然,在非内联表示法中,您可以完全省略该值。

nil 值与可选的属性不同。 例如,你可以定义一个comment属性是可选的并接受 nil 值,通过语法 comment?: string? 或 comment?: nil | string定义.

types:
  NilValue:
    type: object
    properties:
      name:
      comment: nil | string # equivalent to ->
                            # comment: string?
    example:
      name: Fred
      comment: # Providing a value or not providing a value here is allowed.

Union联合类型

联合类型可以用于允许数据实例由几种类型中的任何一种来描述。 联合类型必须通过一个类型表达式来声明,该类型表达式组合了由管道 (|) 符号分隔的 2 个或多个类型;

types:
  CatOrDog:
    type: Cat | Dog # elements: Cat or Dog
  Cat:
    type: object
    properties:
      name: string
      color: string
  Dog:
    type: object
    properties:
      name: string
      fangs: string

想象一种更复杂的情况,如下校验 HomeAnimal类型, 处理器必须测试六种可能的组合: [HasHome, Dog ][HasHome, Cat ][HasHome, Parrot][IsOnFarm, Dog ][IsOnFarm, Cat ], and [IsOnFarm, Parrot].

types:
   HomeAnimal: [ HasHome | IsOnFarm ,  Dog | Cat | Parrot ]

如果联合类型指定了enum,则该枚举的每个值都必须满足与至少一个类型的限制。 这是一个例子:

type: number | boolean
enum: [1, true, 2]

下面的示例是无效的写法,因为 hello 是string类型,既不是number也不是boolean类型:

type: number | boolean
enum: [1, true, 2, "hello"]

联合类型可以使用由其任何成员类型定义的属性,只要联合中的所有成员类型都接受这些属性,例如:

types:
  Foo: number
  Bar: integer
  FooBar:
    type: Foo | Bar
    minimum: 1 # valid because both "Foo" (number) and "Bar" (integer) all accept "minimum"
types:
  Foo: number
  Bar: integer
  Qux: string
  FooBarQux:
    type: Foo | Bar | Qux
    minimum: 1 # invalid because "Qux" (string) does not accept the "minimum" facet
types:
  Foo: number
  Bar: integer
  Qux:
    type: string
    facets:
      minimum: number
  FooBarQux:
    type: Foo | Bar | Qux
    minimum: 1 # valid because "Qux" (string) has a user-defined facet "minimum"

XML and JSON Schemas

RAML 允许使用 XML 和 JSON 模式来描述 API request或response body,方法是将模式集成到其数据类型系统中。

以下示例展示了如何将外部 JSON 包含到根级别类型定义和body声明中。

types:
  Person: !include person.json
/people/{personId}:
  get:
    responses:
      200:
        body:
          application/json:
            type: !include person.json

RAML 处理器不允许定义 XML 或 JSON 模式的类型参与类型继承或定制,或参与任何类型表达式。 因此,您不得定义这些类型的子类型来声明新属性、添加限制、设置构面或声明构面。 但是,您可以创建简单的类型包装器来添加注释、示例、显示名称或描述。

下面示例是有效的声明:

types:
  Person:
    type: !include person.json
    description: this is a schema describing a person

下面示例是无效的声明,因为类型继承了json特征,还添加了其他属性:

types:
  Person:
    type: !include person.json
    properties: # invalid
      single: boolean

另一个无效情况显示在以下将 Person 类型用作属性类型的示例中:

types:
  Person:
    type: !include person.json
    description: this is a schema describing a person
  Board:
    properties:
      members: Person[] # invalid use of type expression '[]' and as a property type

您可以引用一个定义在schema中的元素。 RAML 通过使用 URL 片段来支持这一点,如下例所示:

type: !include elements.xsd#Foo

用户自定义构面facet

用户定义的 facet 是在类型声明中使用可选的 facets 声明的。facets 的值必须是一个映射。 键命名用户定义的facet。 相应的值定义了各个facet可以采用的具体值的类型。

#%RAML 1.0
title: API with Types
types:
  CustomDate:
    type: date-only
    facets:
      onlyFutureDates?: boolean # optional  in `PossibleMeetingDate`
      noHolidays: boolean # required in `PossibleMeetingDate`
  PossibleMeetingDate:
    type: CustomDate
    noHolidays: true

默认类型

If, and only if, 当且仅当类型声明包含对该类型唯一的构面时,默认就是此类型。如下properties是object类型独有的构面,所以及时Person需要制定type,默认类型也是object。

types:
  Person:
    properties:

如果类型声明里的构面不是某个类型独有的,那么默认是string类型。如下name不是某个类型唯一的,所以默认是string类型。

types:
  Person:
    properties:
      name: # no type or schema necessary since the default type is `string`

body节点的默认类型是any类型,不包含propertiestype, or schema,如下所示:

body:
  application/json:
    # default type is `any`
body:
  # default type is `any`

当然了,每个规则都是可以重写的,通过显示定义type,如下所示:

types:
  Person:
    properties:
      name:
        type: number

类型表达式

类型表达式提供了一种强大的引用甚至定义类型的方法。 类型表达式可以在任何需要类型的地方使用。 最简单的类型表达式是类型的名称。 使用类型表达式,您可以设计类型联合、数组、映射和其他东西。

ExpressionDescription
Person最简单的类型表达式,一个单独的类型
Person[]Person对象数组
string[]string标量数组
string[][]字符串标量的二维数组
string | Person由string和Person组成的联合类型
(string | Person)[]联合类型数组,等价于string[] 或 Person[]

类型表达式可以用于任何需要类型的地方:

#%RAML 1.0
title: My API With Types

types:
  Phone:
    type: object
    properties:
      manufacturer:
        type: string
      numberOfSIMCards:
        type: number
  Notebook:
    type: object
    properties:
      manufacturer:
        type: string
      numberOfUSBPorts:
        type: number
  Person:
    type: object
    properties:
      devices: ( Phone | Notebook )[]
      reports: Person[]

您甚至可以从类型表达式“扩展”出其他类型。 例如:

#%RAML 1.0
title: My API With Types
types:
  Phone:
    type: object
    properties:
      manufacturer:
        type: string
      numberOfSIMCards:
        type: number
  Notebook:
    type: object
    properties:
      manufacturer:
        type: string
      numberOfUSBPorts:
        type: number
  Devices:
    type:  ( Phone | Notebook )[]

类型表达式语法

类型表达式由内置或自定义类型的名称和某些符号组成,如下所示:

Expression ComponentsDescriptionExamples
type name一个单独的类型,可以是自定义类型,也可以是内置类型number: 内置类型
Person: 自定义类型
(type expression)括号消除了运算符应用的表达式的歧义。Person | Animal[]
( Person | Animal )[]
(type expression)[]类型表达式数组string[]: string类型数组
Person[][]: Person二维数组
(type expression 1) | (type expression 2)| 联合运算符可以在多个表达式之间组合string | number: string或number
X | Y | Z: X 或 Y 或 Z
(Manager | Admin)[]: Manager[] 或 Admin[]
Manager[] | Admin[]: 和上面的等价

类型多重继承

RAML 类型支持多重继承。 这是通过传递一系列类型来实现的,如下Teacher继承自Employee,又继承自Person:

types:
  Person:
    type: object
    properties:
      name: string
  Employee:
    type: object
    properties:
      employeeNr: integer
  Teacher:
    type: [ Person, Employee ]

只有当子类型在继承其父类型的所有限制后仍然是有效的类型声明时,才允许多重继承。 不允许从不同类型的原始类型继承,例如 [number, string]。还比如一个类型设置了最小值4,另一个类型设置了最大值2,互相冲突,也是无效的,如下所示:

types:
  Number1:
    type: number
    minimum: 4
  Number2:
    type: number
    maximum: 2
  Number3: [ Number1, Number2] # invalid, maximum value cannot be less than minimum value

内联类型声明

除了在类型表达式中,您可以在可以引用类型的任何地方声明内联/匿名类型,如下通过type为firstname、lastname、age声明类型:

#%RAML 1.0
title: My API With Types
/users/{id}:
  get:
    responses:
      200:
        body:
          application/json:
            type: object
            properties:
              firstname:
                type: string
              lastname:
                type: string
              age:
                type: number

examples(highly RECOMMENDED)

Single Example

有两种方式来表示example值:作为特定类型实例的显式描述,或作为包含附加 facet 的映射。

方式一:显式描述特定类型

title: Attention needed
body: You have been added to group 274

方式二:通过包含附加facet的映射

作为一个map,可以有以下附件的facet:

FacetDescription
displayName?example name,如果example是examples节点的一部分,则默认值为为此示例定义的唯一标识符。
description?对example的描述
(<annotationName>)?要应用于此 API 的注释。 注释必须是具有以“(”开头并以“)”结尾的键的映射,其中括号中的文本是注释名称,值是该注释的实例。
valueexample value,实际示例
strict?boolean类型,是否验证示例,一般设置为 false 以避免验证。
(pii): true
strict: false
value:
  title: Attention needed
  body: You have been added to group 274

Multiple Examples

它的值必须是键值对的映射,其中每个键代表一个示例的唯一标识符,值是单个示例。

message: # {key} - unique id
  # example declaration
  title: Attention needed
  body: You have been added to group 274
record: # {key} - unique id
  # example declaration
  name: log item
  comment: permission check

在RAML的不同API如何定义示例:

#%RAML 1.0
title: API with Examples

types:
  User:
    type: object
    properties:
      name: string
      lastname: string
    example:
      name: Bob
      lastname: Marley
  Org:
    type: object
    properties:
      name: string
      address?: string
      value?: string
/organizations:
  post:
    headers:
      UserID:
        description: the identifier for the user who posts a new organization
        type: string
        example: SWED-123 # single scalar example
    body:
      application/json:
        type: Org
        example: # single request body example
          value: # needs to be declared since instance contains a 'value' property
            name: Doe Enterprise
            value: Silver
/organizations/{orgId}:
  get:
    description: Returns an organization entity.
    responses:
      201:
        body:
          application/json:
            type: Org
            examples:
              acme:
                name: Acme
              softwareCorp:
                value: # validate against the available facets for the map value of an example
                  name: Software Corp
                  address: 35 Central Street
                  value: Gold # validate against an instance of the `value` property

resources(MUST)

resource由其相对 URI 标识,该 URI 必须(MUST)以斜杠 (/) 开头。 以斜杠开头的每个节点,可以位于 API 根节点或者是资源节点的子节点。定义在根节点的resource称为顶级资源。 根节点的键是资源相对于 baseUri 的 URI。 定义在一个资源子节点的资源称为嵌套资源。

此示例显示了一个 API 定义,其中包含一个顶级资源 /gists 和一个嵌套资源 /public。

#%RAML 1.0
title: GitHub API
version: v3
baseUri: https://api.github.com
/gists:
  displayName: Gists
  /public:
    displayName: Public Gists

那么以上资源的绝对路径格式如下:

   "https://api.github.com"               <--- baseUri
               +
             "/gists"                     <--- gists resource relative URI
               +
             "/public"                    <--- public gists resource relative URI
               =
"https://api.github.com/gists/public"     <--- public gists absolute URI

嵌套资源本身可以具有子(嵌套)资源,从而创建多重嵌套资源。如下所示,/user是一个没有子资源的顶级资源,/users 是一个有子资源 /{userId}的顶级资源,/{userId} 资源下面又有三个子资源:

#%RAML 1.0
title: GitHub API
version: v3
baseUri: https://api.github.com
/user:
/users:
  /{userId}:
    uriParameters:
      userId:
        type: integer
    /followers:
    /following:
    /keys:
      /{keyId}:
        uriParameters:
          keyId:
            type: integer

那么完整的API URI绝对路径如下所示: 

https://api.github.com/user
https://api.github.com/users
https://api.github.com/users/{userId}
https://api.github.com/users/{userId}/followers
https://api.github.com/users/{userId}/following
https://api.github.com/users/{userId}/keys
https://api.github.com/users/{userId}/keys/{keyId}

RAML 处理器不允许计算的绝对路径之一与另一个相同;绝对路径的比较是在不考虑任何 URI 参数的可能值的情况下进行的。如下示例绝对路径是重复的,是禁止的:

/users:
  /foo:
/users/foo:

但是以下示例是允许的:

/users/{userId}:
/users/{username}:
/users/me:

Resource Property

资源节点的值必须是包含下表中描述的键值对的映射。

NameDescription
displayName?资源名称。 如果没有定义 displayName,默认资源名称的键。
description?对资源的描述
(<annotationName>)? 要应用于此 API 的注释。 注释必须是具有以“(”开头并以“)”结尾的键的映射,其中括号中的文本是注释名称,值是该注释的实例。
get?
patch?
put?
post?
delete?
options?
head?
API请求方法类型method.
is?此资源的所有方法的特征列表。 个别方法可以覆盖此声明。
type?此资源继承的资源类型。
securedBy?适用于此资源的所有方法的安全方案。
uriParameters?有关此资源的任何 URI 参数的详细信息。
/<relativeUri>?嵌套资源是名称以斜杠 (/) 开头的任何节点。 因此,该资源应被视为相对 URI。

Template URIs and URI Parameters

Template URIs包含URI Parameters,如下 /{jobId}:

#%RAML 1.0
title: ZEncoder API
version: v2
baseUri: https://app.zencoder.com/api/{version}
/jobs: # its fully-resolved URI is https://app.zencoder.com/api/{version}/jobs
  description: A collection of jobs
  /{jobId}: # its fully-resolved URI is https://app.zencoder.com/api/{version}/jobs/{jobId}
    description: A specific job, a member of the jobs collection

uriParameters 节点的值是一个映射,尤其是属性声明,就像类型声明一样。 声明对象中的每个属性都是一个 URI 参数声明。 每个属性名称对应于模板 URI 中的一个参数名称。 每个属性值都将 URI 参数类型指定为类型名称或内联类型声明。

uriParameters 声明中的每个属性都必须与资源的相对 URI 中的 URI 参数的名称完全对应。 相对 URI 中的所有 URI 参数不需要在 uriParameters 节点中显式指定,但那些未指定的必须被视为字符串类型的 URI 参数并且是必需的。

#%RAML 1.0
title: GitHub API
version: v3
baseUri: https://api.github.com
/user:
  description: The currently authenticated User
/users:
  description: All users
  /{userId}:
   description: A specific user
   uriParameters:
     userId:
       description: The id of the user
       type: integer

如果 URI 参数声明指定了非标量类型的数组、对象或联合,则处理器必须默认将URI 参数实例的值当成JSON 类型。在此示例中,URI 参数 userIds 是一个 id 数组。 假设数组包含 [blue,green],它在网络上可能显示为 /users/%5B%22blue%22,%22green%22%5D/。

#%RAML 1.0
title: Serialization API

/users:
  description: All users
  /{userIds}:
    description: A specific user
    uriParameters:
      userIds:
        description: A list of userIds
        type: array
    	items:
    	  type: string
    	  minLength: 1
    	uniqueItems: true

Methods

RESTful API 方法是对资源执行的操作。 资源的 OPTIONAL 属性 get、patch、put、post、delete、head 和 options 定义了它的方法。 这些属性对应于 HTTP 版本 1.1 规范 RFC2616 及其扩展 RFC5789 中定义的 HTTP 方法。 这些方法属性的值必须是具有以下键值对的映射:

NameDescription
displayName?方法名字,如果没有定义,则默认方法键为名字。
description?对方法的描述
(<annotationName>)?要应用于此 API 的注释。 注释是具有以“(”开头并以“)”结尾的键的映射,其中括号中的文本是注释名称,值是该注释的实例。
queryParameters?此方法所需的查询参数的详细信息。 与 queryString 互斥。
headers?有关此方法所需的请求头headers的详细信息。
queryString?方法所需的查询字符串,与 queryParameters 互斥。
responses?对请求的预期响应的信息。
body?该方法的request body
protocols?允许访问该方法的协议
is?此方法的特征列表。
securedBy?此方法的安全方案

Headers(OPTIONAL)

headers的值必须是一个映射,以下示例演示一个post method的HTTP header名为Zencoder-Api-Key:

#%RAML 1.0
title: ZEncoder API
version: v2
baseUri: https://app.zencoder.com/api/{version}
/jobs:
  post:
    description: Create a job
    headers:
      Zencoder-Api-Key:
        description: The API key needed to create a new job

如果标头声明为标头的值指定了数组类型,处理器必须允许在请求或响应中出现该标头的多个实例。 在这种情况下,数组元素的类型必须作为头实例值的类型应用。

#%RAML 1.0
title: Example with headers
traits:
  chargeable:
    headers:
      X-Dept:
        type: array
        description: |
          A department code to be charged.
          Multiple of such headers are allowed.
        items:
          pattern: ^\d+\-\w+$
          example: 230-OCTO
  traceable:
    headers:
      X-Tracker:
        description: A code to track API calls end to end
        pattern: ^\w{16}$
        example: abcdefghijklmnop
/users:
  get:
    is: [ chargeable, traceable ]
    description: |
      The HTTP interaction will look like

      GET /users HTTP/1.1
      X-Dept: 18-FINANCE
      X-Dept: 200-MISC
      X-Tracker: gfr456d03ygh38s2
    headers:
      X-Dept:
        example: [ 18-FINANCE, 200-MISC ]
      X-Tracker:
        example: gfr456d03ygh38s2

Query Strings和Query Parameters(OPTIONAL)

API 方法可能支持或需要在调用该方法的 URL 中的查询字符串。 URL 中的查询字符串用于 URL 中问号分隔符 (?) 和任何片段 (#) 分隔符之前的部分。 查询字符串可以由 queryString 节点或 queryParameters 节点指定。 queryString 和 queryParameters 节点互斥:处理器不得允许在同一资源的同一方法上显式或隐式同时指定两者。

Query String作为一个整体

queryString 节点可用于将查询字符串指定为一个整体,而不是名称-值对。 queryString 值必须是数据类型的名称或内联数据类型声明,包括数据类型表达式。

如果该类型派生自标量类型,则查询字符串作为一个整体必须由该类型描述。

如果类型派生自对象类型,处理器必须将查询字符串视为此对象类型实例的 URL 编码序列化。 查询字符串的格式必须为“parameter1=value1&parameter2=value2&...”,其中“parameter1”、“parameter2”等对应于对象类型中的属性。 同样,“value1”、“value2”等对应于对象类型中的值规范。 如果对象类型中的属性值是数组类型,处理器必须允许在查询字符串中出现该查询参数的多个实例。 在这种情况下,数组元素的类型必须应用为该查询参数实例值的类型。

#%RAML 1.0
title: Illustrate query parameter variations
types:
  lat-long: # lat & long required; mutually exclusive with location
    properties:
      lat: number
      long: number
  loc: # location required; mutually exclusive with lat & long
    properties:
      location:
  paging: # each is optional, not exclusive with anything
    properties:
      start?: number
      page-size?: number
/locations:
  get:
    queryString:
      type: [paging,  lat-long | loc ]
      examples:
        first:
          value:
            start: 2
            lat: 12
            long: 13
        second:
          value:
            start: 2
            page-size: 20
            location: 1,2
        third:  # not valid
          value:
            lat: 12
            location: 2
          strict: false # because it's not valid

Query String中的Query Parameters

queryParameters 节点指定组成查询字符串的查询参数集。

以下示例演示一个get 方法使用 HTTP queryParameters。 使用示例值向 https://api.github.com/v3/users?page=1&per_page=50 发送请求。

#%RAML 1.0
title: GitHub API
version: v3
baseUri: https://api.github.com/{version}
/users:
  get:
    description: Get a list of users
    queryParameters:
      page:
        description: Specify the page that you want to retrieve
        type:        integer
        required:    true
        example:     1
      per_page:
        description: Specify the amount of items that will be retrieved per page
        type:        integer
        minimum:     10
        maximum:     200
        default:     30
        example:     50

body(OPTIONAL)

可以使用 body 节点指定方法的 HTTP request body。 例如POST 或 PUT方法,请求主体通常会包含要创建的资源的详细信息。

参考文献

官方网站:http://raml.org

规范文档:raml-spec/raml-10.md at master · raml-org/raml-spec · GitHub

raml-spec/raml-10.md at master · raml-org/raml-spec · GitHub

设计器:GitHub - mulesoft/api-designer: A web editor for creating and sharing RAML API specifications

JS解析器:GitHub - raml-org/raml-js-parser: (deprecated) A RAML parser based on PyYAML written in CoffeScript and available for use as NodeJs module or in-browser., 生成浏览器可访问的API文档

Java解析器:GitHub - raml-org/raml-java-parser: (deprecated) A RAML parser based on SnakeYAML written in Java, 基于该解析器我们可以开发出生成API文档、API调用客户端代码以及将API定义导入到服务中心的功能。


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值