RAML 规范(中文版)
    RAML 规范(中文版)
    • RAML 规范(RESTful API Modeling Language)

    RAML 规范(RESTful API Modeling Language)

    介绍

    本规范描述了 RESTful API 建模语言 (RAML)。RAML 是一种人类和机器可读的语言,用于定义 RESTful 应用程序编程接口 (API)。RAML 旨在通过提供 API 提供者和 API 消费者可以用作相互契约的格式来改进 API 规范。例如,RAML 可以帮助为客户端和服务器实现提供用户文档和源代码存根。此类规定简化并增强了利用 RESTful API 的可互操作应用程序的定义和开发。

    RAML 引入了资源类型和特征的创新概念,用于描述和重用资源模式和相关方法。使用资源类型和特征可以减少 RESTful API 设计中的重复,并在 API 内部和跨 API 中促进一致性。

    本文档的结构如下:

    • 基本信息:如何描述 API 的核心方面,例如名称、标题、位置(或 URI)和默认值,以及如何包含 API 的支持文档。
    • 数据类型:通过包含 JSON 架构和 XML 架构 (XSD) 的简化类型系统对 API 数据进行建模。
    • 资源:如何指定 API 资源和嵌套资源,以及任何 URI 模板中的 URI 参数。
    • 方法:如何指定 API 资源的方法及其请求标头、查询参数和请求正文。
    • 回应:API响应的规范,包括状态代码、媒体类型、响应标头和响应正文。
    • 资源类型和特征:可选择使用 RAML 资源类型和特征来表征资源。
    • 安全性:在 RAML 中指定 API 安全方案。
    • 注释:通过定义强类型注释并在整个规范中应用它们来扩展 RAML 规范。
    • 包括库、覆盖层和扩展:API 定义如何由外部化定义文档组成,将此类定义的集合打包到库中,在 RAML 文档上分离和覆盖元数据层,以及使用附加功能扩展 API 规范。

    RAML 1.0的新特性和差异:

    • 数据类型:一种统一、简洁且功能强大的方式来建模 API 中可能出现的任何数据。

      • 统一涵盖主体、URI 参数、头和查询参数,并消除了对单独的 formParameters 构造的需求。
      • 支持包装 XML 和 JSON 模式,并引用子模式,但在许多情况下,无需使用模式
      • 与 JSON Schema 或 XML Schema (XSD) 相比,简化了编码,因为它基于 YAML
    • 示例:多个示例,可以用 YAML 表达,并且可注释,因此可以注入语义。

    • 注解:一种久经考验的强类型可扩展性机制。

    • 库:改进了模块化以广泛重用 API 工件。

    • 覆盖和扩展:通过分离的文件增加了可扩展性。

    • 改进的安全方案:

      • 更广泛的 OAuth 支持
      • 支持通过(基于密钥)的安全方案
    • 为了一致性和表达力,进行了一些较小的更改。

    标记语言

    该规范使用YAML 1.2作为其基础格式。YAML 是一种人类可读的数据格式,非常符合本规范的设计目标。与 YAML 中一样,所有节点(例如键、值和标签)都区分大小写。

    RAML API 定义必须是符合 YAML 1.2 的文档,以指示 RAML 版本的必需 YAML 注释行开头,如下所示:

    #%RAML 1.0
    title: My API
    

    RAML API 定义文档的第一行必须以文本 #%RAML开头,后面是一个空格,然后是文本1.0,并且在行尾之前不能有其他内容。RAML 片段文档类似地以 RAML 版本注释和片段标识符开头,但其本身并不是 RAML API 定义文档。

    媒体类型 application/raml+yaml 及其相关文件扩展名.raml 应用于表示包含 RAML API 定义、RAML 片段和包含RAML 标记的文件。RAML 还能够包含其他媒体类型的文档,如 “application/schema+json” 和 “application/yaml”。

    为了促进 RAML 文档的自动化处理,除了核心 YAML 1.2 规范之外,RAML 还施加了以下限制和要求:

    • RAML 文件的第一行必须包含指定 RAML 版本的 YAML 注释。因此,RAML 处理器不应完全忽略所有 YAML 注释。
    • RAML 文档中某些级别的某些属性的顺序很重要。因此,处理器应保留此顺序。

    文档的根源

    根节是 RAML 文档的一部分,用于描述 API 的基本信息,例如标题和版本。根节还定义了在 RAML 文档的其他地方使用的资产,例如类型和特征。

    RAML 文档中定义的 API 节点可以按任何顺序出现。处理器必须保留同一定义树节点中同一种类型节点的顺序。这些节点的示例包括位于资源树同一级别的资源、给定资源的方法、给定方法的参数以及给定类型的节点。处理器还必须保留数组中各项的顺序。

    此示例显示了 GitHub v3 公共 API 的 RAML API 定义的一小部分。

    #%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 文档根部可能的节点:

    姓名描述
    标题API 的简短纯文本标签。其值为字符串。
    描述?对 API 进行实质性、人性化的描述。它的值是一个字符串,可以使用markdown进行格式化。
    版本?API 的版本,例如“v1”。其值为字符串。
    baseUri?充当所有资源URI 基础的URI 。通常用作包含 API 位置的每个资源的 URL 的基础。可以是模板 URI。
    baseUriParameters?baseUri (模板)中使用的命名参数。
    协议?API 支持的协议。
    媒体类型?用于请求和响应主体(有效负载)的默认媒体类型,例如“application/json”。
    文档?API 的附加整体文档。
    模式?RAML 0.8 中“类型”节点的别名,用于兼容性。已弃用-API 定义应使用“类型”节点,因为未来的 RAML 版本可能会删除“模式”别名,并用该节点替换。“类型”节点支持 XML 和 JSON 模式。
    类型?API 中使用的(数据)类型声明。
    特质?API 中使用的特征声明。
    资源类型?在 API 中使用的资源类型声明。
    注释类型?供注释使用的注释类型的声明。
    (<注释名称>)?应用于此 API 的注释。注释是一个映射,其键以“(”开头并以“)”结尾,其中括号中的文本是注释名称,值是该注释的实例。
    安全方案?API 中使用的安全方案的声明。
    担保者?适用于 API 中每个资源和方法的安全方案。
    用途?导入外部库以在 API 中使用。
    /<相对Uri>?API 的资源,标识为以斜杠 (/) 开头的相对 URI。资源节点是以斜线开头的节点,位于 API 定义的根节点或资源节点的子节点。例如,/users和/{groupId}。

    “模式”和“类型”节点是互斥且同义的:处理器不得允许在 API 定义的根级别指定两者。我们建议使用“types”节点而不是“schema”,因为 schemas 别名已弃用,并且可能会在未来的 RAML 版本中删除。

    用户文档

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

    文档节点的值必须是一个或多个文档的序列。每个文档都是一个映射,必须具有下表中描述的两个键值对:

    姓名描述
    title文档的标题。它的值必须是非空字符串。
    content文档的内容。它的值必须是非空字符串,并且可以使用 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
    

    基本 URI 和基本 URI 参数

    可选的 baseUri 节点将 URI 指定为整个 API 的标识符,并且可以用于指定提供 API 的 URL(其服务端点),并且该 URL 构成其每个资源的 URL 的基础。baseUri 节点的值是一个必须符合 URI 规范RFC2396或模板 URI 的字符串。

    如果 baseUri 值是Template URI,则以下保留的基本 URI 参数可用。

    URI参数价值
    版本根级版本节点的值

    出现在 baseUri 中的任何其他 URI 模板变量可以在API 定义根部的baseUriParameters节点中显式描述。 baseUriParameters 节点具有相同的语义,因此必须遵循与资源节点上的uriParameters节点相同的结构,除了它在基本 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
    

    以下示例声明显式基本 URI 参数。

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

    当基本 URI 以一个或多个斜杠 ( /) 结尾时,使用该基本 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:
    

    在下面的更复杂的示例中,多个位置都有连续的斜杠,只有基本 URI 中的尾部斜杠被折叠,从而导致这些资源的绝对路径://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//:
    

    协议

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

    以下是同时接受 HTTP 和 HTTPS 请求的 API 端点的示例。

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

    默认媒体类型

    指定 OPTIONAL mediaType节点将为具有正文的响应和请求设置默认媒体类型。您不需要在每个正文定义中指定媒体类型。

    mediaType 节点的值必须是媒体类型字符串序列或单个媒体类型字符串。媒体类型适用于具有正文的请求、预期响应以及使用相同媒体类型字符串序列的示例。每个值必须符合RFC6838中的媒体类型规范。

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

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

    此示例显示了 API 的 RAML 代码段,该 API 接受并返回 JSON 或 XML 格式的主体。

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

    mediaType为 API 请求或响应正文显式定义节点会覆盖默认媒体类型,如以下示例所示。该资源/people返回Person[]JSON 或 XML 格式的正文。该资源/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
    

    默认安全

    指定 OPTIONAL secureBy节点将为 API 中每个资源的每个方法设置默认安全方案并对其进行保护。节点的值必须是安全方案名称的数组。有关详细信息,请参阅应用安全方案部分,包括如何通过继承解决多个安全方案的应用。

    以下示例显示了允许通过 OAuth 2.0 安全方案或 OAuth 1.1 安全方案进行访问的 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 数据类型

    介绍

    RAML 1.0 引入了数据类型的概念,它提供了一种简洁而强大的方式来描述 API 中的数据。数据类型添加了根据类型声明验证数据的规则。有效数据遵守该类型的所有规则。数据类型可以描述基本或资源 URI 参数、查询参数、请求或响应标头、或者请求或响应主体。数据类型是内置的或自定义的。内置类型可以用在 API 需要数据的任何地方。自定义类型可以通过扩展内置类型来定义,并且可以像内置类型一样命名和使用。扩展类型不得创建任何循环依赖项。类型可以内联扩展。

    以下 RAML 示例定义了一个 User 类型,其中包括名字、姓氏和年龄属性的类型声明。该示例将属性声明为内置类型字符串和数字。随后,用户类型用于描述有效负载的类型(模式)。

    #%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 类型系统受面向对象编程语言(如 Java)以及 XML Schema (XSD) 和 JSON Schema 的影响。

    RAML 类型简而言之:

    • 类似 Java 中的类。

      • 受 JSON、XSD 等更表达力的面向对象语言的影响。
    • 可以从其它类型继承,支持多重继承。

      • 允许多重继承。
    • 类型分为四个系列:外部、对象、数组和标量。

    • 类型可以定义两种类型的成员:属性和特征。两者都是继承的。

      • 属性是常规的、面向对象的属性。
      • 特征是特殊的配置。您可以根据方面值的特征来专门化类型。示例:minLength、maxLength
    • 只有对象类型才能声明属性,所有类型都可以声明特征。

    • 为了专门化标量类型,您可以实现构面,为已定义的构面提供具体值。

    • 要专门化对象类型,您可以定义属性。

    定义类型

    类型可以直接在 API 所需数据处内联声明,也可以在 API 根节点的可选 types 节点中声明,亦或是采用引入库的方式声明。要声明一种类型,你必须使用 map,其中 key 表示类型的名称,value 表示类型的声明。

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

    定义类型

    类型应在 API 需要数据的位置、API 根部的可选类型节点或包含的库中内联声明。 要声明类型,必须使用映射,其中键代表类型的名称,其值是类型声明。

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

    类型声明

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

    类型描述
    default? ?对于一个类型来说,都是有一个默认值的,当 API 请求完全缺失一个类型的实例时,比如查询参数被一个类型描述,但请求中完全没有出现,那么 API 就必须像 API 客户端发送了一个该类型的实例,且该实例的值为 default facet 中的值一样。相似的
    schema?为了与 RAML 0.8 的兼容性,等价的"type" facet的别名。不推荐 - API 定义应该使用 "type" facet,因为在未来的 RAML 版本中,可能会移除 "schema" 的别名。 "type" facet支持 XML 和 JSON 架构。
    type?当前类型扩展或仅包装的类型。类型节点的值必须是 a) 用户定义类型的名称或 b) 内置 RAML 数据类型(对象、数组或标量类型之一)的名称或 c) 内联类型宣言。
    example?例如,文档生成器可以使用该类型的实例来生成该类型的对象的样本值。当“examples”方面已经定义时,“example”方面不得可用。有关详细信息,请参阅示例部分。
    examples?此类实例的示例。例如,文档生成器可以使用它来生成此类型的对象的样本值。当“example”方面已经定义时,“examples”方面不得可用。有关详细信息,请参阅示例部分。
    displayName?该类型的替代的、人类友好的名称
    description?对类型的实质性、人性化描述。它的值是一个字符串,可以使用markdown进行格式化。
    (<注释名称annotationName>)?应用于此 API 的注释。注释是一个映射,其键以“(”开头并以“)”结尾,其中括号中的文本是注释名称,值是该注释的实例。
    facets?附加的、用户定义的限制的映射,将由任何扩展子类型继承和应用。有关更多信息,请参阅用户定义的 Facet部分。
    xml?配置此类型实例的 XML 序列化的能力。
    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`
    

    我们建议使用"type"特性而不是"schema",因为"schema"这个别名已经申明作废,且在未来的 RAML 版本可能会被移除。此外,"type"特性支持 XML 和 JSON 架构。

    内置类型

    RAML 类型系统定义了以下内置类型:

    • 任何
    • 对象
    • 数组
    • 通过类型表达式 union
    • 以下标量类型之一:number, boolean, string, date-only, time-only, datetime-only, datetime, file, integer, 或 nil

    除了内置类型之外,RAML 类型系统还允许定义 JSON 或 XML 模式。

    下图显示了继承树,从根级别开始any。

    typesHierarchy.png

    “任何”类型

    每个类型,无论是内置类型还是用户定义类型,都any在其继承树的根部具有该类型。根据定义,该any类型是不施加任何限制的类型,即任何数据实例都对其有效。

    任何类型的“基类”,是指该类型继承树中直接继承自 any 类型根节点的类型;因此,例如,如果一个自定义类型 status 继承自内置类型 integer,该内置类型又继承自内置类型 number,而 number 又继承自 any 类型,那么 status 的基类就是 number。注意,一个类型可以有不止一个基类。

    any 类型没有任何附加特性。

    对象类型

    所有在其继承树中具有内置对象基类型的类型都可以在其类型声明中使用以下方面:

    分面描述
    properties?该类型的实例可以或必须具有的属性。
    minProperties?该类型的实例允许的最小属性数。
    maxProperties?此类型的实例允许的最大属性数。
    additionalProperties?一个布尔值,指示对象实例是否可以包含其他属性。 默认:  true
    discriminator?例如,当有效负载由于联合或继承而包含不明确的类型时,确定运行时单个对象的具体类型。properties该值必须与声明的类型之一的名称匹配。不支持的做法是内联类型声明和与非标量属性一起使用。discriminator
    discriminatorValue?标识声明类型。需要discriminator在类型声明中包含一个方面。有效值是可以标识单个对象的类型并且在类型的层次结构中唯一的实际值。不支持内联类型声明。 默认值: 类型的名称

    例子:

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

    对象类型的属性是使用 OPTIONAL属性方面定义的。RAML 规范将构面的值称为properties“属性声明”。属性声明必须是键和值的映射。键是用于声明类型实例的有效属性名称。这些值必须是类型名称或内联类型声明。

    属性声明可以指定属性是必需的还是可选的。或者,可以使用键名称中的尾随问号 ( ?) 来指示属性是可选的。

    刻面描述
    必需的?指定该属性是否是必需的。 默认值:  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在类型声明中显式指定属性的方面时,其属性名称中的任何问号都必须被视为属性名称的一部分,而不是作为该属性是可选的指示符。

    例如,在

    types:
      profile:
        properties:
          preference?:
            required: true
    

    该profile类型有一个名为 的属性preference?,其中包含尾随问号。以下代码片段显示了两种设置preference?可选的方法:

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

    或者

    types:
      profile:
        properties:
          preference??:
    

    笔记:

    当对象类型不包含“属性”方面时,该对象被认为是不受约束的,因此能够包含任何类型的任何属性。

    附加属性

    默认情况下,任何对象的实例都可以拥有超出数据类型属性分面所指定的额外属性。假设以下代码是前一节中所描述的 Person 数据类型的实例:

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

    该属性note未在Person数据类型中显式声明,但它是有效的,因为默认情况下所有其他属性都是有效的。

    要限制属性的添加,您可以将构面的值设置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”开头,后跟一个或多个数字的字符串的任何其他属性。note1因此,声明具有值“US”的附加属性的对象实例的示例是有效的,但该属性note2对于非字符串值是无效的:

    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
    

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

    此外,如果additionalPropertiesis false(显式或通过继承)在给定类型定义中,则不允许在该定义中显式设置模式属性。如果在给定类型定义中,additionalProperties 为 true(或未指定),则允许模式属性,并further restrict 在该类型中允许的其他属性。

    对象类型专业化

    您可以声明从其他对象类型继承的对象类型。子类型继承其父类型的所有属性。在以下示例中,类型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)父类型中已定义属性的类型声明只能在子类型中更改为更窄的类型(父类型的特化)。

    使用鉴别器

    当有效负载由于联合或继承而包含不明确的类型时,通常不可能在运行时区分单个对象的具体类型。例如,当将有效负载反序列化为静态类型语言时,可能会出现此问题。

    RAML 处理器可以提供自动从一组可能类型中选择具体类型的实现,但更简单的替代方案是存储与对象内的类型关联的唯一值。

    您可以使用判别器 facet 设置对象属性的名称。 对象属性的名称可以用于区分具体类型。 您可以使用 discriminatorValue 存储标识单个对象类型的值。 默认情况下,discriminatorValue 的值为类型的名称。

    这是一个说明如何使用的示例discriminator:

    #%RAML 1.0
    title: My API With Types
    types:
      Person:
        type: object
        discriminator: kind # refers to the `kind` property of object `Person`
        properties:
          kind: string # contains name of the kind of a `Person` instance
          name: string
      Employee: # kind can equal `Employee`; default value for `discriminatorValue`
        type: Person
        properties:
          employeeId: integer
      User: # kind can equal `User`; default value for `discriminatorValue`
        type: Person
        properties:
          userId: integer
    
    data:
      - name: A User
        userId: 111
        kind: User
      - name: An Employee
        employeeId: 222
        kind: Employee
    

    你可以为每个具体子类重写默认的 discriminatorValue 。下面的例子将默认值中的首字母大写替换为小写:

    #%RAML 1.0
    title: My API With Types
    types:
      Person:
        type: object
        discriminator: kind
        properties:
          name: string
          kind: string
      Employee:
        type: Person
        discriminatorValue: employee # override default
        properties:
          employeeId: string
      User:
        type: Person
        discriminatorValue: user # override default
        properties:
          userId: string
    
    data:
      - name: A User
        userId: 111
        kind: user
      - name: An Employee
        employeeId: 222
        kind: employee
    

    discriminator 和 discriminatorValue 必须不能在任何内联类型声明或联合类型中定义。

    # valid whenever there is a key name that can identify a type
    types:
      Device:
        discriminator: kind
        properties:
          kind: string
    
    # invalid in any inline type declaration
    application/json:
       discriminator: kind
       properties:
         kind: string
    
    # invalid for union types
    PersonOrDog:
       type: Person | Dog
       discriminator: hasTail
    

    数组类型

    数组类型必须使用类型表达式[]末尾的数组限定符或作为构面的值来声明。如果你定义一个顶级的数组类型,比如下面的例子中的 Emails,你可以声明以下特征,除了先前描述的,以进一步限制数组类型的行为:

    名称描述
    uniqueItems?布尔值,指示数组中的项目是否必须唯一。
    items?指示数组中所有项目继承的类型。可以是对现有类型或内联类型声明的引用。
    minItems?数组中项目的最小数量。值必须等于或大于 0。 默认值:  0。
    maxItems?数组中的最大项目数。值必须等于或大于 0。 默认值:  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
    

    使用Email[]相当于使用type: array。该items构面将Email类型定义为每个数组项继承的类型。

    标量类型

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

    细绳

    具有以下附加方面的 JSON 字符串:

    片段描述
    pattern?该字符串必须匹配的正则表达式。
    minLength?字符串的最小长度。值必须等于或大于 0。 默认值:  0
    maxLength?字符串的最大长度。值必须等于或大于 0。 默认值:  2147483647

    例子:

    types:
      EmailAddress:
        type: string
        pattern: ^.+@.+..+$
        minLength: 3
        maxLength: 320
    
    数字

    具有以下附加方面的任何 JSON 数字:

    刻面描述
    最低限度?最小值。
    最大限度?最大值。
    格式?值的格式。该值必须是以下之一:int、int8、int16、int32、int64、long、float、double。
    多个?如果实例除以该关键字的值的结果是整数,则数字实例对于“multipleOf”有效。

    例子:

    types:
      Weight:
        type: number
        minimum: -1.1
        maximum: 20.9
        format: float
        multipleOf: 1.1
    
    整数

    任何 1 的正数或负数倍的 JSON 数字。 整数类型从数字类型继承其方面。

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

    没有任何其他方面的 JSON 布尔值。

    types:
      IsMarried:
        type: boolean
    
    日期

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

    类型描述
    date-onlyRFC3339的“完整日期”表示法,即yyyy-mm-dd. 不支持时间或时区偏移表示法。
    time-onlyRFC3339的“部分时间”表示法,即 hh:mm:ss[.ff...]。不支持日期或时区偏移表示法。
    datetime-only将仅日期和仅时间与“T”分隔符组合起来,即 yyyy-mm-ddThh:mm:ss[.ff...]。不支持时区偏移。
    datetime采用以下格式之一的时间戳:如果省略格式或设置为 ,则使用RFC3339rfc3339的“日期时间”表示法;如果format设置为,则使用RFC2616中定义的格式。**rfc2616

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

    特性描述
    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
    
    文件

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

    特性描述
    fileTypes?文件的有效内容类型字符串列表。文件类型*/*必须是有效值。
    minLength?指定参数值的最小字节数。该值必须等于或大于 0。 默认值:  0
    maxLength?指定参数值的最大字节数。该值必须等于或大于 0。 默认值:  2147483647
    types:
      userPicture:
        type: file
        fileTypes: ['image/jpeg', 'image/png']
        maxLength: 307200
      customFile:
        type: file
        fileTypes: ['*/*'] # any file type allowed
        maxLength: 1048576
    
    无记号型

    在 RAML 中,该类型nil是标量类型,只允许 nil 数据值。具体来说,在 YAML 中,它仅允许 YAML(或其相应表现形式)的 null(用 ~ 表示)。在 JSON 中,它仅允许 JSON 的null,而在 XML 中,它仅允许 XML 的xsi:nil。在标头、URI 参数和查询参数中,类型nil应仅允许字符串值“nil”(区分大小写);反过来,当用类型描述时,具有字符串值“nil”(区分大小写)的实例nil应反序列化为 nil 值。

    nil在联合中使用类型会使类型定义为 nilable,这意味着该联合的任何实例都可以是 nil 数据值。当此类联合仅由 之外的一种类型组成时| nil,使用尾随问号?代替联合语法是等效的。该等效替代语法的使用应仅限于标量类型和对用户定义类型的引用,并且不应在类型表达式中使用。

    在以下示例中,类型是一个对象,并且具有两个必需属性name和comment,两者都默认为 type string。在示例中,name分配了一个字符串值,但 comment 为 nil,这是不允许的,因为 RAML 需要一个字符串。

    types:
      NilValue:
        type: object
        properties:
          name:
          comment:
        example:
          name: Fred
          comment: # Providing no value here is not allowed.
    

    以下示例显示了nil类型的分配comment:

    types:
      NilValue:
        type: object
        properties:
          name:
          comment: nil
        example:
          name: Fred
          comment: # Providing a value here is not allowed.
    

    以下示例演示如何使用联合表示可为 nilable 的属性:

    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.
    

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

    Niilable 值与可选属性不同。例如,您可以使用语法或定义一个comment可选且接受值的属性。nil``comment?: string?``comment?: nil | string

    联合型

    联合类型可以用于允许数据实例由多种类型中的任何一种来描述。联合类型必须通过类型表达式来声明,该类型表达式组合了由管道 ( |) 符号分隔的 2 个或多个类型;这些组合类型称为联合类型的超类型。在下面的示例中,类型的实例Device可以由类型Phone或Notebook类型来描述:

    #%RAML 1.0
    title: My API With Types
    types:
      Phone:
        type: object
        properties:
          manufacturer:
            type: string
          numberOfSIMCards:
            type: number
          kind: string
      Notebook:
        type: object
        properties:
          manufacturer:
            type: string
          numberOfUSBPorts:
            type: number
          kind: string
      Device:
        type: Phone | Notebook
    

    当且仅当它满足与至少一种超类型相关的所有限制时,联合类型的实例才应被视为有效。更具体地说,在其类型层次结构中具有联合类型的类型的实例当且仅当它是通过扩展该类型层次结构中的所有联合而获得的至少一个超类型的有效实例时才应被视为有效。这样的实例通过执行此扩展进行反序列化,然后将实例与所有超类型进行匹配,从最左边开始向右进行;第一个成功匹配的基类型用于反序列化实例。

    下面的示例定义了两种类型和第三种类型,它是这两种类型的联合。

    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
    

    以下类型实例的示例CatOrDog是有效的:

    CatOrDog: # follows restrictions applied to the type 'Cat'
      name: Musia,
      color: brown
    

    想象一下在多重继承类型表达式中使用联合类型的更复杂的示例:

    types:
       HasHome:
         type: object
         properties:
           homeAddress: string
       Cat:
         type: object
         properties:
           name: string
           color: string
       Dog:
         type: object
         properties:
           name: string
           fangs: string       
       HomeAnimal: [ HasHome ,  Dog | Cat ]
    

    在本例中, typeHomeAnimal有两个超类型HasHome和一个匿名联合类型,由以下类型表达式定义:Dog | Cat。

    验证HomeAnimal类型涉及验证从每个超类型派生的类型以及联合类型中每个元素的类型。在这种特殊情况下,处理器必须测试该类型[HasHome, Dog]和[HasHome, Cat]是有效类型。

    如果您从两种联合类型扩展,处理器必须对每种可能的组合执行验证。例如,要验证HomeAnimal下面所示的类型,处理器必须测试六种可能的组合:[HasHome, Dog ]、[HasHome, Cat ]、[HasHome, Parrot]、[IsOnFarm, Dog ]、[IsOnFarm, Cat ]和[IsOnFarm, Parrot]。

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

    如果联合类型包含带有 的构面enum,则该构面的每个值都enum必须满足与至少一种超类型相关的所有限制。这是一个例子:

    以下示例说明了有效的表达式:

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

    以下示例说明了无效表达式:

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

    请注意,在这种情况下,类型可以是内置数据类型,例如数字或布尔值,也可以是自定义的用户定义类型,例如联合或具有多个属性的复杂类型。想象一个更复杂的例子:

    #%RAML 1.0
    title: Scheduling API
    
    types:
      CustomDates:
        enum: [Monday12, Tuesday18, Wednesday7]
      PossibleMeetingDates:
        properties:
          daysAllowed:
            type: CustomDates | date-only
            enum: [Monday12, Wednesday7, 2020-02-08, 2020-02-09]
      PossibleVacationDates:
        properties:
          daysAllowed:
            type: datetime-only
            enum: [2020-02-01T00:00:00, 2019-02-22T00:00:00]
      ScheduledDays:
        type: PossibleMeetingDates | PossibleVacationDates
        properties:
          daysAllowed:
            enum: [2020-02-01T00:00:00, Monday12] # VALID
            # enum: [Tuesday123] # INVALID: "Tuesday123" does not match any of the super-types' enum values
            # enum: [Tuesday18] # INVALID: although "Tuesday18" is an (allowed) enum value of "CustomDates", it is not listed in "PossibleMeetingDates" > "daysAllowed" `enum`, which is more restrictive
            # enum: [2020-02-01T00:00:00, 2020-02-18] # INVALID
    

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

    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 和 JSON 模式

    RAML 允许使用 XML 和 JSON 模式通过将模式集成到其数据类型系统中来描述 API 请求或响应的正文。

    以下示例演示如何将外部 JSON 架构包含到根级别类型定义和主体声明中。

    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
    

    RAML 处理器必须能够解释和应用 JSON 和 XML 模式。

    因为 JSON 也是有效的 YAML 1.2 语法,所以 RAML 处理器应该将不包含键的 JSON 格式的数据结构解释$schema为 RAML 类型声明。

    如果媒体类型分别不允许 XML 格式的数据或 JSON 格式的数据,则不得使用 XML 模式或 JSON 模式。在查询参数、查询字符串、URI 参数和标头的任何声明中也禁止使用 XML 和 JSON 模式。

    节点 “schemas” 和 “types”,以及 “schema” 和 “type” 互斥且是同义词,以兼容 RAML 0.8。API 定义应该使用 “types” 和 “type”,因为 “schemas” 和 “schema” 已是过时用法并可能会在未来的 RAML 版本中删除。

    对内部元素的引用

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

    type: !include elements.xsd#Foo
    

    当引用 JSON 或 XML 模式的内部元素时,RAML 处理器必须根据该元素验证实例。RAML 规范支持引用 JSON 模式中有效模式的任何内部元素、任何全局定义的元素以及 XML 模式中的复杂类型。有一些限制:

    • 对于任何 XML 或 JSON实例的验证,必须 遵循与普通 XML 或 JSON schema 相同的限制。
    • 引用 XML 模式内的复杂类型对于确定 XML 实例的结构是有效的。但是,由于复杂类型没有定义顶级 XML 元素的名称,因此这些类型不得用于序列化 XML 实例。
    • 指向 JSON 模式内部元素的引用必须是RFC6901中定义的有效 JSON 指针。

    用户定义的 Facet

    facets 表达类型规定的实例的附加限制,比如数字的可选最小和最大面,或者是标量的枚举面。除了内置的 facets,RAML 同样为任何数据类型提供一种声明用户定义的 facets 的方式。

    在类型声明时,用户定义的 Facet 通过 OPTIONAL facets facet 来声明。facets facet 的值必须是映射。键名就是用户定义的 Facet,其对应的值定义了该 Facet 可以接受的具体值。属性声明和用户定义的 Facet 的声明语法是相同的。要注意, Facet 是根据它所声明的具体值来限制类型的子类型的,而不是它们的超类型。

    Facet 的名称不能以开括号开头,以便与注释区分开。类型上的用户定义的 Facet 的名称不能与该类型上的内置 Facet、或继承链中任何祖先类型的 Facet 的名称匹配。

    如果类型的某个 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
    

    在此示例中,声明noHolidays构面并将其值定义为布尔值可以限制位于假期的日期实例。任何继承类型的实例(例如类型PossibleMeetingDate)必须具有不属于假日的值。

    根据定义,用户定义的方面并未内置到此 RAML 规范中,因此,它们的语义可能无法被所有 RAML 处理器理解。因此,RAML 处理器可以(也可以不)选择在验证该类型的实例时使用该类型上的用户定义的方面。在上面的示例中,RAML 处理器可能会也可能不会为 分配含义noHolidays,因此可以选择忽略noHolidays: true验证 实例时的值PossibleMeetingDate。

    确定默认类型

    RAML 处理器必须能够使用以下规则确定类型声明的默认类型:

    • 当且仅当类型声明包含该类型唯一的构面时,才会推断其默认类型是唯一支持所使用的构面的类型。

    例如,如果类型声明包含properties构面,则其默认类型为object。以下代码片段举例说明了该规则:

    types:
      Person:
        # default type is "object" because "properties" is unique to that type
        # i.e. no need to explicitly define it, "type: object" is inferred
        properties:
    
    • 当且仅当类型声明包含的分面既不是给定类型所独有的(如上面的规则中所述),也不是 atype或schema分面,则默认类型为string。以下代码片段举例说明了该规则:
    types:
      Person:
        properties:
          name: # no type or schema necessary since the default type is `string`
    
    • 默认类型any适用于任何body不包含properties、type或 的节点schema。例如:
    body:
      application/json:
        # default type is `any`
    

    或者,如果已经定义了默认媒体类型,则无需在此处声明它:

    body:
      # default type is `any`
    

    当然,每个规则都可以通过显式定义类型来覆盖。例如:

    types:
      Person:
        properties:
          name:
            type: number
    

    类型表达式

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

    表达描述
    Person最简单的类型表达式:单一类型
    Person[]Person 对象的数组
    string[]字符串标量数组
    string[][]字符串标量的二维数组
    string / Person由字符串 OR 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 )[]
    

    这个例子实际上声明了一个“类型别名”,它为使用复杂类型表达式定义的类型提供了一个更易读的名称(Devices)。在本例中,类型表达式由 Phone 和 Notebook 类型的并集数组组成。您可以使用此技术为复杂类型指定简单名称。类型别名还可以保存额外的属性,例如描述和注释。

    语法

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

    表达成分描述例子
    type name类型名称是类型表达式的基本构建块,单独使用可创建最简单的表达式。number:内置类型 Person:自定义类型
    (type expression)括号消除了运算符所应用的表达式的歧义。Person / Animal[] ( Person / Animal )[]
    (type expression)[]数组是一个一元后缀运算符,放置在另一个类型表达式之后,根据需要括在括号中,指示结果类型是该类型表达式的实例数组。string[]:字符串数组 Person[][]:Person 实例数组的数组
    (type expression 1) / (type expression 2)中缀联合运算符指示结果类型可能是类型表达式 1 或类型表达式 2。可以在类型表达式对之间组合多个联合运算符。string / number:字符串或数字 X / Y / Z:X、Y 或 Z (Manager / Admin)[]:其成员由 Manager 或 Admin 实例组成的数组 Manager[] / Admin[]:Manager 实例数组或 Admin 实例数组。

    多重继承

    RAML 类型支持多重继承。这是通过传递一系列类型来实现的:

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

    在上面的示例中,Teacher 类型继承了 Person 和 Employee 的所有限制。

    仅当子类型在继承其父类型的所有限制后仍然是有效的类型声明时,才允许多重继承。此外,不允许它继承不同种类的原始类型,例如[ number, string ].

    在以下示例中,子类型Number3完全有效:

    types:
      Number1:
        type: number
        minimum: 4
      Number2:
        type: number
        maximum: 10
      Number3: [ Number1, Number2]
    

    而使用相同的示例并且仅将 type 的最大值Number2从 10 更改为 2 将导致类型无效Number3。

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

    联合类型部分说明了如何验证使用多重继承和联合类型的类型的另一个示例。

    如果子类型从它的至少两个父类型继承出具有相同名称的属性,则子类型应保留应用于该属性的所有限制,有两个例外:

    1. 当父类型已声明模式特定时
    2. 当另一个用户定义的特征具有相同的值时

    在这些情况下,无效的类型声明就发生了。

    内联类型声明

    除了类型表达式之外,您可以在任何可以引用类型的地方声明内联/匿名类型。

    #%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
    

    在 RAML 中定义示例

    强烈建议 API 文档包含丰富的示例。RAML 支持为类型声明的任何给定实例定义多个示例或单个示例。除了默认支持 YAML 之外,处理器还应该支持示例的 JSON 和 XML 表示形式。处理器可以支持其他格式。请注意,类型定义与示例编码无关,因此 YAML 中的示例适用于 JSON 或 XML,反之亦然,适用于这三种支持的编码的任何选定组合。

    多个例子

    可选 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
    

    单个示例

    可选 examples 方面可以用于将类型实例的示例附加到类型声明。有两种方法可以表示示例构面值:作为特定类型实例的显式描述,或作为包含其他构面的映射。

    作为特定类型实例的显式描述

    例如:

    title: Attention needed
    body: You have been added to group 274
    
    作为包含其他方面的地图

    该地图可能包含以下附加方面:

    特征描述
    displayName?该示例的替代的、人类友好的名称。如果示例是示例节点的一部分,则默认值是为此示例定义的唯一标识符。
    description?例如,一个实质性的、人性化的描述。它的值必须是字符串,并且可以使用 Markdown 进行格式化。
    (<注释名称annotationName>)?应用于此 API 的注释。注释必须是具有以“(”开头并以“)”结尾的键的映射,其中括号内的文本是注释名称,值是该注释的实例。
    value类型实例的实际示例。
    strict?根据任何类型声明(默认)验证示例,或不验证。将此方面设置为 false 以避免验证。

    例如:

    (pii): true
    strict: false
    value:
      title: Attention needed
      body: You have been added to group 274
    

    如何在 RAML 中定义 example/examples 的示例

    以下代码片段说明了 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
    

    类型实例的 XML 序列化

    RAML 引入了附加的 xml 节点来帮助序列化到 XML 的可能复杂的过程。该节点可用于配置类型实例应如何序列化为 XML。节点的值必须是包含以下节点的映射:xml

    姓名类型描述
    attribute?booleantrue将类型实例序列化为 XML 属性。只能用于true标量类型。 默认:  false
    wrapped?booleantrue将类型实例包装在其自己的 XML 元素中。不能用于true标量类型或true同时attribute为true。 默认:  false
    name?string覆盖 XML 元素或 XML 属性的名称。 默认值: 类型或属性的名称
    namespace?string配置 XML 命名空间的名称。
    prefix?string配置序列化为 XML 期间使用的前缀。

    以下类型声明显示了使用节点的示例xml:

    types:
      Person:
        properties:
          name:
            type: string
            xml:
              attribute: true # serialize it as an XML attribute
              name: "fullname" # attribute should be called fullname
          addresses:
            type: Address[]
            xml:
              wrapped: true # serialize it into its own <addresses>...</addresses> XML element
      Address:
        properties:
          street: string
          city: string
    

    上面的示例可以序列化为以下 XML:

    <Person fullname="John Doe">
      <addresses>
         <Address>…</Address>
         ...
      </addresses>
    </Person>
    

    使用 RAML 中的类型

    类型可以用在多个位置:

    • 正文(JSON)
    • 正文(XML)
    • 正文(网页表单)
    • 标头
    • 查询参数
    • URI 参数

    关于序列化的要点是:

    • 序列化规则取决于类型和使用该类型的位置。
    • “字符串”是自定义值类型的默认序列化目标,它是内​​置类型的扩展“值”。
    • 扩展内置类型继承其序列化目标。

    资源和嵌套资源

    资源由其相对 URI 标识,该 URI 必须以斜杠 ( /) 开头。每个节点的键以斜杠开头,并且位于 API 定义的根节点或者是资源节点的子节点,都是这样的资源节点。

    定义为根级别节点的资源,称为顶级资源。根级别节点的键,为相对于 baseUri 的资源 URI。定义为另一个资源的子节点的资源,称为嵌套资源。子节点的键,为相对于父资源 URI 的嵌套资源的 URI。

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

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

    资源节点的键,即它的相对 URI,可以由多个用斜杠分隔的 URI 路径片段组成。例如,/bom/items可能将物料清单中的项目集合指示为单个资源。但是,如果各个 URI 路径片段本身就是资源,则 API 定义应该使用嵌套资源来描述此结构。例如,如果/bom本身是一个资源,那么/items应该是 的嵌套资源/bom,而不是用作/bom/items非嵌套资源。

    未明确指定绝对 URI。它们的计算方法是附加顶级资源的相对 URI,然后连续附加每个嵌套资源的相对 URI 值,直到到达目标资源。在绝对 URI 的形成中,如果定义了 baseUri,则将其添加到顶级资源的相对 URI 之前;在添加之前,baseUri 中的所有尾部斜杠都会被删除。

    继续前面的示例,公共 gists 资源的绝对 URI 的形成如下。

       "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}具有三个嵌套资源/followers、/following和/keys。

    #%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
    

    计算出的资源绝对 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 之一与另一个相同;绝对 URI 的比较是在不考虑任何 URI 参数的可能值的情况下进行的。任何 URI 参数都不会被扩展或评估,而是保持原样。

    以下示例显示了有效重复的 URI,因为两个路径组合到相同的/users/foo. 这将是被禁止的。

    /users:
      /foo:
    /users/foo:
    

    以下示例中的 URI 始终是允许的。

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

    资源属性

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

    姓名描述
    displayName?如果资源没有定义显示名称节点,文档工具应该引用资源的关键字作为资源名称,例如: /jobs.
    description?对资源的实质性、人性化描述。它的值必须是字符串,并且可以使用 Markdown 进行格式化。
    (<注释名称annotationName>)?应用于此 API 的注释。注释必须是具有以“(”开头并以“)”结尾的键的映射,其中括号内的文本是注释名称,值是该注释的实例。
    get? patch? put? post? delete? options? head?描述方法的对象。
    is?应用于为此资源声明(隐式或显式)的所有方法的特征列表。个别方法可以覆盖此声明。
    type?该资源继承的资源类型。
    securedBy?适用于为此资源声明(隐式或显式)的所有方法的安全方案。
    uriParameters?有关此资源的任何 URI 参数的详细信息。
    /<相对 Uri>?嵌套资源是名称以斜杠 ( /) 开头的任何节点。因此,该资源应被视为相对 URI。

    模板 URI 和 URI 参数

    包含 URI 参数的模板 URI可以用于定义包含可变元素的资源的相对 URI。以下示例显示了带有 key 的顶级资源和带有 key (模板 URI)的/jobs嵌套资源。/{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
    

    下一个示例中显示的OPTIONAL uriParameters节点用于在模板 URI中显式指定 URI 参数。uriParameters 节点的值是一个映射,特别是属性声明,就像类型声明的属性方面的值一样。声明对象中的每个属性都是一个URI 参数声明。每个属性名称对应于模板 URI中的一个参数名称。每个属性值将 URI 参数类型指定为类型名称或内联类型声明。

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

    与baseUriParameters 根节点一样,version 参数是 uriParameters 属性声明中的保留参数名称。version 参数值对应版本根级节点的值。

    以下示例显示了两个顶级资源/user和/users,以及由其模板 URI 指定的嵌套资源。/{userId}URI 参数userId是显式声明的,并给出了描述和整数类型。

    #%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 参数声明指定非标量类型的数组、对象或联合,则处理器必须默认将 JSON 类型应用于 URI 参数实例的值。以下示例夸大了预期的行为:

    #%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
    

    在此示例中,URI 参数userIds是 ids 数组。假设数组应该包含[blue,green],在线路上可能显示为/users/%5B%22blue%22,%22green%22%5D/。

    如果 URI 参数声明为标头值指定非字符串标量类型,则在将该类型应用于该 URI 参数的实例时必须调用类型的标准序列化规则。

    为了避免不明确的匹配,URI 参数匹配的值不得包含斜杠 ( /) 字符。在本节的第一个示例中,是一个与嵌套在资源中的资源/jobs/123匹配的 URI(相对于 baseUri),但该 URI不与任何资源匹配。/{jobId}``/jobs``/jobs/123/x

    在下一个示例中,顶级资源具有 URI 参数folderId 和fileId。

    #%RAML 1.0
    title: Flat Filesystem API
    version: v1
    /files:
      description: A collection of all files
      /folder_{folderId}-file_{fileId}:
        description: An item in the collection of all files
    

    尽管 URI 参数可以显式指定为可选,但当直接用斜杠 ( /) 包围时,它应该是必需的。此时,URI参数就构成了一个完整的URI路径片段,例如.../{objectId}/...。允许 URI 包含相邻的斜杠、不包含任何字符(例如 )通常是没有意义的...//...。因此,只有当 URI 参数与其他文本相邻出现时,才应将其指定为可选。例如,/people/~{fieldSelectors}指示 URI 参数{fieldSelectors}可以为空,因此是可选的,这意味着这/people/~是一个有效的相对 URI。

    特殊的 URI 保留参数ext可以在 uriParameters 节点中显式指定。它的含义保留供客户端使用,以指定请求或响应的正文属于关联的媒体类型。

    URI参数价值
    分机请求或响应正文所需的媒体类型

    按照约定,.json 的 ext 参数的值相当于 application/json 的 Accept 标头。.xml 值相当于 text/xml 的 Accept 标头。通过使用 ext 参数,客户端可以通过 URI 而不是 Accept HTTP 标头指定请求或响应的媒体类型。在以下示例中,/users可以将资源请求为 application/json 或 text/xml:

    #%RAML 1.0
    title: API Using media type in the URL
    version: v1
    /users{ext}:
      uriParameters:
        ext:
          enum: [ .json, .xml ]
          description: Use .json to specify application/json or .xml to specify text/xml
    

    方法

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

    名称描述
    displayName?资源上下文中的替代的、人类友好的方法名称。如果没有为方法定义 displayName 节点,则文档工具应该通过其键(充当方法名称)引用资源。
    description?在资源上下文中对方法进行更长、更人性化的描述。它的值是一个字符串,可以使用 markdown 进行格式化。
    (<注释名称annotationName>)?应用于此 API 的注释。注释是一个映射,其键以“(”开头并以“)”结尾,其中括号中的文本是注释名称,值是该注释的实例。
    queryParameters?有关此方法所需的任何查询参数的详细信息。与 queryString 互斥。
    headers?有关此方法所需的任何请求标头的详细信息。
    queryString?此方法所需的查询字符串。与 queryParameters 互斥。
    responses?有关请求的预期响应的信息。
    body?该方法承认的请求主体。
    protocols?显式指定用于调用方法的协议,从而覆盖其他地方设置的协议,例如在 baseUri 或根级协议节点中。
    is?应用于此方法的特征列表。
    securedBy?适用于该方法的安全方案。

    请求头

    API 的方法可能支持或需要各种 HTTP 标头。OPTIONAL headers节点可以用于显式指定这些 headers。与属性节点的值一样,标题节点的值必须是一个映射,特别是属性声明。该声明对象中的每个属性都是一个标头声明。每个属性名称指定一个允许的标头名称。每个属性值将标头值类型指定为类型名称或内联类型声明。

    下面的简单示例显示了一个带有一个名为 Zencoder-Api-Key 的 HTTP 头的 post 方法,其类型为(暗示的)字符串。

    #%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 不定义验证:对象类型、非标量类型的联合或数组类型(如果数组的基础类型是对象类型),数组类型或非标量类型的联合。在将类型应用于该标头的实例时,处理器可以默认将标头值的格式视为 JSON,或者它们可以允许基于注释的其他处理。

    一些标头也可以由中间客户端和服务器端系统(例如浏览器或代理)添加。

    以下示例说明了从特征继承标头、允许标头的多个实例、指定示例以及在应用于方法和资源时覆盖标头。

    #%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
    

    查询字符串和查询参数

    API 方法可以在调用方法的 URL 中支持或要求查询字符串。URL 中的查询字符串必须遵循 RFC3986 规范,该规范规定了问号分隔符(?)之后和片段(#)分隔符之前的 URL 部分。查询字符串可以通过可选的 queryString 节点或可选的 queryParameters 节点指定。queryString 和 queryParameters 节点必须相互排斥:处理器不能允许同时在同一资源的同一方法中显式或隐式地指定两者。

    整个查询字符串

    queryString 节点可以用于指定查询字符串作为一个整体,而不是作为名称-值对 。 queryString 值必须是数据类型的名称或内联数据类型声明,包括数据类型表达式。在任何一种情况下,在类型层次结构的每个级别完全扩展任何联合类型表达式之后,数据类型的类型层次结构中的所有基类型都必须是标量类型或对象类型。

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

    如果该类型派生自对象类型,则处理器必须将查询字符串视为该对象类型实例的 URL 编码序列化。查询字符串的格式必须为“parameter1=value1¶meter2=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
    

    查询字符串中的查询参数

    queryParameters节点指定组成查询字符串的查询参数集 。 当应用 API 定义的限制时,处理器必须根据 URL 编码格式将查询字符串视为一组查询参数。queryParameters 节点的值是属性声明对象,也是类型声明的属性对象的值。该声明对象中的每个属性都称为查询参数声明。每个属性名称指定一个允许的查询参数名称。尾随问号(?) 属性名称中可以用于指示查询参数是可选的。每个属性值将查询参数值类型指定为类型名称或内联类型声明。

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

    如果查询参数声明为查询参数的值指定了非数组类型,或者未指定类型(相当于指定字符串类型),则处理器必须禁止在请求中使用该查询参数的多个实例。

    当查询参数声明为查询参数的值指定以下任何类型时,RAML 不定义验证:对象类型、非标量类型的联合或数组类型(如果数组的基础类型是对象)非标量类型的类型或联合。在将类型应用于该查询参数的实例时,处理器可以默认将查询参数值的格式视为 JSON,或者它们可以允许基于注释的其他处理。

    如果查询参数定义将查询参数的值指定为以下任何类型,则在将类型应用于查询参数的实例时,必须调用类型的标准序列化规则:非字符串标量类型、非字符串标量类型的并集字符串标量类型,如果数组的基础类型是非字符串标量类型或非字符串标量类型的联合,则为数组类型。

    以下示例显示了使用 HTTP 查询参数的 get 方法。使用示例值将请求发送到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 节点可以指定方法的 HTTP 请求体。例如,使用 POST 或 PUT 创建资源时,请求的正文通常包含要创建的资源的详细信息。

    body 节点的值是“body 声明”。一般来说,主体声明应该是一个映射,其键名称是请求主体的有效媒体类型。每个键名称必须是符合RFC6838中媒体类型规范的媒体类型字符串。这些值是描述请求正文的相应数据类型声明或数据类型名称。或者,如果默认媒体类型已在 API 根部声明,则主体声明可以仅包含描述该媒体类型的请求主体的数据类型声明或数据类型名称。

    以下示例说明了默认和非默认媒体类型以及数据类型声明和引用的各种组合。

    #%RAML 1.0
    title: Example of request bodies
    mediaType: application/json
    types:
      User:
        properties:
          firstName:
          lastName:
    /users:
      post:
        body:
          type: User
    /groups:
      post:
        body:
          application/json:
            properties:
              groupName:
              deptCode:
                type: number
          text/xml:
            type: !include schemas/group.xsd
    

    响应

    本文档的资源和方法部分描述了 HTTP 请求。本节描述对资源上的方法调用的 HTTP 响应。

    资源上的方法的可选响应节点描述了对该资源调用该方法的可能响应。响应的值必须是一个映射,其中每个键名称代表该资源上该方法的可能的 HTTP 状态代码。这些值描述了相应的响应。每个值必须是一个响应声明。

    键应该是数字,例如 200 或 204。在所有情况下,处理器必须将这些数字键视为字符串键。例如,“200”和 200 必须被视为重复键,因此不允许同时使用。

    回应声明

    响应声明的值必须是具有以下键值对的映射:

    姓名描述
    description?对响应的实质性、人性化描述。它的值必须是字符串,并且可以使用 Markdown 进行格式化。
    (<注释名称annotationName>)应用于此 API 的注释。注释必须是具有以“(”开头并以“)”结尾的键的映射,其中括号内的文本是注释名称,值是该注释的实例。
    headers?有关此方法返回的任何响应标头的详细信息
    body?响应正文

    响应和方法声明的可选节点描述、标头、主体和注释的语法和语义是相同的,但分别应用于 HTTP 响应而不是 HTTP 请求。

    以下示例说明了一些可能的响应:

    #%RAML 1.0
    title: Example with responses
    mediaType: application/json
    types:
      Invoice:
        properties:
          amount:
            type: number
            minimum: 0
          vendorName:
    /invoices:
      get:
        responses:
          200:
            body:
              type: Invoice
              properties:
                id: number
      post:
        body:
          type: Invoice
        responses:
          201:
            headers:
              Location:
                example: /invoices/45612
            body:
              application/json:
                type: !include schemas/invoice.json
              text/xml:
                type: !include schemas/invoice.xsd
          422:
            body:
              properties:
                error:
              example:
                error: Amount cannot be negative
    

    资源类型和特征

    跨多种资源和方法重用模式有很多优点。例如,可以定义集合类型资源的特征,然后将其应用于多个资源。这种模式的使用促进了服务器和客户端的一致性并降低了复杂性。

    此外,资源和方法声明经常是重复的。例如,需要 OAuth 身份验证的 API 可能需要跨所有资源的所有方法使用 X-Access-Token 标头。由于多种原因,最好在一个地方定义这样的模式并在任何地方一致地应用它。

    资源类型与资源一样,可以指定安全方案、方法和其他节点。使用资源类型的资源继承其节点。资源类型还可以使用另一种资源类型,从而继承另一种资源类型。资源类型和资源通过继承链模式相关联。资源类型定义不得包含嵌套资源。当资源类型定义应用于资源时,该定义不能用于生成嵌套资源。资源类型定义不适用于其自己的现有嵌套资​​源。

    特征与方法一样,可以提供方法级节点,例如描述、标头、查询字符串参数和响应。使用一个或多个特征的方法继承这些特征的节点。资源和资源类型还可以使用一个或多个特征,从而继承一个或多个特征,然后将其应用于该资源和资源类型的所有方法。特征通过 mixin 模式与方法相关联。

    声明资源类型和特征

    资源类型可以使用API 定义根部的可选资源类型节点来声明。 该节点的值必须是一个映射,其中键名称成为可以在整个 API 中引用的资源类型的名称,而值是资源类型声明。

    同样,特征可以使用API 定义根部的可选特征节点来声明。 该节点的值必须是一个映射,其中键名称成为可以在整个 API 中引用的特征名称,值是特征声明。

    除了资源和方法可以分别具有的所有节点之外,资源类型和特征声明还可以具有以下节点(资源类型声明不得具有嵌套资源节点)。

    名称定义
    usage?资源类型或特征的可选使用节点提供有关如何以及何时使用资源类型或特征的说明。文档生成器必须分别根据资源和方法的特征来描述该节点。但是,资源和方法不得继承使用节点。资源和方法都不允许使用名为“usage”的节点。

    以下示例说明了几种资源类型和特征的声明:

    #%RAML 1.0
    title: Example API
    version: v1
    resourceTypes:
      collection:
        usage: This resourceType should be used for any collection of items
        description: The collection of <<resourcePathName>>
        get:
          description: Get all <<resourcePathName>>, optionally filtered
        post:
          description: Create a new <<resourcePathName | !singularize>>
    traits:
      secured:
        usage: Apply this to any method that needs to be secured
        description: Some requests require authentication.
        headers:
          access_token:
            description: Access Token
            example: 5757gh76
            required: true
    

    以下示例基于前一个示例,但资源类型和特征是在使用 !include 标记包含的外部文件中定义的。

    #%RAML 1.0
    title: Example API
    version: v1
    resourceTypes:
      collection: !include resourceTypes/collection.raml
      member: !include resourceTypes/member.raml
    traits:
      secured: !include traits/secured.raml
      rateLimited: !include traits/rate-limited.raml
    

    资源类型和特征都不允许合并嵌套资源。因此,下面的例子是无效的:

    resourceTypes:
      hasGroups:
        get:
        patch:
        delete:
        /groups:
    

    应用资源类型和特征

    资源可以指定使用 OPTIONAL类型节点派生的资源类型。该值必须是根级 resourceTypes 节点或库中定义的资源类型的名称。资源类型定义不适用于现有的嵌套资源。

    同样,方法可以使用 OPTIONAL is 节点指定它继承的一个或多个特征。特征的值必须是任意数量元素的数组。每个元素必须是根级特征节点或库中定义的特征的名称。还可以使用 is 节点将特征应用于资源。使用此节点相当于将该特征应用于该资源的所有方法,无论该方法是在资源定义中显式声明还是从资源类型继承。根据 is 节点中定义的特征,特征按从左到右的顺序应用于方法。特征定义不适用于嵌套资源。

    下面的例子说明了资源类型和特征的应用。

    #%RAML 1.0
    title: Example API
    version: v1
    resourceTypes:
      collection:  !include resourceTypes/collection.raml
      member:      !include resourceTypes/member.raml
    traits:
      secured:     !include traits/secured.raml
      paged:       !include traits/paged.raml
      rateLimited: !include traits/rate-limited.raml
    /users:
      type: collection
      is: [ secured ]
      get:
        is: [ paged, rateLimited ] # this method is also secured
      post:                        # this method is also secured
    

    要将参数值传递给资源类型和特征,请在声明资源类型或特征时使用映射,如以下示例所示。

    #%RAML 1.0
    title: Example API
    version: v1
    resourceTypes:
      searchableCollection:
       get:
          queryParameters:
            <<queryParamName>>:
              description: Return <<resourcePathName>> that have their <<queryParamName>> matching the given value
            <<fallbackParamName>>:
              description: If no values match the value given for <<queryParamName>>, use <<fallbackParamName>> instead
    traits:
      secured:
        queryParameters:
          <<tokenName>>:
            description: A valid <<tokenName>> is required
      paged:
        queryParameters:
          numPages:
            description: The number of pages to return, not to exceed <<maxPages>>
    /books:
      type: { searchableCollection: { queryParamName: title, fallbackParamName: digest_all_fields } }
      get:
        is: [ secured: { tokenName: access_token }, paged: { maxPages: 10 } ]
    

    资源类型和特征参数

    资源类型和特征的声明可以包含具有在应用资源类型或特征时必须指定的值的参数,除非参数名称被保留,在这种情况下其值必须由处理应用程序提供。

    在资源类型和特征声明中,resourcePath和resourcePathName是保留参数名称。

    范围价值
    resourcePath相对于 baseUri 的完整资源 URI(如果有)。
    resourcePathName最右边的不包含 URI 参数的路径片段。

    双尖括号(双 V 形)将资源类型和特征定义中的参数名称括起来;例如,<<parameterName>>.

    处理应用程序必须将 <<resourcePath>> 的值设置为继承资源的资源URI(相对于baseUri,如果有的话)和所有父资源的相对资源URI的连接。处理应用程序必须将 <<resourcePathName>> 的值设置在URI中的右斜杠(/)后面的位置,省略任何包含URI参数的路径片段。

    例如,将资源类型或特性应用于嵌套在资源 /{groupId} 中的资源 /users 会将 resourcePath 参数的值设置为 "/groups/{groupId}/users"。将资源类型或特性应用于资源 /jobs/{jobId} 会将 resourcePathName 参数的值设置为 "jobs"。

    当设置 resourcePath 和 resourcePathName 时,处理应用程序还必须省略在资源URI中找到的任何ext参数及其参数化括号(“{”和“}”)。例如,将资源类型或特征应用于根级资源/bom/{itemId}{ext}会将resourcePath 和resourcePathName 参数的值分别设置为“ /bom/{itemId}”和“ bom”。

    在特征声明中,methodName 是保留参数。

    范围价值
    methodName方法名称

    处理应用程序必须将 methodName 参数的值设置为继承方法名称。

    可以通过应用以下函数之一进一步转换参数值。RAML 支持的唯一语言环境是美国英语。

    功能定义
    !singularize!singularize 函数必须通过对其原始值进行特定于语言环境的奇异化来作用于参数的值。 例如:users --> user
    !pluralize!pluralize 函数必须通过其原始值的特定于语言环境的复数来作用于参数的值。 例如:user --> users
    !uppercase!uppercase 函数必须将参数的值转换为大写字母。 例如:userId --> USERID
    !lowercase!lowercase 函数必须将参数的值转换为小写字母。 例如:userId --> userid
    !lowercamelcase!lowercamelcase 函数必须将参数的值转换为驼峰式大小写,其中第一个字母为小写。 例如:UserId --> userId
    !uppercamelcase!uppercamelcase 函数必须将参数的值转换为驼峰式大小写,其中第一个字母为大写。 例如:userId --> UserId
    !lowerunderscorecase!lowerunderscorecase 函数必须将参数的值转换为小写字母;如果该值是复合词,则该函数还必须在尚未由一个或多个下划线分隔的连续单词之间添加一个额外的下划线。 例如:userId-->user_id
    !upperunderscorecase!upperunderscorecase 函数必须将参数的值转换为大写字母;如果该值是复合词,则该函数还必须在尚未由一个或多个下划线分隔的连续单词之间添加一个额外的下划线。 例如:userId --> USER_ID
    !lowerhyphencase!lowerhyphencase 函数必须将参数的值转换为小写字母;如果该值是复合词,则该函数还必须在尚未由一个或多个连字符分隔的连续单词之间添加一个附加连字符。 例如:userId --> user-id
    !upperhyphencase!upperhyphencase 函数必须将参数的值转换为大写字母;如果该值是复合词,则该函数还必须在尚未由一个或多个连字符分隔的连续单词之间添加一个附加连字符。 例如:userId --> USER-ID

    将这些函数附加到双尖括号内的参数名称,并用竖线 ( |) 字符和可选的空格填充分隔。下面是一个使用函数和保留参数的示例:

    #%RAML 1.0
    title: Example API
    version: v1
    mediaType: application/json
    types:
      Users: !include types/users.raml
      User:  !include types/user.raml
    resourceTypes:
      collection:
        get:
          responses:
            200:
              body:
                type: <<resourcePathName>> # e.g. Users
        post:
          responses:
            200:
              body:
                type: <<resourcePathName | !singularize>>  # e.g. User
      member:
        get:
          responses:
            200:
              body:
                type: <<resourcePathName>> # e.g. User
    traits:
      secured:
        description: Some requests require authentication
        queryParameters:
          <<methodName>>: # e.g. get:
            description: A <<methodName>>-token pair is required  # e.g. A get-token pair...
            example: <<methodName>>=h8duh3uhhu38   # e.g. get=h8duh3uhhu38
    

    参数不能在模块化上下文中使用的任何文件位置中使用,即标记中定义的任何文件位置!include或作为任何uses或extends节点的值。

    将 HTTP 方法声明为可选

    定义资源类型时,捕获表现继承资源以下几个级别的模式而不强制创建中间级别可能很有用。例如,资源类型声明描述了 API 为该资源定义 post 方法时使用的主体参数。将资源类型应用于没有 post 方法的资源不会创建 post 方法。

    为了满足这种需要,资源类型定义可以在任何方法的名称后面附加问号 ( ?) 后缀,以将该方法声明为可选,从而导致以下行为:

    • 如果资源中的相应级别尚不存在该方法,则不要将该方法应用于该资源。
    • 如果不带问号的方法名称已在资源中的相应级别显式或隐式定义,则将方法节点的值应用于资源类型。

    以下示例显示了名为 corpResource 的资源类型,带有可选的 post? 节点,定义名为 X-Chargeback 的必需标头和名为 TextAboutPost 的自定义参数。继承资源/servers定义了一个post方法,因此需要包含X-Chargeback标头要求。TextAboutPost 也必须被定义。继承资源/queues没有定义 post 方法,因此不必定义 X-Chargeback 标头或 TextAboutPost 参数。

    #%RAML 1.0
    title: Example of Optional Properties
    resourceTypes:
      corpResource:
        post?:
          description: Some info about <<TextAboutPost>>.
          headers:
            X-Chargeback:
              required: true
    /servers:
      type:
        corpResource:
          TextAboutPost: post method # post defined which will force to define the TextAboutPost parameter
      get:
      post: # will require the X-Chargeback header
    /queues:
      type: corpResource
      get:
      # will not have a post method defined which means the TextAboutPost parameter is
      # not required; same for the X-Chargeback header
    

    合并特征的算法和方法

    每个 RAML 元素都有其 RAML 文档的分支。将特征应用于方法的高级描述是将特征分支放在方法分支下。实际上,将特征应用于方法是一个递归过程:

    1. 检查方法节点属性,特征节点中未定义的属性保持不变。
    2. 方法节点接收特征节点的所有属性(不包括可选属性),这些属性在方法节点中未定义。
    3. 方法节点和特征节点中定义的属性(包括可选属性)按如下方式处理:
    • 标量属性保持不变。
    • 集合属性按值合并,如下所述。
    • 对象属性的值遵循此过程的步骤 1-3。

    通常,一种方法可以具有多个特征,每个特征都具有足够的层次结构。应用所有特征相当于构建一堆分支,如下所示:

    • 顶部分支是方法分支。

    • 其他分支是特征分支。

      • 与其他方法相比,在层次结构上远离该方法的特征分支将被绕过,转而选择最接近的分支。

      • 与方法处于同一层次结构距离内的那些特征可以在队列中排序:

        • 对于距离一,它只是方法特征列表。
        • Queue(d+1) 是从 Queue(d) 获得的,通过连接其元素的特征列表并取消除每个特征的第一次出现之外的所有元素。
        • 分支顺序确定如下:在队列中位置较高的特征,在堆栈中具有较深的分支。

    最后,资源可以有自己的特征,并且可以应用资源类型链,例如resourceType1、resourceType2……。每种资源类型都可能有自己的特征并定义相同的方法。堆栈的构造如下:

    1. 方法本身的特点
    2. 拥有该方法的资源的特征
    3. ResourceType1 拥有的方法的特征
    4. ResourceType1 的特征
    5. ...

    将资源类型与资源合并遵循类似的规则。

    以下示例说明了如何将资源类型合并到/products资源中。

    resourceTypes:
      collection:
        get:
          description: a list
          headers:
            APIKey:
    /products:
      type: collection
      get:
        description: override the description
        responses:
          200:
            body:
              application/json:
    

    collection资源类型和资源声明之间唯一的重叠是description两者都定义了资源类型和资源声明。在此示例中,最终版本具有已在资源中显式定义的描述。

    每个显式节点都会胜过在资源类型或特征中声明的节点。其余的简单合并即可。最终的合并结果必须是:

    /products:
      get:
        headers:
          APIKey:
        description: override the description
        responses:
          200:
            body:
              application/json:
    

    资源类型和特征对收藏品的影响

    所有受应用特征和资源类型影响的集合或序列都会被合并。此示例在特征和资源中定义了查询参数的枚举值:

    #%RAML 1.0
    title: Example API
    version: v1
    traits:
      withQueryParameters:
        queryParameters:
          platform:
            enum:
              - win
              - mac
    /installer:
      get:
        is: [ withQueryParameters ]
        queryParameters:
          platform: #the actual enum is [ mac, unix, win ]
            enum:
              - mac
              - unix
    

    合并后的枚举值为 [ mac, unix, win ] 。

    在集合中,特征可以作为方法、资源、特征或资源类型的“is”属性值出现。这样的列表可以包含相同的特征,但参数集不同,因此不能被视为相等。

    #%RAML 1.0
    title: Example API
    version: v1
    resourceTypes:
      apiResource:
        get:
          is: [ { secured : { tokenName: access_token } } ]
    traits:
      secured:
        queryParameters:
          <<tokenName>>:
            description: A valid <<tokenName>> is required
    /servers:
      get:
        is: [ { secured : { tokenName: token } } ]
    

    为了解决由这个不平等引起的冲突,优先考虑与目标方法或资源最接近的特征。在前面的例子中, tokenName 方法的 GET:/servers 参数值为 token ,特征列表包含单个特征出现: [ {secured:{ tokenName:token}} ] 。

    安全方案

    大多数 REST API 都有一种或多种机制来保护数据访问、识别请求并确定访问级别和数据可见性。

    本节描述了 API 设计者可以如何在 RAML API 定义中包含安全方案定义。本节还概述了客户端和服务器实现生成器应该包含的支持文档。

    安全方案类型

    RAML 支持以下内置的安全方案类型:

    Type Description 描述
    OAuth 1.0 API 认证需要使用 OAuth 1.0,如 RFC5849 中所述。
    OAuth 2.0 API 认证需要使用 OAuth 2.0,如 RFC6749 所述。
    Basic Authentication API 身份验证依赖于使用 RFC2617 中描述的基本访问身份验证
    Digest Authentication API 身份验证依赖于使用 RFC2617 中描述的摘要访问认证
    Pass Through 根据定义的映射,标题或查询参数将传递给 API。
    x-{other}API 认证依赖于另一种认证方法。

    处理应用程序开发人员可以为这些机制提供支持。如果支持某个机制,则必须符合指定的标准。

    此外,任何安全方案定义都可以通过一个 describedBy 节点进行扩充,这允许设计者对API安全方案进行文档化。

    安全方案声明

    安全方案节点是一个具有以下键值对的映射:

    Name Description 
    type 指定 API 安全机制。只允许一种 API 支持的身份验证方法。该值必须是以下方法之一:OAuth 1.0,OAuth 2.0,基本身份验证,摘要身份验证,透传,x-<其他>。
    displayName? 一个备选的、人性化的安全方案名称。
    description? ?可能用于描述安全方案的信息。其值为字符串,可以使用 Markdown 格式化。
    describedBy? 由方案确定的以下与安全相关的请求组件的描述:头部、查询参数或响应。作为最佳实践,即使对于标准安全方案,API 设计者也应该描述这些安全方案的节点。包括安全方案描述可以完善 API 文档。
    settings? 设置?设置属性可以用于提供特定于安全方案的信息。

    在RAML文档根目录中定义了一个可选的 securitySchemes 节点。 securitySchemes 的值是一个映射,其中包含将安全方案名称映射到安全方案声明的键值对。

    API 支持的每种身份验证模式都必须作为 securitySchemes 节点值的组成部分来表达。

    在这个例子中,Dropbox API 支持使用 OAuth 2.0 和 OAuth 1.0 进行身份验证。

    #%RAML 1.0
    title: Dropbox API
    version: 1
    baseUri: https://api.dropbox.com/{version}
    securitySchemes:
      oauth_2_0:
        description: |
          Dropbox supports OAuth 2.0 for authenticating all API 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 with
                 the "Authorization" header.
              type: string
          responses:
            401:
              description: |
                  Bad or expired token. This can happen if the user or Dropbox
                  revoked or expired an access token. To fix, re-authenticate
                  the user.
            403:
              description: |
                  Bad OAuth request (wrong consumer key, bad nonce, expired
                  timestamp...). Unfortunately, re-authenticating the user won't help here.
        settings:
          authorizationUri: https://www.dropbox.com/1/oauth2/authorize
          accessTokenUri: https://api.dropbox.com/1/oauth2/token
          authorizationGrants: [ authorization_code, implicit ]
      oauth_1_0:
        description: |
          OAuth 1.0 continues to be supported for all API requests, but OAuth 2.0 is now preferred.
        type: OAuth 1.0
        settings:
          requestTokenUri: https://api.dropbox.com/1/oauth/request_token
          authorizationUri: https://www.dropbox.com/1/oauth/authorize
          tokenCredentialsUri: https://api.dropbox.com/1/oauth/access_token
    

    描述者

    所描述的节点的值被定义为一个具有以下键值对的映射,如下所示:

     名称描述 
    headers? 可选的头部数组,记录可能被接受的头部。
    queryParameters? 查询参数,由模式用于授权请求。与查询字符串互斥。
    queryString? 模式使用的查询字符串来授权请求。与查询参数互斥。
    responses? 一个可选的响应数组,表示可能发送的响应。
    (<annotationName.>)? 应用于此 API 的注解。注解是一个具有以"("开头和以")"结尾的键的映射,括号中的文本是注解名称,值是该注解的实例。

    设置

    设置节点可以用来提供特定于安全方案的信息。所需节点根据声明的安全方案类型而异。

    设置节点描述了任何处理应用程序必须提供和验证的最小属性集,如果选择实现安全方案的话。处理应用程序可以选择识别其他属性,如令牌生命周期、首选加密算法等。

    OAuth 1.0 

    这种类型的安全方案具有以下节点:

    名称描述 
    requestTokenUri 在 RFC5849 第 2.1 节中定义的临时凭证请求端点的URI。
    authorizationUri在 RFC5849 第 2.2 节中定义的资源所有者授权终端点的 URI。
    tokenCredentialsUri tokenCredentialsUri在 RFC5849 第 2.3 节中定义的令牌请求端点的 URI。
    signatures? 授权服务器使用的签名方法列表,可以是以下任何一种: HMAC-SHA1 , RSA-SHA1 或 PLAINTEXT 。如果缺少签名,则假定认证服务器允许在RFC5849第3.4节中定义的任何签名方法。

    OAuth 1.0 认证遵循 RFC5849 中描述的标准。以下示例展示了如何设置 OAuth 1.0 属性:

    #%RAML 1.0
    title: My Sample API
    securitySchemes:
      oauth_1_0:
        description: |
          OAuth 1.0 continues to be supported for all API requests, but OAuth 2.0 is now preferred.
        type: OAuth 1.0
        settings:
          requestTokenUri: https://api.mysampleapi.com/1/oauth/request_token
          authorizationUri: https://api.mysampleapi.com/1/oauth/authorize
          tokenCredentialsUri: https://api.mysampleapi.com/1/oauth/access_token
          signatures: [ 'HMAC-SHA1', 'PLAINTEXT' ]
    
    OAuth 2.0 

    这种类型的安全方案具有以下节点:

    名称Description 
    authorizationUri 在 RFC6749 第 3.1 节中定义的授权终端点的 URI。只有在使用 authorization_code 或 implicit 授权类型时,提供授权终端点是强制性的。对于其他任何授权类型,都不是强制性的。
    accessTokenUri accessTokenUri在 RFC6749 第 3.2 节中定义的令牌终端的 URI。
    authorizationGrants API 支持的授权授予列表,根据 RFC6749 第4.1、4.2、4.3 和 4.4 节的定义,可以是以下任何一种授权授予: authorization_code , password , client_credentials 或 implicit ;或者是根据第 [4.5] 节定义的任何绝对 URI(定义在https://tools.ietf.org/html/rfc6749#section-4.5)。
    scopes? API 支持的范围列表,根据 RFC6749 第 3.3 节定义。

    OAuth 2.0 认证遵循 RFC6749 中描述的标准。以下示例展示了如何设置 OAuth 2.0 属性:

    #%RAML 1.0
    title: Dropbox API
    version: 1
    baseUri: https://api.dropbox.com/{version}
    securitySchemes:
      oauth_2_0:
        description: |
          Dropbox supports OAuth 2.0 for authenticating all API 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 with
                 the "Authorization" header.
              type: string
          responses:
            401:
              description: |
                  Bad or expired token. This can happen if the user or Dropbox
                  revoked or expired an access token. To fix, re-authenticate
                  the user.
            403:
              description: |
                  Bad OAuth request (wrong consumer key, bad nonce, expired
                  timestamp...). Unfortunately, re-authenticating the user won't help here.
        settings:
          authorizationUri: https://www.dropbox.com/1/oauth2/authorize
          accessTokenUri: https://api.dropbox.com/1/oauth2/token
          authorizationGrants: [ authorization_code, implicit, 'urn:ietf:params:oauth:grant-type:saml2-bearer' ]
    
    基本身份验证

    注意:基本安全性不需要在 API 定义中进一步指定设置。

    #%RAML 1.0
    title: Dropbox API
    version: 1
    baseUri: https://api.dropbox.com/{version}
    securitySchemes:
      basic:
        description: |
          This API supports Basic Authentication.
        type: Basic Authentication
    
    摘要认证

    注意:摘要安全不需要在 API 定义中进一步指定任何设置。

    #%RAML 1.0
    title: Dropbox API
    version: 1
    baseUri: https://api.dropbox.com/{version}
    securitySchemes:
      digest:
        description: |
          This API supports DigestSecurityScheme Authentication.
        type: Digest Authentication
    
    通过

    通过身份验证没有定义任何特定的设置,实现已知的 RAML。您必须为每个在 describedBy 中定义的 header 或 queryParameter 提供一个值,并将其与请求一起传递,而不进行修改。以下示例显示如何提供这些值:

    #%RAML 1.0
    title: Dropbox API
    version: 1
    baseUri: https://api.dropbox.com/{version}
    securitySchemes:
      passthrough:
        description: |
          This API supports Pass Through Authentication.
        type: Pass Through
        describedBy:
          queryParameters:
            query:
              type: string
          headers:
            api_key:
              type: string
    
    x-<其他>

    x-身份验证方法没有定义任何特定的设置,因为这些方法的实现在 RAML 中是未知的标准。这些安全方案可能只包括描述和描述部分,以便记录安全方案的预期使用方式。以下示例展示了这样一个安全方案:

    #%RAML 1.0
    title: Custom API
    version: 1
    baseUri: https://api.custom.com/{version}
    securitySchemes:
      custom_scheme:
        description: |
          A custom security scheme for authenticating requests.
        type: x-custom
        describedBy:
          headers:
            SpecialToken:
              description: |
                Used to send a custom token.
              type: string
          responses:
            401:
              description: |
                Bad token.
            403:
    

    应用安全方案

    在 RAML 文档根目录中的 securedBy 节点可以将安全方案应用于 API 的每个方法。除了具有自己 securedBy 节点的方法外,所有 API 方法都可以通过指定的安全方案进行身份验证。

    将安全方案应用于方法会覆盖应用于整个 API 的安全方案。为了指示某个方法使用特定的安全方案进行保护,必须使用 securedBy 节点来定义该方法。

    securedBy 节点分配的值必须是 RAML 文档根节点中 securitySchemes 节点先前定义的安全方案之一的列表。

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

    一个包含 null 作为数组组件的 securedBy 节点表示该方法可以在不应用任何安全方案的情况下调用。

    #%RAML 1.0
    title: GitHub API
    version: v3
    baseUri: https://api.github.com
    securitySchemes:
      oauth_2_0: !include securitySchemes/oauth_2_0.raml
    /users/{userid}/gists:
      get:
        securedBy: [null, oauth_2_0]
    

    securedBy 节点还可以将一系列安全方案应用于资源。除了那些有自己 securedBy 节点的资源方法外,所有资源方法都可以通过指定的任何安全方案进行身份验证。resources 节点的值将覆盖根属性的值。应用于资源的安全方案不得包含嵌套资源;安全方案不适用于现有的嵌套资源。

    将安全方案应用于方法会覆盖应用于 API 和具有该方法作为同级的资源的安全方案。

    如果处理应用程序支持自定义节点,则可以在方法包含时为安全方案提供自定义参数。

    以下示例为参数 scopes 赋值:

    #%RAML 1.0
    title: GitHub API
    version: v3
    baseUri: https://api.github.com
    securitySchemes:
      oauth_2_0: !include securitySchemes/oauth_2_0.raml
    /users/{userid}/gists:
      get:
        securedBy: [null, oauth_2_0: { scopes: [ ADMINISTRATOR ] } ]
    

    安全方案所需提供的必填和可选参数列表由安全方案类型指定。

    注释

    注解提供了一种机制,可以在此 RAML 1.0 规范中已定义的元数据之外,通过元数据扩展 API 规范。注解还可以用于在 RAML 规范中的特定位置向内置的 RAML 节点添加属性。处理器可以支持某些注解,以增加 API 描述的特定性,启用测试等工具,支持 API 存储库和 API 发现等功能。处理器可以忽略任何和所有的注解。

    API 规范中使用的注解必须在根级别的 annotationTypes 节点中声明。注解可以具有值,在注解类型声明中定义和约束。处理器可以依赖这些声明来确保注解值符合预期。

    以下示例展示了各种注解类型声明以及将注解应用于 API 定义的方式。

    #%RAML 1.0
    title: Illustrating annotations
    mediaType: application/json
    annotationTypes:
      deprecated: nil
      experimental: nil | string
      feedbackRequested: string?
      testHarness:
        type: string # This line can be omitted as it's the default type
      badge:         # This annotation type allows string values, too
      clearanceLevel:
        properties:
          level:
            enum: [ low, medium, high ]
            required: true
          signature:
            pattern: "\d{3}-\w{12}"
            required: true
    /groups:
      (experimental):
      (feedbackRequested):
    /users:
      (testHarness): usersTest
      (badge): tested.gif
      (clearanceLevel):
        level: high
        signature: 230-ghtwvfrs1itr
      get:
        (deprecated):
        (experimental):
        (feedbackRequested): Feedback committed!
        responses:
          200:
    

    当一个数据类型被继承时,应用于该数据类型的注解不会被继承。然而,处理器应该将数据类型层次结构中的注解信息提供出来。应用于资源类型或特性的注解也会被应用于继承该资源类型或特性的资源类型、资源或方法。特别地,如果一个特性被应用于资源类型或资源,那么该特性上的所有注解都会隐式地应用于该资源的所有方法。如果继承的资源类型、资源或方法明确地应用了某种类型的注解,那么该注解将覆盖所有继承和隐式应用的该类型注解。特别地,如果一个特性被应用于资源类型或资源,并且该资源类型或资源应用了某种类型的注解,那么该特性上的所有该类型注解都会被覆盖。

    声明注解类型

    注解类型是使用 OPTIONAL 根级注解类型节点声明的。注解类型节点的值是一个映射,其键定义注解类型名称,也称为注解,其值是称为注解类型声明的键值对。注解类型声明具有与数据类型声明相同的语法,其特性具有与数据类型相应特性相同的语法,但还添加了 allowedTargets 特性。注解类型声明约束了该类型注解的值,就像数据类型声明约束了 URI 参数、查询参数、标头或该类型的主体的值一样。allowedTargets 节点限制了可以应用注解的位置类型。注解类型和数据类型一样,可以扩展其他数据类型,但注解类型本身既不能被扩展,也不能在数据类型可以使用的任何地方使用。

    名称描述
    displayName? 一个友好的名称,仅用于显示或文档目的。默认值是元素键,即注释本身的名称。
    description? 注释的预期用途或含义。可以使用 markdown 进行格式化的字符串。
    (<annotationName.>)?应用于此API的注解。注解是一个具有以"("开头和以")"结尾的键的映射,括号中的文本是注解名称,值是该注解的实例。
    allowedTargets?限制注释的位置。如果指定了此节点,则只能在与这些位置对应的节点上应用此类型的注释。值必须是目标位置中描述的一个或多个选项。

    如果注解类型声明既没有指定类型限制也没有指定属性限制,那么默认的注解类型是字符串。

    API 规范中使用的所有注解都必须在其 annotationTypes 节点中声明。任何注解的值都必须符合其注解类型的规范。

    如果 allowedTargets 节点不存在,则注释可以应用于目标位置表中列出的任何目标位置。如果 allowedTargets 节点存在,则限制了注释的应用范围,如注释目标中所述。

    应用注释

    要应用于 API 规范中,注解必须在注解类型中声明。

    通过在规范中的节点上添加一个注解节点,可以将声明的注解应用于该节点,其键是括在括号中的注解类型的名称。注解值必须符合相应的注解类型的规范。

    下面的示例是前一个示例的一个小子集,显示了一个明确的声明和使用 testHarness 注释,它应该是一个字符串值。

    #%RAML 1.0
    title: Testing annotations
    mediaType: application/json
    annotationTypes:
      testHarness:
        type: string
    /users:
      (testHarness): usersTest
    

    下面的例子在语义上与前一个例子等价,但是依赖于当没有显式类型声明时的隐式默认值类型声明。

    #%RAML 1.0
    title: Testing annotations
    mediaType: application/json
    annotationTypes:
      testHarness:
    /users:
      (testHarness): usersTest
    

    标注标量值节点

    通常情况下,对标量值节点进行注释是很有用的,例如 baseUri 。注释通常作为额外的键值对应用于本质上接受键值对的映射值节点。注释不能轻易地应用于标量值节点。为了将注释应用于任何标量值节点,RAML 处理器还必须支持将标量值节点表示为映射的形式,并允许使用单个键 value 作为替代正常语法的方式。

    下面的例子展示了一个标量值节点,通常表示为:

    baseUri: http://www.example.com/api
    

    在示例中添加了使用 value 作为键的替代地图语法

    baseUri:
      value: http://www.example.com/api
    

    现在,注释可以正常应用,就像这个例子中所示:

    baseUri:
      value: http://www.example.com/api
      (redirectable): true
    

    以下列表显示了 RAML 支持的所有可用的标量值节点:

    displayName
    description
    type
    schema
    default
    example
    usage
    required
    content
    strict
    minLength
    maxLength
    uniqueItems
    minItems
    maxItems
    discriminator
    minProperties
    maxProperties
    discriminatorValue
    pattern
    format
    minimum
    maximum
    multipleOf
    requestTokenUri
    authorizationUri
    tokenCredentialsUri
    accessTokenUri
    title
    version
    baseUri
    mediaType
    extends
    

    注释目标

    在 API 规范中,注解可以应用的位置必须是以下目标位置表中的一个。这些目标是位置本身,而不是位置内的子属性;例如,方法目标指的是方法节点,而不是方法的显示名称、描述等。

    目标地点

    目标描述
    API 文档的根节点
    DocumentationItem 根级文档节点的值是集合中的一个项目
    Resource 一个资源(相对 URI)节点,根级或嵌套的
    Method 一个方法节点
    Response 一个响应节点的声明,其键是一个 HTTP 状态码
    RequestBody 方法的主体节点
    ResponseBody 响应的主体节点
    TypeDeclaration 数据类型声明(内联或在全局类型集合中),头声明,查询参数声明,URI参数声明或其中任何一个声明中的属性,其中类型属性可以使用
    Example 一个例子或者多个例子节点
    ResourceType 资源类型节点
    Trait 一个特征节点
    SecurityScheme 安全方案声明
    SecuritySchemeSettings 安全方案声明的设置节点
    AnnotationType 注解类型节点的声明,其键是注解类型的名称,其值描述了注解。
    Library 图书馆的根源
    Overlay 覆盖的根节点。
    Extension 扩展的根源

    以下示例说明了对注解允许的目标应用一些限制。

    #%RAML 1.0
    title: Illustrating allowed targets
    mediaType: application/json
    annotationTypes:
      meta-resource-method:
        allowedTargets: [ Resource, Method ]
      meta-data:
        allowedTargets: TypeDeclaration
    types:
      User:
        type: object
        (meta-data): on an object; on a data type declaration
        properties:
          name:
            type: string
            (meta-data): on a string property
    /users:
      (meta-resource-method): on a resource
      get:
        (meta-resource-method): on a method
        responses:
          200:
            body:
              type: User[]
              (meta-data): on a body
    

    模块化

    RAML 提供了几种机制来帮助模块化 API 规范的生态系统:

    • Includes 
    • Libraries 
    • Overlays
    • Extensions 

    包括

    RAML 处理器必须支持可选的 !include 标签,该标签指定将外部文件包含到 API 规范中。作为 YAML 标签,必须使用感叹号( ! )前缀。在 API 规范中,!include 标签仅位于节点值位置。!include 标签必须是一个节点的值,该节点将 !include 标签命名的文件的内容分配给节点的值。因此,!include 标签不能在任何类型表达式或多重继承中使用。

    在下面的示例中,从名为 myTypes.raml 的文件中检索要在 API 规范中使用的类型集,并将其作为 types 节点的值使用。

    #%RAML 1.0
    title: My API with Types
    types: !include myTypes.raml
    

    !include 标签接受一个参数,即要包含的内容的位置,必须明确指定。参数的值必须是路径或 URL,如下表所述:

    参考描述例子
    absolute path 以单个斜杠( / )开头,并相对于根 RAML 文件位置进行解释的路径。/traits/pageable.raml /traits/pageable.raml
    relative path 相对路径一个路径既不以单斜杠( / )开头,也不构成 URL,并且相对于包含文件的位置进行解释。description.md 描述.md ../traits/pageable.raml
    URL URL绝对URLhttp://dev.domain.com/api/patterns/traits.raml

    为了简化 API 定义,并且由于包含文件的解析上下文不在文件和其父文件之间共享,所以包含的文件不得使用 YAML 引用来引用另一个文件中的锚点。同样地,父文件中的引用不得引用包含文件中定义的锚点。

    !include 标签的参数必须是静态的:也就是说,它不能包含任何资源类型参数或特性参数。

    打字的碎片

    一个要包含的文件可以以 RAML 片段标识行开头,该行由文本 #%RAML 后跟从左到右的一个空格,文本 1.0,一个空格,以及以下片段标识符之一组成:

    片段标识符描述相关的 RAML 规范部分
    DocumentationItem 根级文档节点的值是集合中的一个项目 用户文档
    DataType 数据类型声明,其中类型节点可以使用 类型
    NamedExample 示例方面的声明。该方面的键是示例的名称,其值描述了每个示例。Examples. 
    ResourceType 单一资源类型声明资源类型和特征
    Trait 单个特征声明Resource Types and Traits
    AnnotationTypeDeclaration一个单独的注解类型说明Annotations 
    Library 一个 RAML 库Libraries 
    Overlay 一个叠加文件Overlays 
    Extension  扩展文件Extensions 
    SecurityScheme 安全方案的定义Security Schemes 

    除了上述任何相关的 RAML 规范部分中允许的节点外,还允许以下可选节点:

    允许的节点描述
    uses? 导入外部库以在 RAML 类型片段中使用。

    如果一个文件以 RAML 片段标识符行开头,并且片段标识符不是 Library、Overlay 或 Extension,那么在删除 RAML 片段标识符行和任何 uses 节点后,文件的内容在结构上必须符合相关的 RAML 规范。例如,以 #%RAML 1.0 Trait 开头的 RAML 文件必须具有在资源类型和特性部分中定义的 RAML 特性声明的结构。将文件包含在正确的位置作为特性声明将得到一个有效的 RAML 文件。

    以下示例显示了一个定义资源类型的 RAML 片段文件,以及一个包含此类型化片段文件的文件。

    #%RAML 1.0 ResourceType
    
    #This file is located at resourceTypes/collection.raml
    
    description: A collection resource
    usage: Use this to describe a resource that lists items
    get:
      description: Retrieve all items
    post:
      description: Add an item
      responses:
        201:
          headers:
            Location:
    
    #%RAML 1.0
    title: Products API
    resourceTypes:
      collection: !include resourceTypes/collection.raml
    /products:
      type: collection
      description: All products
    

    生成的 API 定义等同于以下单个文档:

    #%RAML 1.0
    title: Products API
    resourceTypes:
      collection:
        description: A collection resource
        usage: Use this to describe a resource that lists items
        get:
          description: Retrieve all items
        post:
          description: Add an item
          responses:
            201:
              headers:
                Location:
    /products:
      type: collection
      description: All products
    

    以下示例显示了一个定义了多个示例和一个包含此片段文件的文件的 RAML 片段文件。

    #%RAML 1.0 NamedExample
    
    #This file is located at examples/paging-examples.raml
    
    onlyStart:
      displayName: Only Start
      value:
        start: 2
    startAndPageSize:
      description: Contains start and page size
      value:
        start: 3
        page-size: 20
    
    #%RAML 1.0
    title: Products API
    
    types:
      paging:
        properties:
          start?: number
          page-size?: number
    
    /products:
      description: All products
      get:
        queryString:
          type: paging
          examples: !include examples/paging-examples.raml
    

    解决包含

    当包含 RAML 或 YAML 文件时,RAML 解析器不仅必须读取内容,还必须解析并将内容添加到声明的结构中,就好像内容是内联声明的一样。如果包含的文件具有.raml、.yml 或.yaml 扩展名,或者具有以下媒体类型之一,RAML 解析器必须将文件内容解析为 RAML 内容,并将解析后的结构追加到 RAML 文档节点中:

    • application/raml+yaml /raml+yaml
    • text/yaml
    • text/x-yaml
    • application/yaml / YAML
    • application/x-yaml application/x-yaml

    否则,如果 RAML 解析器无法解析内容并附加结构,则文件的内容将被包含为标量。

    由于包含文件的解析上下文不在包含文件和其父文件之间共享,因此包含文件不得使用 YAML 引用来引用另一个文件中的锚点。同样地,父文件中的引用不得引用包含文件中定义的结构锚点。这些规则简化了 RAML 定义。

    在下面的示例中,API 根文档包括来自模式文件夹的两个文件,一个包含资源类型声明,另一个包含特性声明。

    #%RAML 1.0
    title: Example API
    version: v1
    resourceTypes: !include patterns/resourceTypes.raml
    traits: !include patterns/traits.raml
    
    # This file is located at patterns/resourceTypes.raml
    
    collection:
      get:
        is: [ paged ]
      post:
    member:
      get:
      patch:
      delete:
    
    # This file is located at patterns/traits.raml
    
    chargeable:
      headers:
        dept_code:
    paged:
      queryParameters:
        start:
          type: number
    

    生成的 API 定义等同于以下单个文档。

    #%RAML 1.0
    title: Example API
    version: v1
    resourceTypes:
      collection:
        get:
          is: [ paged ]
        post:
      member:
        get:
        patch:
        delete:
    traits:
      chargeable:
        headers:
          dept_code:
      paged:
        queryParameters:
          start:
            type: number
    

    图书馆

    RAML 库用于将任何数据类型声明、资源类型声明、特性声明和安全方案声明组合成模块化、外部化、可重用的组。虽然库的目的是在外部文档中定义常见声明,然后在需要的地方进行包含,但库也可以内联定义。

    除了允许在 RAML 类型片段的根节点上的任何节点外,RAML 库还允许以下可选节点:

    名称 描述
    types?  schemas?  resourceTypes?  traits?  securitySchemes? annotationTypes?  (<annotationName.>)? ()? uses? 每个节点的定义与 RAML 文档根节点的相应节点相同。库支持注释节点,就像其他 RAML 文档一样。
    usage? 描述特定库的内容或目的。该值是一个字符串,可以使用 Markdown 格式化。

    以下示例展示了一个简单的库,作为一个独立的、可重用的 RAML 片段文档。

    #%RAML 1.0 Library
    usage: |
      Use to define some basic file-related constructs.
    types:
      File:
        properties:
          name:
          length:
            type: integer
    traits:
      drm:
        headers:
          drm-key:
    resourceTypes:
      file:
        get:
          is: [ drm ]
        put:
          is: [ drm ]
    

    应用图书馆

    您可以通过在 RAM L或 RAML 类型片段文件的根部使用 OPTIONAL uses 节点来应用任意数量的库。uses 节点的值是一个键值对的映射。键被视为库名称或命名空间,值必须是 RAML 库文件的位置,通常是外部的 RAML 库片段文档。

    在应用库的文档中,库中的数据类型、资源类型、特性、安全方案和注释类型在文档中可用。在文档中使用点表示法引用库中的资源,如下所示:将库名称和点号(.)连接起来,然后是数据类型、资源类型、特性、安全方案或注释类型的名称。库名称定义了库节点在应用库的上下文中的命名空间。在特定文件的使用语句中定义的命名空间只能在该文件中使用,并且仅用于消除其中包含的库之间的歧义。因此,任何处理器都不得允许使用"."跨多个库组合命名空间。

    使用 uses 不会自动将远程库资源导入到本地文件中,但本地文件可以通过引用其内容中的资源来导入这些资源。例如,使用远程类型库的 RAML 类型片段文件可以通过引用来导入其中的某个类型。远程类型被包含在 RAML 类型片段文件中,就好像它是在本地定义的一样。

    以下示例演示了在第二个库中使用库、在资源类型片段中使用第二个库以及在RAML API 定义中使用第二个库。

    #%RAML 1.0 Library
    # This file is located at libraries/file-type.raml
    types:
      File:
        properties:
          name:
          length:
            type: integer
    
    #%RAML 1.0 Library
    # This file is located at libraries/files.raml
    usage: |
      Use to define some basic file-related constructs.
    uses:
      file-type: file-type.raml
    traits:
      drm:
        headers:
          drm-key:
    resourceTypes:
      file:
        get:
          is: [ drm ]
          responses:
            201:
              body:
                application/json:
                  type: file-type.File
        put:
          is: [ drm ]
    
    #%RAML 1.0 ResourceType
    # This file is located at files-resource.raml
    uses:
      files: libraries/files.raml
    get:
      is: [ files.drm ]
    

    以下示例无效,因为不允许链接命名空间。

    #%RAML 1.0 ResourceType
    # Invalid RAML Fragment
    uses:
      files: libraries/files.raml
    get:
      is: [ files.drm ]
      responses:
        200:
          body:
            application/json:
              type: files.file-type.File # invalid - no chaining allowed
    

     叠加和扩展

    API 定义可能需要以各种方式进行扩展以满足不同的需求。注解通过在此 RAML 规范中标准化之外添加元数据来扩展API。在现有 API 定义之上叠加标准或非标准元数据可以指定实现细节,或将面向人的文档翻译成不同语言,而不会改变 API 的行为。通过添加行为或覆盖某些方面来扩展 API 定义是满足不同需求的另一种方式。RAML 提供了两种机制来扩展 API 定义:叠加和扩展。

    覆盖和扩展是 RAML 文档,用于添加或覆盖 RAML API 定义的节点。覆盖或扩展文档的第一行必须以 “#%RAML 1.0 Overlay” 或“#%RAML 1.0 Extension” 开头,后面只能是行尾。覆盖或扩展文档必须包含一个根级 extends 节点,其值必须是有效的 RAML API 定义或另一个覆盖或扩展的位置;位置规范可以是绝对路径、相对路径或URL,相当于 !include 标签的参数。在 extends 节点中指定的文档称为主 RAML 文档。

    覆盖或扩展文档的剩余部分遵循与 RAML API 定义相同的规则,但在覆盖的情况下有一定的限制。覆盖或扩展文档中路径的解析(例如 !include 标签或库)必须相对于引用所在的文档,而不是 API 根文档。

    要应用叠加或扩展,RAML 处理器必须对主 RAML 文档和叠加或扩展应用合并算法,从而生成修改后的 API 定义;在应用叠加的情况下,修改后的 API 定义将被验证以符合叠加的限制。

    要应用任何叠加和/或扩展的组合,所有内容必须共享相同的主RAML文档。应用程序的流程是:

    1. 将第一个叠加或扩展应用于主 RAML 文档,生成修改后的 API 定义,并在叠加的情况下验证结果。
    2. 将第二个覆盖或扩展应用于修改后的 API 定义,就好像后者是主要的 RAML 文档一样,并在覆盖的情况下再次验证结果。
    3. 重复上一步骤,直到应用最后一个叠加或扩展。
    4. 在应用合并算法之前解析所有的 !include 标签,在每个叠加层应用后验证叠加限制,并应用所有类型、资源类型、特性和注解类型的继承。

    叠加层

    覆盖层在保留行为和功能方面的同时,添加或覆盖 RAML API 定义的节点。RAML API 定义的某些节点指定了 API 的行为,例如资源、方法、参数、主体、响应等等。这些节点不能通过应用覆盖层来更改。相反,其他节点,例如描述或注释,涉及功能接口之外的问题,例如某种语言的面向人类的描述性文档,或用于自动化工具的实现或验证信息。通过应用覆盖层可以更改这些节点。

    覆盖层对于将界面与实现分离非常重要。覆盖层使得需要严格控制的 API 行为方面(例如 API 提供者与使用者之间的合同)与需要较少控制的人类或实现方面能够有不同的生命周期。例如,可以通过添加用于测试和监控工具的钩子、附加与 API 注册表相关的元数据,或提供更新或翻译的人类文档来实现,而无需改变 API 的行为方面。这些事情可以通过严格的版本和变更管理流程来控制。

    很难在API的行为和实现导向方面划定一个明确的界限,因为例如,API的某些语义通常只在人类文档中记录。然而,RAML确实定义了处理器必须遵循的覆盖文件上的特定行为不变性限制。处理器可以选择在应用一个或多个覆盖后,提供主API定义以及其修改,以便消费者可以从所有可用信息中受益。例如,如果覆盖文件用于本地化资源、方法和数据的文本描述,那么生成的文档的消费者可以选择应用哪个本地化覆盖。

    覆盖层的行为不变性限制定义如下:在应用合并算法以及应用资源类型和特性之后,将合并文档中的节点树与解析所有!include标签后的主RAML文档中的节点树进行比较。文档中的任何差异必须仅限于以下表格中列出的节点。

    名称允许的差异
    title  displayName  description  documentation  usage  example 合并的树可以包含此类型的新节点或具有与主树中不同值的节点。
    types 除了在本表其他地方描述的允许的差异之外,合并树还可以包括新的数据类型。
    annotationTypes 合并的树可以包含新的注释类型或现有注释类型的新值,只要合并的 API 定义中的所有注释都符合合并树中的注释类型。
    any annotation node 合并的树可以包含在合并树中声明的注释类型的新注释,或者具有与主树中不同值的注释。
    examples 合并的树可以包含新的命名示例,或者具有与主树中不同值的命名示例。
    documentation 合并的树可以包含作为文档根级节点值的数组中的新项目。要更改现有项目,可以在覆盖中覆盖文档节点本身。

    以下示例演示了一个非常简单的 RAML 定义图书馆 API,以及提供西班牙语翻译和 API 监控服务的叠加文件的元数据。

    #%RAML 1.0
    # This file is located at librarybooks.raml
    title: Book Library API
    documentation:
      - title: Introduction
        content: Automated access to books
      - title: Licensing
        content: Please respect copyrights on our books.
    /books:
      description: The collection of library books
      get:
    
    #%RAML 1.0 Overlay
    usage: Spanish localization
    extends: librarybooks.raml
    documentation:
      - title: Introducción
        content: El acceso automatizado a los libros
      - title: Licencias
        content: Por favor respeta los derechos de autor de los libros
    /books:
      description: La colección de libros de la biblioteca
    
    #%RAML 1.0 Overlay
    usage: Hints for monitoring the library books API
    extends: librarybooks.raml
    annotationTypes:
      monitor:
        properties:
          frequency:
            properties:
              interval: integer
              unitOfMeasure:
                enum: [ seconds, minutes, hours ]
          script:
    /books:
      get:
        (monitor):
          frequency:
            interval: 5
            unitOfMeasure: minutes
          script: randomBooksFetch
    

    扩展

    扩展通过添加或修改行为和其他功能来扩展 RAML API 定义。扩展可以将核心的广泛可用的 API 与提供给更受限制的受众的功能层分离开来,用于创建用于稍有不同目的的 API 变体,或者用于指定 API 的特定实例节点,例如其服务端点(URL),而不改变其纯接口定义文档。

    以下示例是在“覆盖层”部分的示例基础上进行扩展,增加了管理员添加图书项目到收藏集的功能扩展,添加了一个覆盖层来提供新增功能的翻译,并添加了一个定位 API 特定服务端点的扩展。

    #%RAML 1.0 Extension
    usage: Add administrative functionality
    extends: librarybooks.raml
    /books:
      post:
        description: Add a new book to the collection
    
    #%RAML 1.0 Overlay
    usage: Spanish localization for admin functionality
    extends: librarybooks.raml
    /books:
      post:
        description: Añadir un nuevo libro para la colección
    
    #%RAML 1.0 Extension
    usage: The location of the public instance of the Piedmont library API
    extends: librarybooks.raml
    baseUri: http://api.piedmont-library.com
    

    合并规则

    本节描述了如何将叠加/扩展结构应用于主体。

     术语

    对象和属性

    对象是一个 YAML 中包含映射的 MAP 或 SEQUENCE。

    属性是 YAML 中的一种映射,由键和值对组成。

    在下面的例子中,“properties” 是一个属性键,而子节点对象是其值。

    properties:
      statusCode: 200
      responseParameters:
        type: object
        description: "some description"
    

    在同一个示例中,还有一个 "responseParameters" 属性键和其对象值:

    properties:
      statusCode: 200
      responseParameters:
        type: object
        description: "some description"
    

    而且,尽管 "statusCode"、"type" 和 "description" 也是属性,但它们的值并不是对象:

    properties:
      statusCode: 200
      responseParameters:
        type: object
        description: "some description"
    

    在下面的示例中,“FilteredByPrice” 和 “Paged” 是具有对象值的属性。

    traits:
     - FilteredByPrice:
         queryParameters:
           priceLessThen?:
             type: number
           priceMoreThen?:
            type: number
     - Paged:
         queryParameters:
           offset: number
           length: number
    

    数组

    数组是指 YAML 中包含标量或包含映射的序列。

    在下面的示例中,“enum” 属性键具有一个数组值。

    enum:
      - White
      - Black
      - Colored
    

    在这个数组定义的例子中,一个名为 "documentation" 的属性键具有一个包含两个对象的数组值:

    documentation:
      - title: Introduction
        content: Automated access to books
      - title: Licensing
        content: Please respect copyrights on our books.
    

    物业类型

    在合并算法中,属性类型被称为属性种类,可以是以下加粗显示的属性之一:

    对象属性 - 一个具有对象作为值的属性。

    在下面的例子中,“properties”属性是一个对象属性:

    properties:
      statusCode: 200
      responseParameters:
        type: object
        description: "some description"
    

    数组属性 - 一个具有对象数组作为值的属性。

    在下面的例子中,“documentation”属性是一个数组属性:

    documentation:
      - title: Introduction
        content: Automated access to books
      - title: Licensing
        content: Please respect copyrights on our books.
    

    简单属性 - 一个具有YAML标量或YAML标量序列作为值的属性。

    在下面的例子中,"statusCode" 和 "enum" 是简单的属性。

    statusCode: 200
    enum:
      - White
      - Black
      - Colored
    

    单值简单属性 - 一个具有YAML标量值的简单属性。

    statusCode: 200
    

    多值简单属性 - 一个简单属性,其值是一个 YAML 标量的序列。

    enum:
      - White
      - Black
      - Colored
    

    例外情况:

    • 示例始终是简单属性,尽管可以将复杂的 YAML 样本作为值。
    • 注释始终是简单属性,尽管可能具有复杂的节点结构。
    • 资源类型应用程序始终是简单属性。
    • 特征应用始终是简单属性。
    • 安全模式应用程序始终是简单属性。

    冲突的属性

    冲突属性是指不能在同一个对象中共存的属性。它们被称为互斥属性。

    例如,方法对象的"queryString"和"queryParameters"属性是冲突的属性。

    忽略的属性

    忽略的属性 - 以下属性被视为被忽略的:"uses" 和 "usage"。

    树木

    主树 - 主文件文档YAML解析树结果。扩展树 - 叠加或扩展的YAML解析树结果。目标树 - 结果树。

    合并算法:

    主文档和扩展或叠加文档通过YAML解析器解析,生成主树和扩展树。

    主树和扩展树已通过验证,如果出现错误,合并过程将被取消。

    所有包含的问题都已解决并应用于主树和扩展树。

    所有用途都已解决并应用于主树和扩展树。树木不得具有使用相同命名空间但引用不同文件的指令。

    所有特征和资源类型的应用都适用于主树

    最初,目标树与主树相等。

    当前扩展树对象已设置为扩展树根(API)。当前目标树对象已设置为目标树根(API)。

    对于每个当前扩展树对象属性,执行以下操作:

    • 如果该属性是一个被忽略的属性,则继续处理下一个属性。

    • 如果当前目标树对象中存在同名属性:

      • 如果属性和当前目标树对象中同名的属性具有不同的属性类型:

        • 在具有相同名称的当前目标树对象属性中,属性值将被替换为来自当前扩展树对象属性的值。
      • 如果该物业是一个简单的物业

        • 如果该属性是单值简单属性,

          • 在具有相同名称的当前目标树对象属性中,属性值将被替换为当前扩展树对象属性中的值。
        • 如果该属性是多值简单属性

          • 如果不存在相同的值,则将当前扩展树对象属性的属性值添加到同名的当前目标树对象属性值中。
      • 如果该属性是一个对象属性:

        • 相同的合并算法递归地应用于将当前扩展树对象设置为属性值,并将当前目标树对象设置为当前目标树对象中同名属性的值。
      • 如果该属性是一个数组属性:

        • 将属性值中的对象添加到同名的当前目标树对象属性值中。
    • 如果当前目标树对象中不存在同名属性:

      • 从当前目标树对象中移除所有冲突属性
      • 该属性被添加到当前目标树对象中。

    特征和资源类型的应用再次应用于目标树。

    目标树已验证。

    如果扩展树是一个覆盖层,目标树将与主树进行比较。除了在“允许差异”表格中列出的差异之外,绝对不能有任何差异。否则,该过程将被取消。

    目标树具有其资源类型和应用的特征。

    目标树正在被序列化为文档或作为算法输出返回。

    参考文献

    规范参考文献

    Berners-Lee, T., Masinter, L., and M. McCahill, "Uniform Resource Locators (URL)", RFC 1738, December 1994.

    Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, March 1997.

    Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform Resource Identifiers (URI): Generic Syntax", RFC 2396, August 1998.

    Fielding, R., Gettys, J., Mogul, J., Frystyk, H., Masinter, L., Leach, P., and T. Berners-Lee, "Hypertext Transfer Protocol -- HTTP/1.1", RFC 2616, June 1999.

    Crockford, D., "The application/json Media Type for JavaScript Object Notation (JSON)", RFC 4627, July 2006.

    Dusseault, L. and J. Snell, "PATCH Method for HTTP", RFC 5789, March 2010.

    Gregorio, J., Fielding, R., Hadley, M., Nottingham, M., and D. Orchard, "URI Template", RFC 6570, March 2012.

    Ben Kiki, O., Evans, C., and I. Net, "YAML Aint Markup Language", 2009, http://www.yaml.org/spec/1.2/spec.html.

    信息参考资料

    Galiegue, F., Zyp, K., and G. Court, "JSON Schema: core definitions and terminology", 2013, http://tools.ietf.org/html/draft-zyp-json-schema-04.

    Gruber, J., "Markdown Syntax Documentation", 2004, http://daringfireball.net/projects/markdown/syntax.

    Fielding, R., "Representational State Transfer (REST)", 2000, http://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm.

    Rose, M., "Writing I-Ds and RFCs using XML", RFC 2629, June 1999.

    Rescorla, E. and B. Korver, "Guidelines for Writing RFC Text on Security Considerations", BCP 72, RFC 3552, July 2003.

    Gao, S., Sperberg-McQueen, C., and H. Thompson, "W3C XML Schema Definition Language (XSD) 1.1", 2012, http://www.w3.org/XML/Schema.

    Built with