元数据
本节详细介绍了基于 Spring Data REST 的应用所提供的各种元数据形式。
应用级配置文件语义 (ALPS)
ALPS 是一种数据格式,用于定义应用级语义的简单描述,其复杂性与 HTML microformats 类似。ALPS 文档可用作配置文件,以解释使用应用无关的媒体类型(如 HTML、HAL、Collection+JSON、Siren 等)的文档的应用语义。这提高了配置文件文档在不同媒体类型之间的可重用性。
https://tools.ietf.org/html/draft-amundsen-richardson-foster-alps-00
Spring Data REST 为每个导出的仓库提供一个 ALPS 文档。它包含关于 RESTful 转换以及每个仓库属性的信息。
Spring Data REST 应用的根目录有一个 profile 链接。假设你有一个包含 persons
和相关 addresses
的应用,根文档如下所示
{
"_links" : {
"persons" : {
"href" : "http://localhost:8080/persons"
},
"addresses" : {
"href" : "http://localhost:8080/addresses"
},
"profile" : {
"href" : "http://localhost:8080/profile"
}
}
}
如果你导航到 localhost:8080/profile
的 profile 链接,你会看到类似以下内容
{
"_links" : {
"self" : {
"href" : "http://localhost:8080/profile"
},
"persons" : {
"href" : "http://localhost:8080/profile/persons"
},
"addresses" : {
"href" : "http://localhost:8080/profile/addresses"
}
}
}
在根级别,profile 是一个单一链接,无法提供多个应用配置文件。这就是为什么你必须导航到 /profile 来查找每个资源的元数据的链接。 |
如果你导航到 /profile/persons
并查看 Person
资源的 profile 数据,你会看到类似以下示例的内容
{
"version" : "1.0",
"descriptors" : [ {
"id" : "person-representation", (1)
"descriptors" : [ {
"name" : "firstName",
"type" : "SEMANTIC"
}, {
"name" : "lastName",
"type" : "SEMANTIC"
}, {
"name" : "id",
"type" : "SEMANTIC"
}, {
"name" : "address",
"type" : "SAFE",
"rt" : "http://localhost:8080/profile/addresses#address"
} ]
}, {
"id" : "create-persons", (2)
"name" : "persons", (3)
"type" : "UNSAFE", (4)
"rt" : "#person-representation" (5)
}, {
"id" : "get-persons",
"name" : "persons",
"type" : "SAFE",
"rt" : "#person-representation"
}, {
"id" : "delete-person",
"name" : "person",
"type" : "IDEMPOTENT",
"rt" : "#person-representation"
}, {
"id" : "patch-person",
"name" : "person",
"type" : "UNSAFE",
"rt" : "#person-representation"
}, {
"id" : "update-person",
"name" : "person",
"type" : "IDEMPOTENT",
"rt" : "#person-representation"
}, {
"id" : "get-person",
"name" : "person",
"type" : "SAFE",
"rt" : "#person-representation"
} ]
}
1 | Person 资源的属性详细列表,标识为 #person-representation ,列出了属性的名称。 |
2 | 支持的操作。这个操作表明如何创建一个新的 Person 。 |
3 | name 是 persons ,这表明(因为它采用复数形式)POST 操作应应用于整个集合,而不是单个 person 。 |
4 | type 是 UNSAFE ,因为此操作会改变系统的状态。 |
5 | rt 是 #person-representation ,这表明返回的资源类型将是 Person 资源。 |
此 JSON 文档的媒体类型是 application/alps+json 。这与之前的 JSON 文档不同,之前的文档媒体类型是 application/hal+json 。这些格式不同,并受不同规范的管理。 |
当你查看一个集合资源时,你还可以在 _links
集合中找到一个 profile
链接,如下例所示
{
"_links" : {
"self" : {
"href" : "http://localhost:8080/persons" (1)
},
... other links ...
"profile" : {
"href" : "http://localhost:8080/profile/persons" (2)
}
},
...
}
1 | 此 HAL 文档表示 Person 集合。 |
2 | 它有一个指向相同 URI 的 profile 链接,用于元数据。 |
同样,默认情况下,profile
链接提供 ALPS。但是,如果你使用 Accept
头,它可以提供 application/alps+json
。
超媒体控制类型
ALPS 为每个超媒体控制显示类型。它们包括
类型 | 描述 |
---|---|
SEMANTIC (语义) |
一个状态元素(例如 |
SAFE (安全) |
触发安全、幂等状态转换的超媒体控制(例如 |
IDEMPOTENT (幂等) |
触发不安全、幂等状态转换的超媒体控制(例如 |
UNSAFE (不安全) |
触发不安全、非幂等状态转换的超媒体控制(例如 |
在前面所示的表示部分,来自应用的数据片段被标记为 SEMANTIC
。address
字段是一个链接,涉及安全的 GET
操作来检索。因此,它被标记为 SAFE
。超媒体操作本身映射到前面表格中所示的类型。
带投影的 ALPS
如果你定义了任何投影,它们也会列在 ALPS 元数据中。假设我们也定义了 inlineAddress
和 noAddresses
,它们会出现在相关的操作中。(有关这两个投影的定义和讨论,请参见“投影”。)也就是说,GET 会出现在整个集合的操作中,而 GET 会出现在单个资源的操作中。以下示例显示了 get-persons
子部分的替代版本
...
{
"id" : "get-persons",
"name" : "persons",
"type" : "SAFE",
"rt" : "#person-representation",
"descriptors" : [ { (1)
"name" : "projection",
"doc" : {
"value" : "The projection that shall be applied when rendering the response. Acceptable values available in nested descriptors.",
"format" : "TEXT"
},
"type" : "SEMANTIC",
"descriptors" : [ {
"name" : "inlineAddress", (2)
"type" : "SEMANTIC",
"descriptors" : [ {
"name" : "address",
"type" : "SEMANTIC"
}, {
"name" : "firstName",
"type" : "SEMANTIC"
}, {
"name" : "lastName",
"type" : "SEMANTIC"
} ]
}, {
"name" : "noAddresses", (3)
"type" : "SEMANTIC",
"descriptors" : [ {
"name" : "firstName",
"type" : "SEMANTIC"
}, {
"name" : "lastName",
"type" : "SEMANTIC"
} ]
} ]
} ]
}
...
1 | 出现了一个新属性 descriptors ,它包含一个数组,其中有一个条目 projection 。 |
2 | 在 projection.descriptors 内部,我们可以看到 inLineAddress 。它渲染 address 、firstName 和 lastName 。在投影中渲染关系会导致包含内联的数据字段。 |
3 | noAddresses 提供了一个包含 firstName 和 lastName 的子集。 |
有了所有这些信息,客户端不仅可以推断出可用的 RESTful 转换,还可以在一定程度上推断出与后端交互所需的数据元素。
向 ALPS 描述添加自定义详细信息
你可以创建出现在 ALPS 元数据中的自定义消息。为此,请创建 rest-messages.properties
文件,如下所示
rest.description.person=A collection of people
rest.description.person.id=primary key used internally to store a person (not for RESTful usage)
rest.description.person.firstName=Person's first name
rest.description.person.lastName=Person's last name
rest.description.person.address=Person's address
这些 rest.description.*
属性定义了要为 Person
资源显示的详细信息。它们会改变 person-representation
的 ALPS 格式,如下所示
...
{
"id" : "person-representation",
"doc" : {
"value" : "A collection of people", (1)
"format" : "TEXT"
},
"descriptors" : [ {
"name" : "firstName",
"doc" : {
"value" : "Person's first name", (2)
"format" : "TEXT"
},
"type" : "SEMANTIC"
}, {
"name" : "lastName",
"doc" : {
"value" : "Person's last name", (3)
"format" : "TEXT"
},
"type" : "SEMANTIC"
}, {
"name" : "id",
"doc" : {
"value" : "primary key used internally to store a person (not for RESTful usage)", (4)
"format" : "TEXT"
},
"type" : "SEMANTIC"
}, {
"name" : "address",
"doc" : {
"value" : "Person's address", (5)
"format" : "TEXT"
},
"type" : "SAFE",
"rt" : "http://localhost:8080/profile/addresses#address"
} ]
}
...
1 | rest.description.person 的值映射到整个表示。 |
2 | rest.description.person.firstName 的值映射到 firstName 属性。 |
3 | rest.description.person.lastName 的值映射到 lastName 属性。 |
4 | rest.description.person.id 的值映射到 id 属性,该字段通常不显示。 |
5 | rest.description.person.address 的值映射到 address 属性。 |
提供这些属性设置会导致每个字段都有一个额外的 doc
属性。
Spring MVC(它是 Spring Data REST 应用的核心)支持 locale,这意味着你可以捆绑包含不同消息的多个属性文件。 |
JSON Schema
JSON Schema 是 Spring Data REST 支持的另一种元数据形式。根据其网站所述,JSON Schema 具有以下优点
-
描述你现有数据格式
-
清晰、人类可读和机器可读的文档
-
完整的结构验证,对于自动化测试和验证客户端提交的数据很有用
如上一节所示,你可以通过从根 URI 导航到 profile
链接来获取此数据。
{
"_links" : {
"self" : {
"href" : "http://localhost:8080/profile"
},
"persons" : {
"href" : "http://localhost:8080/profile/persons"
},
"addresses" : {
"href" : "http://localhost:8080/profile/addresses"
}
}
}
这些链接与前面所示的相同。要检索 JSON Schema,你可以使用以下 Accept
头调用它们:application/schema+json
。
在这种情况下,如果你运行 curl -H 'Accept:application/schema+json' localhost:8080/profile/persons
,你会看到类似以下内容的输出
{
"title" : "org.springframework.data.rest.webmvc.jpa.Person", (1)
"properties" : { (2)
"firstName" : {
"readOnly" : false,
"type" : "string"
},
"lastName" : {
"readOnly" : false,
"type" : "string"
},
"siblings" : {
"readOnly" : false,
"type" : "string",
"format" : "uri"
},
"created" : {
"readOnly" : false,
"type" : "string",
"format" : "date-time"
},
"father" : {
"readOnly" : false,
"type" : "string",
"format" : "uri"
},
"weight" : {
"readOnly" : false,
"type" : "integer"
},
"height" : {
"readOnly" : false,
"type" : "integer"
}
},
"descriptors" : { },
"type" : "object",
"$schema" : "https://json-schema.fullstack.org.cn/draft-04/schema#"
}
1 | 导出的类型 |
2 | 属性列表 |
如果你的资源链接到其他资源,会有更多详细信息。
当你查看一个集合资源时,你还可以在 _links
集合中找到一个 profile
链接,如下例所示
{
"_links" : {
"self" : {
"href" : "http://localhost:8080/persons" (1)
},
... other links ...
"profile" : {
"href" : "http://localhost:8080/profile/persons" (2)
}
},
...
}
1 | 此 HAL 文档表示 Person 集合。 |
2 | 它有一个指向相同 URI 的 profile 链接,用于元数据。 |