Modular RAML Using Includes, Libraries, Overlays and Extensions – 使用包含物、库、覆盖物和扩展物的模块化RAML

最后修改: 2016年 2月 8日

1. Introduction

1.介绍

In our first two articles on RAML – the RESTful API Modeling Language – we introduced some basic syntax, including the use of data types and JSON schema, and we showed how to simplify a RAML definition by extracting common patterns into resource types and traits.

在我们的第一篇两篇关于RAML(RESTful API建模语言)的文章中,我们介绍了一些基本语法。我们介绍了一些基本语法,包括数据类型和JSON模式的使用,并展示了如何通过将常见模式提取为资源类型属性来简化RAML定义。

In this article, we show how you can break your RAML API definition into modules by making use of includes, libraries, overlays, and extensions.

在这篇文章中,我们展示了如何通过利用includeslibrariesoverlaysextensions,将RAML API定义分解成模块

2. Our API

2.我们的API

For the purpose of this article, we shall focus on the portion of our API involving the entity type called Foo.

为了本文的目的,我们将重点关注我们的API中涉及名为Foo的实体类型的部分。

Here are the resources making up our API:

以下是构成我们API的资源。

  • GET /api/v1/foos
  • POST /api/v1/foos
  • GET /api/v1/foos/{fooId}
  • PUT /api/v1/foos/{fooId}
  • DELETE /api/v1/foos/{fooId}

3. Includes

3.包括

The purpose of an include is to modularize a complex property value in a RAML definition by placing the property value in an external file.

include的目的是将RAML定义中的复杂属性值模块化,将该属性值放在一个外部文件中。

Our first article touched briefly on the use of includes when we were specifying data types and examples whose properties were being repeated inline throughout the API.

我们的第一篇文章简要地谈到了includes的使用,当时我们正在指定数据类型和例子,其属性在整个API中被内联重复。

3.1. General Usage and Syntax

3.1.一般用法和语法

The !include tag takes a single argument: the location of the external file containing the property value. This location may be an absolute URL, a path relative to the root RAML file, or a path relative to the including file.

! include标签需要一个参数:包含属性值的外部文件的位置。这个位置可以是一个绝对的URL,一个相对于RAML根文件的路径,或者一个相对于包含文件的路径。

A location starting with a forward slash (/) indicates a path relative to the location of the root RAML file, and a location beginning without a slash is interpreted to be relative to the location of the including file.

以正斜杠(/)开头的位置表示相对于RAML根文件的位置,而没有斜杠开头的位置则被解释为相对于包括文件的位置。

The logical corollary to the latter is that an included file may itself contain other !include directives.

后者的逻辑推论是,一个被包含的文件本身可能包含其他!include指令。

Here is an example showing all three uses of the !include tag:

下面是一个例子,显示了!include标签的所有三种用途。

#%RAML 1.0
title: Baeldung Foo REST Services API
...
types: !include /types/allDataTypes.raml
resourceTypes: !include allResourceTypes.raml
traits: !include http://foo.com/docs/allTraits.raml

3.2. Typed Fragments

3.2.类型化的片段

Rather than placing all the types, resource types or traits in their own respective include files, you can also use special types of includes known as typed fragments to break each of these constructs into multiple include files, specifying a different file for each type, resource type or trait.

而不是将所有的类型资源类型特质放在他们各自的include文件中。你也可以使用特殊类型的include,称为typed fragments,将这些结构分成多个include文件,为每个typeresource typetrait指定一个不同文件。

You can also use typed fragments to define user documentation items, named examples, annotations, libraries, overlays, and extensions. We will cover the use of overlays and extensions later in the article.

您还可以使用类型片段来定义用户文档项目命名的示例注释覆盖物扩展。我们将在文章后面介绍overlaysextensions的使用。

Although it is not required, the first line of an include file that is a typed fragment may be a RAML fragment identifier of the following format:

虽然不是必须的,但作为类型片段include文件的第一行可以是以下格式的RAML片段标识。

#%RAML 1.0 <fragment-type>

For example, the first line of a typed fragment file for a trait would be:

例如,一个typed fragment文件的第一行将是trait的。

#%RAML 1.0 Trait

If a fragment identifier is used, then the contents of the file MUST contain only valid RAML for the type of fragment being specified.

如果使用一个片段标识符,那么文件的内容必须只包含指定的片段类型的有效RAML。

Let’s look first at a portion of the traits section of our API:

让我们先看看我们的API中traits部分的内容。

traits:
  - hasRequestItem:
      body:
        application/json:
          type: <<typeName>>
  - hasResponseItem:
      responses:
          200:
            body:
              application/json:
                type: <<typeName>>
                example: !include examples/<<typeName>>.json

In order to modularize this section using typed fragments, we first rewrite the traits section as follows:

为了使用类型片段来模块化这一部分,我们首先将traits部分重写如下。

traits:
  - hasRequestItem: !include traits/hasRequestItem.raml
  - hasResponseItem: !include traits/hasResponseItem.raml

We would then write the typed fragment file hasRequestItem.raml:

然后我们会写下类型片段文件hasRequestItem.raml

#%RAML 1.0 Trait
body:
  application/json:
    type: <<typeName>>

The typed fragment file hasResponseItem.raml would look like this:

typed fragment文件hasResponseItem.raml将看起来像这样。

#%RAML 1.0 Trait
responses:
    200:
      body:
        application/json:
          type: <<typeName>>
          example: !include /examples/<<typeName>>.json

4. Libraries

4.图书馆

RAML libraries may be used to modularize any number and combination of data types, security schemes, resource types, traits, and annotations.

RAML 可用于模块化任何数量和组合的数据类型安全方案资源类型traits注释

4.1. Defining a Library

4.1.定义一个图书馆

Although usually defined in an external file, which is then referenced as an include, a library may also be defined inline. A library contained in an external file can reference other libraries as well.

尽管通常在外部文件中定义,然后作为include被引用,但library也可以内联定义。包含在外部文件中的library也可以引用其他library

Unlike a regular include or typed fragment, a library contained in an external file must declare the top-level element names that are being defined.

与普通的includetyped fragment不同,包含在外部文件中的library必须声明正在定义的顶级元素名称。

Let’s rewrite our traits section as a library file:

让我们把traits部分改写为library文件。

#%RAML 1.0 Library
# This is the file /libraries/traits.raml
usage: This library defines some basic traits
traits:
  hasRequestItem:
    usage: Use this trait for resources whose request body is a single item
    body:
      application/json:
        type: <<typeName>>
  hasResponseItem:
    usage: Use this trait for resources whose response body is a single item
    responses:
        200:
          body:
            application/json:
              type: <<typeName>>
              example: !include /examples/<<typeName>>.json

4.2. Applying a Library

4.2.应用一个库

Libraries are applied via the top-level uses property, the value of which is one or more objects whose property names are the library names and whose property values make up the contents of the libraries.

通过顶层的uses属性被应用,其值是一个或多个对象,其属性名称是名称,其属性值构成的内容。

Once we have created the libraries for our security schemes, data types, resource types, and traits, we can apply the libraries to the root RAML file:

一旦我们为我们的安全方案数据类型资源类型特质创建了,我们就可以将应用到RAML根文件。

#%RAML 1.0
title: Baeldung Foo REST Services API
uses:
  mySecuritySchemes: !include libraries/security.raml
  myDataTypes: !include libraries/dataTypes.raml
  myResourceTypes: !include libraries/resourceTypes.raml
  myTraits: !include libraries/traits.raml

4.3. Referencing a Library

4.3.引用一个图书馆

A library is referenced by concatenating the library name, a dot (.), and the name of the element (e.g. data type, resource type, trait, etc) being referenced.

通过连接library名称、点(.)和被引用的元素(如数据类型、资源类型、特性等)的名称来引用library

You may recall from our previous article how we refactored our resource types using the traits that we had defined. The following example shows how to rewrite our “item” resource type as a library, how to include the traits library file (shown above) within the new library, and how to reference the traits by prefixing the trait names with their library name qualifier (“myTraits“):

您可能还记得我们之前的文章中,我们如何使用我们定义的traits重构我们的resource types。下面的示例展示了如何将我们的 “项目”资源类型重写为库,如何将traits文件(如上所示)包含在新的中。以及如何通过在traits名称前加上library名称限定符(”myTraits“)来引用traits

#%RAML 1.0 Library
# This is the file /libraries/resourceTypes.raml
usage: This library defines the resource types for the API
uses:
  myTraits: !include traits.raml
resourceTypes:
  item:
    usage: Use this resourceType to represent any single item
    description: A single <<typeName>>
    get:
      description: Get a <<typeName>> by <<resourcePathName>>
      is: [ myTraits.hasResponseItem, myTraits.hasNotFound ]
    put:
      description: Update a <<typeName>> by <<resourcePathName>>
      is: [ myTraits.hasRequestItem, myTraits.hasResponseItem, myTraits.hasNotFound ]
    delete:
      description: Delete a <<typeName>> by <<resourcePathName>>
      is: [ myTraits.hasNotFound ]
      responses:
        204:

5. Overlays and Extensions

5.叠加和扩展

Overlays and extensions are modules defined in external files that are used to extend an API. An overlay is used to extend non-behavioral aspects of an API, such as descriptions, usage directions, and user documentation items, whereas an extension is used to extend or override behavioral aspects of the API.

覆盖物扩展是在外部文件中定义的模块,用于扩展API。覆盖用于扩展API的非行为方面,例如描述、使用说明和用户文档项目,而扩展则用于扩展或覆盖API的行为方面。

Unlike includes, which are referenced by other RAML files to be applied as if they were being coded inline, all overlay and extension files must contain a reference (via the top-level masterRef property) to its master file, which can be either a valid RAML API definition or another overlay or extension file, to which they are to be applied.

includes不同,includes被其他RAML文件引用来应用,就像它们被内联编码一样,所有的overlayextension文件必须包含对其主文件的引用(通过顶层的masterRef属性),该文件可以是一个有效的RAML API定义,或另一个overlayextension文件,它们将被应用到其中。

5.1. Definition

5.1.定义

The first line of an overlay or extension file must be formatted as follows:

覆盖或扩展文件的第一行必须有如下格式。

RAML 1.0 Overlay

And the first line of an overlay file must be formatted similarly:

而叠加文件的第一行也必须采用类似的格式。

RAML 1.0 Extension

5.2. Usage Constraints

5.2.使用限制条件

When using a set of overlays and/or extensions, all of them must refer to the same master RAML file. In addition, RAML processing tools usually expect the root RAML file and all overlay and extension files to have a common file extension (e.g. “.raml”).

当使用一组覆盖件和/或扩展件时,所有这些都必须指向同一个主RAML文件。此外,RAML处理工具通常希望根RAML文件和所有覆盖扩展文件有一个共同的文件扩展名(例如”.raml”)。

5.3. Use Cases for Overlays

5.3.覆盖层的使用案例

The motivation behind overlays is to provide a mechanism for separating interface from implementation, thus allowing the more human-oriented parts of a RAML definition to change or grow more frequently, while the core behavioral aspects of the API remain stable.

overlays背后的动机是提供一种机制,将接口与实现分开,从而允许RAML定义中更多面向人类的部分更频繁地改变或增长,而API的核心行为方面保持稳定。

A common use case for overlays is to provide user documentation and other descriptive elements in multiple languages. Let’s rewrite the title of our API and add some user documentation items:

overlays的一个常见用例是提供多语言的用户文档和其他描述性元素。让我们重写我们的API的标题并添加一些用户文档项目。

#%RAML 1.0
title: API for REST Services used in the RAML tutorials on Baeldung.com
documentation:
  - title: Overview
  - content: |
      This document defines the interface for the REST services
      used in the popular RAML Tutorial series at Baeldung.com.
  - title: Copyright
  - content: Copyright 2016 by Baeldung.com. All rights reserved.

Here is how we would define a Spanish language overlay for this section:

以下是我们如何定义本节的西班牙语覆盖层。

#%RAML 1.0 Overlay
# File located at (archivo situado en):
# /overlays/es_ES/documentationItems.raml
masterRef: /api.raml
usage: |
  To provide user documentation and other descriptive text in Spanish
  (Para proporcionar la documentación del usuario y otro texto descriptivo
  en español)
title: |
  API para servicios REST utilizados en los tutoriales RAML
  en Baeldung.com
documentation:
  - title: Descripción general
  - content: |
      Este documento define la interfaz para los servicios REST
      utilizados en la popular serie de RAML Tutorial en Baeldung.com.
  - title: Derechos de autor
  - content: |
      Derechos de autor 2016 por Baeldung.com.
      Todos los derechos reservados.

Another common use case for overlays is to externalize annotation metadata, which essentially are a way of adding non-standard constructs to an API in order to provide hooks for RAML processors such as testing and monitoring tools.

overlays的另一个常见用例是外部化annotation元数据,这基本上是向API添加非标准构造的一种方式,以便为RAML处理器(如测试和监控工具)提供钩子。

5.4. Use Cases for Extensions

5.4.扩展的使用案例

As you may infer from the name, extensions are used to extend an API by adding new behaviors and/or modifying existing behaviors of an API. An analogy from the object-oriented programming world would be a subclass extending a superclass, where the subclass can add new methods and/or override existing methods. An extension may also extend an API’s non-functional aspects.

正如您从名称中推断的那样,扩展用于通过添加新行为和/或修改API的现有行为来扩展API。在面向对象的编程世界中,有一个类比是子类扩展超类,子类可以添加新的方法和/或覆盖现有的方法。扩展也可以扩展一个API的非功能方面。

An extension might be used, for example, to define additional resources that are exposed only to a select set of users, such as administrators or users having been assigned a particular role. An extension could also be used to add features for a newer version of an API.

例如,extension可用于定义仅向一组特定用户(例如管理员或被分配了特定角色的用户)公开的额外资源。扩展也可用于为较新版本的API添加功能。

Below is an extension that overrides the version of our API and adds resources that were unavailable in the previous version:

下面是一个扩展,它覆盖了我们的API的版本,并增加了以前版本中不可用的资源。

#%RAML 1.0 Extension
# File located at:
# /extensions/en_US/additionalResources.raml
masterRef: /api.raml
usage: This extension defines additional resources for version 2 of the API.
version: v2
/foos:
  /bar/{barId}:
    get:
      description: |
        Get the foo that is related to the bar having barId = {barId}
      typeName: Foo
      queryParameters:
        barId?: integer
        typeName: Foo
        is: [ hasResponseItem ]

And here is a Spanish-language overlay for that extension:

这里是该扩展的西班牙语覆盖物

#%RAML 1.0 Overlay
# Archivo situado en:
# /overlays/es_ES/additionalResources.raml
masterRef: /api.raml
usage: |
  Se trata de un español demasiado que describe los recursos adicionales
  para la versión 2 del API.
version: v2
/foos:
  /bar/{barId}:
    get:
      description: |
        Obtener el foo que se relaciona con el bar tomando barId = {barId}

It is worth noting here that although we used an overlay for the Spanish-language overrides in this example because it does not modify any behaviors of the API, we could just as easily have defined this module to be an extension. And it may be more appropriately defined as an extension, given that its purpose is to override properties found in the English-language extension above it.

值得注意的是,尽管我们在这个例子中使用了overlay来覆盖西班牙语,因为它没有修改API的任何行为,但我们也可以很容易地将这个模块定义为extension。考虑到它的目的是覆盖它上面的英语extension中的属性,它可能更适合被定义为extension

6. Conclusion

6.结论

In this tutorial, we have introduced several techniques to make a RAML API definition more modular by separating common constructs into external files.

在本教程中,我们介绍了几种技术,通过将常见的构造分离到外部文件中,使RAML API定义更加模块化。

First, we showed how the include feature in RAML can be used to refactor individual, complex property values into reusable external file modules known as typed fragments. Next, we demonstrated a way of using the include feature to externalize certain sets of elements into reusable libraries. Finally, we extended some behavioral and non-behavioral aspects of an API through the use of overlays and extensions.

首先,我们展示了RAML中的include特性如何被用来重构单个复杂的属性值,使之成为可重用的外部文件模块,即typed fragments。接下来,我们展示了一种使用include功能的方法,将某些元素集外化为可重用的libraries。最后,我们通过使用overlaysextensions来扩展API的一些行为和非行为方面。

To learn even more about RAML modularization techniques, please visit the RAML 1.0 spec.

要了解有关RAML模块化技术的更多信息,请访问RAML 1.0规范

You can view the full implementation of the API definition used for this tutorial in the github project.

您可以在github项目中查看本教程所使用的API定义的完整实现

Next »

Define Custom RAML Properties Using Annotations

« Previous

Eliminate Redundancies in RAML with Resource Types and Traits