Copyright © 2026 World Wide Web Consortium. W3C® liability, trademark and permissive document license rules apply.
Linked Web Storage Protocol 规范旨在以互操作的方式,为应用程序提供对外部存储数据的安全且经许可的访问。
本节描述本文档在发布时的状态。当前 W3C 出版物列表以及本技术报告的最新修订版可在 W3C 标准和草案 索引中找到。
这是一项非官方提案。
本文档由 Linked Web Storage 工作 组作为 工作草案发布,并使用 推荐标准 轨道。
作为 工作草案发布并不意味着 W3C 及其成员认可该文档。
这是一份草案文档,可能随时被其他文档更新、替换或废弃。 除了作为正在进行中的工作外,不宜引用本文档。
本文档由一个按照 W3C 专利 政策运作的组制作。 W3C 维护着一份 与该组交付成果相关的任何专利披露的公开列表; 该页面还包含 披露专利的说明。实际 知晓某项专利且认为该专利包含 必要权利要求的个人 必须按照 W3C 专利政策第 6 节 披露相关信息。
本文档受 2025年8月18日 W3C 流程文档管辖。
本节为非规范性内容。
本规范定义了关联 Web 存储(LWS)协议,该协议使客户端应用程序能够根据代理的身份和权限访问和管理 Web 资源。
通过标准化资源服务器管理关联资源层次结构并提供其访问权限的方式,该 协议使用户能够使用不同的客户端应用程序与相同的已存储数据进行交互。这些 关联资源通过容器和包含关系进行定义,前者描述事物的 所在位置,后者以及元数据描述资源之间的相互关系。
该协议定义了对这些资源执行创建、读取、更新和删除的标准操作。每个 资源均由 URI 标识。客户端从根容器开始遍历资源层次结构, 并通过服务器响应中提供的链接发现其中包含的资源及其关系。
代理的身份通过身份提供方进行确认,该身份提供方可以位于关联 Web 存储 服务器之外。这种松耦合意味着服务器不一定直接管理凭据;相反,它 可以接收并验证由受信任身份 提供方作为令牌签发的已签名身份验证凭据。因此,用户可以向符合要求的服务器提供现有身份,而不必总是 仅为该服务器创建新账户或使用已有账户。例如,LWS 使用 OpenID Connect、SAML 2.0 和自签名受控标识符(CID)规范中定义的用户身份验证。
授权用于确定发出请求的代理是否有权访问资源或对其执行 操作。符合要求的 LWS 服务器为每个资源指定一个资源管理器,由其确定 资源是私有的(即仅可供其所有者使用)、受限的(即可供一组指定的 用户使用),还是公开的(即可供任何用户使用)。服务器在处理每个客户端请求时,会强制执行 资源管理器作出的访问决策。
本规范旨在供以下人员使用:
不同的 LWS 服务器实例各自维护其自身的关联资源层次结构。代理可以使用 相同的标识跨服务器进行遍历,而其对每台服务器资源的访问权限由该服务器的 资源管理器授予的权限决定。不能保证每个 LWS 服务器实例 都支持所有可能的代理标识符;任何给定的 LWS 服务器实例都可能要求代理 通过特定的身份提供方标识自身,以获得对该服务器 实例所管理资源的访问权限。
LWS 协议定义了标准交互,某一方可通过这些交互向某些 代理提供某些资源。
资源管理者可以将被服务资源 保持为私有,可以使其对任何人公开可用,或者可以将其可见性限制到一组受约束的 请求代理。
除标记为非规范性的章节外,本规范中的所有创作指南、图表、示例和注释均为非规范性内容。 本规范中的其他所有内容均为规范性内容。
本文档中的关键词 MAY、MUST、MUST NOT、OPTIONAL、RECOMMENDED、REQUIRED、SHOULD 和 SHOULD NOT 应按 BCP 14 [RFC2119] [RFC8174] 中的描述解释,且仅当它们像这里所示以全 大写形式出现时才如此解释。
LWS 服务器是一个 HTTP 服务器 [rfc9112],它遵守 本规范中所有相关的“MUST”陈述。具体而言, 必须遵守本文档9. 操作中的相关规范性“MUST”陈述。
LWS 客户端是一个 HTTP 客户端 [rfc9112],它遵守本规范中所有相关的“MUST”陈述。具体而言,必须遵守本文档9. 操作中的相关规范性 “MUST”陈述。
以下图表是与平台无关的模型, 用于指导实现。
“授权服务器”和“客户端”这两个术语由 OAuth 2.0 授权框架 [RFC6749] 定义。
“最终用户”和“签发者”这两个术语由 OpenID Connect Core 1.0 [OPENID-CONNECT-CORE] 定义。
本规范定义了以下术语:
conformsTo 属性中引用一个或多个访问配置,表示对它们的支持。
https://www.w3.org/ns/lws#StorageResource 标识。
本节定义了一种机制,用于标识与链接式 Web 存储服务器交互的代理和最终用户。 本规范并不强制要求身份验证凭证采用特定 格式,但它确实描述了如何将现有身份系统 与链接式 Web 存储授权框架结合使用。
本节描述的数据模型概述了身份验证凭证的任何 具体序列化所需满足的要求。
身份验证凭证MUST 包含关于主体的防篡改声明,包括:
对身份验证凭证的验证需要凭证的 验证者和颁发者之间存在信任关系。此信任关系MAY 通过 带外机制建立。用于在验证者和颁发者之间建立信任的任何其他机制 均在特定的身份验证套件中概述。
身份验证凭证MUST 被签名。RECOMMENDED 使用非对称密码学 进行签名。
每个身份验证套件MUST 关联一个令牌类型 URI。身份验证套件SHOULD 使用 IANA “OAuth URI”注册表中定义的 URI。
一个存储描述资源提供 客户端可使用的信息 ,供其与存储交互时使用,包括功能和 服务端点的描述。
一个 存储描述资源是 W3C 受控标识符 文档 [CID-1.0] 的一种特化形式。 它符合 [CID-1.0] 定义的数据模型,并扩展了 LWS 特有的属性。
id - id 属性是必需的。其值必须是规范 URI
,属于该存储。
根据 [CID-1.0],此 URI 也是规范
URL,属于该受控标识符文档。
type - type 属性是必需的。其值必须是一个字符串,其值
等于
Storage,或是一个字符串集合,其中包含等于 Storage 的项
(区分大小写)。
capability - capability 属性是可选的。其值必须是一组
功能,其中每项功能由包含以下属性的映射描述。
可以存在其他属性。
id - id 属性是可选的。如果存在,其值必须
是 URI。type - type 属性是必需的。其值必须是字符串
或字符串集合。service - service 属性是必需的。其值必须是一组服务,
其中每项服务由包含以下属性的映射描述。其他
属性可以存在。
该集合必须包含一项服务,其 type 等于
StorageRoot。
id - id 属性是可选的。如果存在,其值必须
是 URI。type - type 属性是必需的。其值必须是字符串
或字符串集合。serviceEndpoint - serviceEndpoint
属性是必需的。其值必须
是 URI。{
"@context": [
"https://www.w3.org/ns/cid/v1",
"https://www.w3.org/ns/lws/v1"
],
"id": "https://storage.example/",
"type": "Storage",
"service": [{
"type": "StorageRoot",
"serviceEndpoint": "https://storage.example/root/"
}]
}
对以存储资源为目标的 GET 和 HEAD 请求的所有响应必须包含一个
Link 标头,其目标是该存储的规范 URI,并包含一个
关系
(rel) 参数,其值等于 https://www.w3.org/ns/lws#storage。
对存储
URI 的请求必须返回一个文档,该文档
符合
存储描述资源数据模型,且
媒体类型为 application/lws+cid,
除非内容协商要求使用其他格式。
一个 存储描述资源 必须包含一个
StorageRoot 服务,其
serviceEndpoint 是存储根容器的 URI。
与存储关联的附加服务也可以存在。
这些附加服务的 API 定义不在本规范的范围之内。
一份 存储描述 必须能够以媒体类型
application/lws+cid 序列化。
@context 属性必须是一个数组,其起始项依次为 URI
https://www.w3.org/ns/cid/v1
和 https://www.w3.org/ns/lws/v1。
其他表示形式可以通过内容协商提供。
{
"@context": [
"https://www.w3.org/ns/cid/v1",
"https://www.w3.org/ns/lws/v1"
],
"id": "https://storage.example/",
"type": "Storage",
"capability": [{
"type": "https://feature.example/PatchSupport",
"format": {
"text/turtle": ["application/sparql-update"],
"application/n-triples": ["application/sparql-update"],
"application/linkset+json": ["application/json-patch+json", "application/merge-patch+json"]
}
}, {
"type": "https://feature.example/ResumableUploads"
}, {
"type": "https://feature.example/ContentNegotiation",
"source": "application/ld+json",
"target": ["text/turtle", "application/n-triples"]
}, {
"type": "https://feature.example/ContentNegotiation",
"source": "image/jpeg",
"target": ["image/png"]
}],
"service": [{
"type": "StorageRoot",
"serviceEndpoint": "https://storage.example/root/"
}, {
"type": "NotificationService",
"serviceEndpoint": "https://storage.example/notification/api",
"subscriptionType": ["WebhookSubscription"]
}, {
"type": "TypeIndexService",
"serviceEndpoint": "https://storage.example/types/index"
}, {
"type": "TypeSearchService",
"serviceEndpoint": "https://storage.example/types/search"
}, {
"type": "DataSharingService",
"serviceEndpoint": "https://storage.example/sharing/api"
}]
}
关联 Web 存储将资源组织到容器中。一个容器是一种 特殊资源,它持有对其他资源的引用,这些资源称为其成员。容器充当 组织单元,类似于目录或集合,使客户端能够对资源进行分组、发现、 和导航。容器维护对其成员资源的引用, 这些成员可以同时包括非容器资源和其他容器资源,从而形成 层次结构。通常,容器自身仅包含极少的内容, 除了元数据或其成员的枚举之外;其主要作用是聚合和组织 下级资源。该存储系统的根被指定为一个容器,充当 没有上级父节点的顶层组织单元。容器表示的结构定义于8.1 容器 表示;其媒体类型和分页要求定义于12. LWS 媒体类型。 存储可以充当根容器,从而允许直接写入。
每个 LWS 存储都有一个存储 根,充当顶层组织单元。该存储根没有父节点,并充当 存储 层次结构的入口点。
LWS 中的资源分为以下两类:
资源与其父容器之间的包含关系通过 rel="up" 链接
关系表示。对于任何
非根资源上的 GET 和 HEAD 请求,服务器MUST 在响应中包含一个
Link 标头,其中 rel="up" 指向父容器。
Link: </alice/notes/>; rel="up"
容器的成员在其表示中使用
items 属性列出。服务器管理此列表;客户端不能直接修改它。成员关系
变更作为资源创建和删除的副作用发生。
服务器MUST 始终维护包含关系完整性:
资源由 URI 标识。资源的 URI 与其在包含关系层次结构中的位置无关。服务器在创建资源时分配 URI, 并且可以采用客户端提示,但客户端不应该假定 URI 结构反映了包含关系。
包含关系通过元数据表示
(rel="up" 链接和容器
表示中的 items 属性),而不是通过 URI 路径结构表示。这种分离允许服务器在 URI
分配方面具有灵活性,同时维护一个定义良好的组织模型。
当客户端检索容器时,服务器会返回一个结构化的容器表示,用于描述该容器及其内容。本节定义容器表示的必需和 可选属性。
容器表示MUST 包含以下属性:
items 数组中的每个条目描述容器中包含的一个资源。被包含资源描述MUST 包含:
id:被包含资源的 URI。type:资源的类型。MUST 是
"DataResource" 或 "Container",或是至少包含
这两个字符串之一的数组。服务器MAY 包含其他用户定义类型作为
URI(例如 ["DataResource", "http://example.org/customType"])。
被包含资源描述SHOULD 包含:
format:资源的媒体类型(例如
"text/plain"、"image/jpeg")。对于 DataResources,
MUST 存在此项。
size:资源的大小,以字节为单位,以整数表示。
modified:资源最后修改的日期和时间,
表示为 ISO 8601 日期时间字符串。以下示例显示位于 /alice/notes/ 的一个容器,其中包含两个
资源:
{
"@context": "https://www.w3.org/ns/lws/v1",
"id": "/alice/notes/",
"type": "Container",
"totalItems": 2,
"items": [
{
"type": "DataResource",
"id": "/alice/notes/shoppinglist.txt",
"format": "text/plain",
"size": 47,
"modified": "2025-11-24T12:00:00Z"
},
{
"type": ["DataResource", "http://example.org/customType"],
"id": "/alice/notes/todo.json",
"format": "application/json",
"size": 2048,
"modified": "2025-11-24T13:00:00Z"
}
]
}
本节定义了链接式 Web 存储(LWS)服务器必须支持的四种核心操作。这些操作以与传输方式无关的方式操纵资源和容器, 重点关注语义而非实现细节。每个操作都指定了输入、预期行为和可能的响应。响应包括成功指示、资源 表示(如适用)以及错误条件。实现必须 以原子且一致的方式处理这些操作,这意味着每个操作要么完全成功,要么 失败且不会产生部分副作用。发生错误时,响应应当提供 足够的详细信息,使代理能够了解问题,同时不会泄露敏感信息。
对于每种核心操作(创建、读取、更新、删除),我们描述了要使用的 HTTP 方法、必需的
标头或其他特殊注意事项(包括内容协商、容器列表的分页
以及条件请求),以及服务器应执行的操作和返回的内容。使用标准 HTTP 状态码来
指示结果,并为配额超出等场景提供额外的映射
(507 Insufficient Storage)或前置条件失败(412 Precondition Failed)。该
绑定力求遵循 HTTP/1.1 和相关 RFC(例如 [RFC7231] 关于 HTTP
语义、[RFC7233] 关于范围
请求、[RFC5789] 关于 PATCH、[RFC8288] 关于 Web 链接,
以及 [RFC9264] 关于链接
集),从而使其能够自然地与 Web 标准集成。通过诸如 Link 标头以及 401 响应中的
WWW-Authenticate 标头等机制来强调可发现性,
避免硬编码 URI 位置。所有操作都要求进行元数据集成,以确保原子性,并
对服务器管理的属性和用户管理的属性使用链接集。
注:与本规范中的所有示例一样,本节给出的示例(HTTP 请求 和响应片段)均为非规范性内容,旨在说明典型用法。实际 要求在描述性文本和表格中陈述。此外,虽然此绑定涵盖 HTTP(作为 初始目标协议),但原则上,LWS 操作将来可以绑定到其他协议。 容器表示的媒体类型要求定义于 12. LWS 媒体类型。
本节定义将元数据与存储 资源关联的模型。LWS 元数据系统基于 Web Linking [RFC8288] 的原则,该原则允许服务器使用带类型的链接描述 资源之间的关系。元数据增强可发现性,支持自描述 API, 并与资源操作以及容器层次结构保持一致。
元数据模型 LWS 中的所有元数据都表示为从资源(链接上下文)发出的带类型链接集合。 每个链接由以下部分组成:
元数据区分资源及其表示形式,在适用情况下允许存在多种媒体类型。 对于数据资源,元数据包括表示形式,每个表示形式具有 格式以及可选的 sizeInBytes。对于容器和数据资源,我们认为指向其父容器资源的链接属于该资源元数据的一部分。
链接集资源 对于存储中的每个资源,服务器 必须根据 [RFC9264], 将元数据链接作为独立资源提供。
rel="linkset" 的 Link 标头发现。
application/linkset+json 的形式提供。
发现元数据 客户端主要通过 GET 或 HEAD 请求响应中的 Link 标头发现元数据。
rel="https://www.w3.org/ns/lws#storage" 的 Link 标头。
rel="up" 且指向父容器的
Link 标头。
https://www.w3.org/ns/lws#PreferLinkRelations 一起使用,以包含或省略特定关系。
元数据类型
| 类别 | 描述 |
|---|---|
| 系统管理 | 由服务器维护;只读。包括 linkset、type、
format、size、modified。
|
| 核心元数据 | 由客户端管理(受服务器限制)。包括 up、
items、title、creator。
|
| 用户定义 | 由用户创建的自定义词汇表和索引。 |
可修改性注意事项 核心元数据可以由客户端修改。为确保互操作性,服务器 必须使用标准 HTTP 标头公布其能力:
方法发现:服务器必须通过 Allow 标头公布对链接集资源执行 GET 和 PATCH 操作的支持。
补丁格式发现:服务器 必须 通过 Accept-Patch
标头公告对 JSON Patch [RFC6902] 的支持:
Accept-Patch: application/json-patch+json。
可选方法:服务器可以支持 PUT 或其他补丁格式; 如果支持,则这些方法和格式必须分别包含在 Allow 和 Accept-Patch 标头中。
[!重要] 除非资源标头中公布了对 PUT 或特定补丁格式的支持,否则客户端不应该假定支持它们,并且必须妥善处理 405 方法 不允许或 415 不支持的媒体类型响应。
管理元数据
元数据通过与资源关联的链接集资源
URI 进行交互来管理。服务器必须在对 GET 和 HEAD 请求的响应中包含 [RFC9110] 中定义的
ETag 标头。
部分更新(PATCH):这是管理元数据的主要机制。服务器必须支持使用
application/json-patch+json 的 PATCH。
替换(PUT):如果 Allow 标头中公布了 PUT,客户端可以 替换整个链接集。如果服务器不支持 PUT,则它必须 以 405 方法不允许拒绝该请求。
限制:服务器可以限制对特定链接(例如
up 或 items)的修改,以维持系统完整性。
生命周期:元数据的生命周期与所描述的资源绑定;删除资源必须导致自动移除其关联链接集资源中的元数据。
创建资源操作将一个新的被服务资源添加到现有容器中。此 操作同时处理数据资源和子容器的创建。
输入:
行为:
可能的响应:
使用 POST 向目标容器 URI 创建新资源,由服务器分配
最终标识符。客户端可以通过在 POST 请求中包含一个或多个
Link 标头,按照 [RFC8288] 中 Web 链接的语法,为新资源提供初始的用户管理元数据。
服务器管理的元数据必须
由服务器在创建时自动生成,并且不得
被客户端提供的链接覆盖。
成功后,服务器必须返回 201 状态码,并在
Location 标头中提供新 URI。服务器必须包含用于
关键服务器管理元数据的 Link 标头,其中包括指向父容器的链接
(rel="up"),以及指向所创建资源专用链接集资源的链接
(rel="linkset"; type="application/linkset+json")。其他链接应该包括
rel="type"(指示
https://www.w3.org/ns/lws#Container 或
https://www.w3.org/ns/lws#DataResource)。响应主体可以为空,也可以
包含资源的最小表示。所有元数据的创建和链接必须与资源创建以原子方式进行,以保持一致性。
POST(向容器 URI) – 使用服务器分配的名称创建: 使用 POST 在现有容器内添加新资源。服务器为资源分配 标识符。客户端按如下方式指示要创建的资源类型:
rel="type" 的 Link 标头,
使其指向容器类型:
Link: <https://www.w3.org/ns/lws#Container>; rel="type"。
Content-Type 标头。示例(使用 POST 创建新的数据资源):
POST /alice/notes/ HTTP/1.1
Host: example.com
Authorization: Bearer <token>
Content-Type: text/plain
Content-Length: 47
milk
eggs
bread
butter
apples
orange juice
在此示例中,客户端正在向容器 /alice/notes/ 发送 POST 请求。它
提供 text/plain 内容(一份购物清单),并为新资源建议名称
shoppinglist.txt。如果 /alice/notes/ 存在且客户端
已获得授权,服务器将创建新的数据资源,并将其添加到该容器的成员中。
示例(对 POST 的响应 — 数据资源):
HTTP/1.1 201 Created
Location: /alice/notes/shoppinglist.txt
Content-Type: text/plain; charset=UTF-8
Link: </alice/notes/shoppinglist.txt.meta>; rel="linkset"; type="application/linkset+json"
Link: </alice/notes/>; rel="up"
Link: <https://www.w3.org/ns/lws#DataResource>; rel="type"
Content-Length: 0
成功后,返回 201 Created,并在 Location 标头中提供新 URI。响应主体可以
为空,也可以是最小表示。
如果目标容器 /alice/notes/ 不存在,服务器
必须返回 404 错误状态,除非其他状态码更为适当。
创建容器:要创建新的容器,客户端使用 POST 向现有父容器发送请求,并使用 Link 标头指示
容器类型。例如:
POST /alice/ HTTP/1.1
Host: example.com
Authorization: Bearer <token>
Content-Length: 0
Link: <https://www.w3.org/ns/lws#Container>; rel="type"
示例(对 POST 的响应 — 容器):
HTTP/1.1 201 Created
Location: /alice/notes/
Link: </alice/notes/.meta>; rel="linkset"; type="application/linkset+json"
Link: </alice/>; rel="up"
Link: <https://www.w3.org/ns/lws#Container>; rel="type"
Content-Length: 0
这将在 /alice/notes/ 创建新的容器,其中由服务器生成的
元数据包括值为 https://www.w3.org/ns/lws#Container 的 rel="type"。
有关创建操作的其他说明(HTTP 绑定):
rel="https://www.w3.org/ns/lws#storage" 的 Link 标头,以引导没有
硬编码 URI 的客户端。
管理和检索元数据(与创建相关):
尽管元数据主要通过读取操作检索,但它是在创建过程中生成的。客户端可以
在创建后立即对新资源 URI 使用 GET 或 HEAD 来检索元数据。客户端可以使用
Prefer 标头请求包含特定的元数据链接(通过关系类型)和
属性。
检索现有资源的表示或容器的列表。
读取资源操作使用 HTTP GET 请求来请求资源表示(对于仅请求
标头的请求则使用 HEAD)。其行为取决于目标 URL 是容器还是非容器资源(数据资源)。服务器必须
通过元数据区分资源类型。所有响应必须与
第 8.1 节中定义的元数据集成,包括用于 rel="linkset"、
rel="up" 和 rel="type" 等关键关系的 Link 标头。服务器必须在读取期间确保资源状态与其元数据之间的原子性。
GET(非容器资源) – 检索资源的内容:
向资源 URI 发送 GET 以获取完整内容(如果已获授权)。以 200 OK 响应,响应主体包含
数据,并且 Content-Type 与存储的媒体类型匹配。服务器必须按照 [RFC7233]
支持用于部分
检索的范围请求。响应必须包含 [RFC9110] 中定义的 ETag 标头。
示例(GET 文件):
GET /alice/notes/shoppinglist.txt HTTP/1.1
Authorization: Bearer <token>
Accept: text/plain
这会请求 /alice/notes/shoppinglist.txt 的内容,并表明客户端希望
以文本形式获取它。假设该资源存在、属于文本,并且客户端具有访问权限:
HTTP/1.1 200 OK
Content-Type: text/plain; charset=UTF-8
Content-Length: 34
ETag: "abc123456"
Link: </alice/notes/shoppinglist.txt.meta>; rel="linkset"; type="application/linkset+json"
Link: </alice/notes/>; rel="up"
Link: <https://www.w3.org/ns/lws#DataResource>; rel="type"
milk
cheese
bread
guacamole
soda
chocolate bars
hash
eggs
服务器返回了文本内容(总计 34 字节,如 Content-Length 所示)。
该内容与文件中存储的数据完全一致。ETag: "abc123456" 是用于
缓存和条件请求的验证器。响应包含用于
元数据可发现性的 Link 标头,其中包含 up 和 type 等必需字段。
GET(容器资源) – 列出容器的内容: 当目标 URI 对应于容器(通过元数据类型确定)时,GET 请求返回 容器成员的列表。响应主体是容器表示,如容器表示一节中所定义,并使用 LWS 容器媒体 类型。该列表包括每个成员的元数据:资源标识符(必须)、类型(必须)、媒体类型(对于 DataResources 必须)、大小(应当)以及 修改时间戳(应当)。
示例(GET 容器):
GET /alice/notes/ HTTP/1.1
Authorization: Bearer <token>
Accept: application/lws+json
假设容器存在且客户端具有访问权限:
HTTP/1.1 200 OK
Content-Type: application/lws+json
ETag: "container-etag-789"
Link: </alice/notes/.meta>; rel="linkset"; type="application/linkset+json"
Link: </alice/>; rel="up"
Link: <https://www.w3.org/ns/lws#Container>; rel="type"
{
"@context": "https://www.w3.org/ns/lws/v1",
"id": "/alice/notes/",
"type": "Container",
"totalItems": 2,
"items": [
{
"type": "DataResource",
"id": "/alice/notes/shoppinglist.txt",
"format": "text/plain",
"size": 47,
"modified": "2025-11-24T12:00:00Z"
},
{
"type": ["DataResource", "http://example.org/customType"],
"id": "/alice/notes/todo.json",
"format": "application/json",
"size": 2048,
"modified": "2025-11-24T13:00:00Z"
}
]
}
在此示例中,/alice/notes/ 是一个容器。响应使用带有
LWS 上下文的 JSON-LD,列出具有必需元数据的成员。每个项目都包含其 type、
id、format、size 和 modified 时间戳,并作为扁平
属性。
在所有情况下,服务器必须在响应
标头中包含以下元数据:[RFC9110] 中定义的 ETag 标头,以及带有
rel="linkset"、rel="up" 和 rel="type" 的 Link
标头,用于表明它是一个
容器。
HEAD(任何资源或容器) – 仅标头/元数据:
LWS 服务器必须对容器和非容器都支持 [RFC9110] 中的 HEAD,并返回与 GET
相同的标头(包括 Content-Type、用于元数据的 Link),但不返回响应主体。这使得
无需传输内容即可检索元数据。
缓存和条件请求:LWS 利用 HTTP 缓存语义。服务器应当支持 [RFC9110] 中定义的条件请求,
包括实体标签(ETags)和基于日期的验证器(例如
If-Modified-Since 标头)等机制。如果资源或容器列表
未发生更改,则以 304 Not Modified 响应,以避免冗余传输。
可发现性与授权:为增强可发现性,
401 Unauthorized 响应包含一个 WWW-Authenticate 标头,其中带有
resource_metadata 参数,如授权服务器发现中所述,以便客户端能够
在不硬编码 URI 的情况下定位授权服务器。
通过完全替换或部分补丁来修改现有 [served resource] 的状态。
更新资源通过 PUT 请求(替换整个资源)或 PATCH 请求(应用
部分修改)来修改现有已提供
资源的内容。客户端必须具有对资源 URL 的写入权限才能执行这些
操作。
注:本节描述如何更新资源的主要内容。要更新其元数据,请参阅第
9.3.2 节。
LWS 服务器必须将对资源 URI 的 PUT 和 PATCH 请求作为
仅对资源内容的修改处理,默认情况下不会影响关联的链接集资源。为了可选地在单个原子操作中同时更新内容和
元数据,客户端可以在向资源 URI 发出的 PUT/PATCH 请求中包含 Link
标头,并指定首选项
'Prefer: set-linkset'(如 [RFC7240] 中所定义)。在这种情况下,
服务器必须将提供的 Link 标头解释为对链接集的替换
(对于 PUT)或部分更新(对于 PATCH),同时应用内容更改。
此行为对于服务器而言是可选的,但如果支持,则必须通过 Prefer
标头显式调用,以防止
无意覆盖元数据。不支持组合更新的服务器必须忽略该首选项或以 501 Not Implemented 响应。
PUT(替换整个资源) – 向资源 URI 发送 PUT,请求主体中包含新的完整内容,并带有匹配的
Content-Type(通常与现有类型一致)。对于现有资源,PUT 是幂等的。
客户端应当使用 [RFC9110] 中定义的条件请求,以避免覆盖并发更改。
示例(使用 PUT 更新资源):
PUT /alice/personalinfo.json HTTP/1.1
Authorization: Bearer <token>
Content-Type: application/json
If-Match: "abc123456"
{
"name": "Alice",
"age": 30,
"city": "New London",
"state": "Connecticut"
}
在此示例中,客户端正在更新
/alice/personalinfo.json 处的现有 JSON 资源。它包含一个 If-Match 标头,其中带有
从先前的 GET 或 HEAD 请求中收到的实体标签(ETag)。服务器会将其
与当前实体标签(ETag)进行比较;如果匹配,则继续使用
提供的 JSON 替换内容。如果不匹配,服务器会拒绝更新(因为该资源在此期间已被
其他人更改)。
成功响应:如果更新成功,服务器可以使用 200 OK 响应,并且
可以包含更新后的表示或某种确认信息(例如新内容或其中的一部分)。
或者,服务器可以使用 204 No Content 响应,以表示成功但没有响应主体
(尤其常见于无需传达其他信息的情况)。例如:
HTTP/1.1 204 No Content
If-Match 前置条件不匹配
(并发修改),服务器以 412 Precondition Failed 响应。如果
资源不存在,则作为更新用途的 PUT 将导致 404 Not Found。如果
客户端未获授权,则返回 403 Forbidden(如果未提供有效
凭据,则返回 401 Unauthorized)。如果请求负载无效,则返回 400 Bad Request。PATCH(部分更新) – HTTP PATCH 方法 [RFC5789]
允许客户端指定对资源的部分修改,而不是发送全部新的
内容。这对于大型资源很有用,因为如果
仅有一小部分发生变化,发送整个内容会很低效;或者对于并发编辑,你希望应用特定更改时也很有用。LWS
服务器 必须 至少支持 JSON Patch
(application/json-patch+json),如 [RFC6902] 中所定义。
更新资源元数据(对 Linkset 使用 HTTP PUT / PATCH) 资源的元数据通过修改其对应的链接集资源来更新, 该资源通过带有 rel="linkset" 的 Link 标头发现。 完全替换(PUT):向链接集资源 URI 发出 PUT 请求,并在主体中包含完整的 链接集文档,以替换该资源的所有元数据。 部分更新(PATCH):向链接集资源 URI 发出 PATCH 请求,以添加、删除或 修改特定链接。
元数据的条件请求
由于资源的元数据可以由多个参与者修改,因此防止并发覆盖非常
重要。对于链接集资源上的 PUT 和 PATCH 操作,服务器和客户端应当使用
[RFC9110]
中定义的条件请求。当服务器收到前置条件
失败的条件请求时,它必须以
412 Precondition Failed 状态码拒绝该请求。
示例(使用 PUT 替换链接集):
客户端首先获取链接集。
GET /alice/personalinfo.json.meta HTTP/1.1
Authorization: Bearer <token>
Accept: application/linkset+json
HTTP/1.1 200 OK
Content-Type: application/linkset+json
{
"linkset": [
{
"anchor": "/alice/personalinfo.json",
"describedby": [ { "href": "/schemas/personal-info.json" } ]
}
]
}
客户端现在希望添加许可证。它构造一个新的完整链接集文档,并发送 PUT 请求。
PUT /alice/personalinfo.json.meta HTTP/1.1
Authorization: Bearer <token>
Content-Type: application/linkset+json
{
"linkset": [
{
"anchor": "/alice/personalinfo.json",
"describedby": [ { "href": "/schemas/personal-info.json" } ],
"license": [ { "href": "https://creativecommons.org/licenses/by/4.0/" } ]
}
]
}
如果成功,服务器将返回成功响应。
HTTP/1.1 204 No Content
更新规则摘要 如果只想更改资源的内容 → 对资源本身执行 PUT/PATCH。 如果只想更改资源的链接(元数据)→ 对资源关联的链接集资源执行 PUT/PATCH。 如果想同时更改内容和链接 → 对资源本身执行 PUT/PATCH,并包含相应的 Link 标头以及 'Prefer: set-linkset'。默认情况下不会同时设置两者。
永久删除资源及其关联的元数据。
删除资源操作使用 HTTP DELETE 方法实现,如上面的抽象 操作中所定义。本节规定输入、行为和响应的 HTTP 绑定。
DELETE 请求以要删除的资源或容器的 URI 为目标。客户端可以包含带有 ETag 的
If-Match 标头,以进行并发
检查。
删除与包含关系:
删除资源时,服务器必须以原子方式将其从
父容器的 items 列表中删除。父容器的
totalItems 计数应当相应更新。
对于非容器资源,服务器删除资源内容、其关联的元数据(链接集资源),以及父包含关系引用 中的容器。
对于容器资源,服务器默认执行非递归
删除。如果容器非空且未请求递归删除,则服务器
必须以 409 Conflict 拒绝请求。服务器可以
支持递归删除正在删除的容器中包含的所有资源。
客户端必须使用 Depth: infinity 标头来请求
递归删除,如 [RFC4918] 中所定义。
成功时,服务器必须以 204 No Content 响应。服务器应当支持条件请求,如 [RFC9110] 中所定义。
如果客户端缺少授权,服务器必须返回 403 Forbidden(如果 已知客户端的身份但权限不足)或 401 Unauthorized(如果未提供有效 身份验证)。如果暴露资源是否存在会带来安全风险, 服务器可以改为返回 404 Not Found。
示例(DELETE 非容器资源):
DELETE /alice/notes/shoppinglist.txt HTTP/1.1
Authorization: Bearer <token>
If-Match: "abc123456"
假定 ETag 匹配且客户端已获授权,服务器会以原子方式删除资源、其元数据,
并将其从父容器 /alice/notes/ 中移除:
HTTP/1.1 204 No Content
示例(DELETE 非空容器且不递归):
DELETE /alice/notes/ HTTP/1.1
Authorization: Bearer <token>
假定 /alice/notes/ 包含资源,服务器会拒绝删除:
HTTP/1.1 409 Conflict
Content-Type: text/plain
Cannot delete container /alice/notes/ - container is not empty.
示例(如果支持,递归 DELETE 容器):
DELETE /alice/notes/ HTTP/1.1
Authorization: Bearer <token>
Depth: infinity
假定服务器支持递归且客户端对所有内容都有权限,服务器会以原子方式 删除该容器及其后代:
HTTP/1.1 204 No Content
此表将通用 LWS 响应(来自 第 8 节)映射到 HTTP 状态码和负载,以保持一致性,并涵盖诸如 分页、条件请求、配额约束和元数据集成等特定场景:
| LWS 响应 | HTTP 状态码 | HTTP 负载 |
|---|---|---|
| 成功(读取或更新,并返回 数据) | 200 OK |
响应主体中的资源
表示(用于 GET,或者在 PUT/PATCH 返回内容时),以及
相关标头(Content-Type、用于元数据的 Link,例如
rel="linkset" 或 rel="up")。对于容器列表,应包括
带有规范性上下文和成员元数据(ID、类型、大小、时间戳等)的 JSON-LD。
|
| 已创建(新资源) | 201 Created |
通常没有响应主体(或者仅包含新资源的最小表示)。
Location 标头设置为新资源的 URI。用于
服务器管理元数据的 Link 标头。
|
| 已删除(无内容可返回) | 204 No Content |
没有响应主体。表示资源已被删除,或者请求已成功且
没有其他内容需要返回。服务器可以对
永久删除使用 410 Gone。 |
| 错误请求(输入或约束无效) | 400 Bad Request |
说明错误原因的详细信息。服务器应当使用
[RFC9457] 中定义的
标准格式来提供结构化错误响应,例如包含 "type"、
"title"、"status"、"detail" 和
"instance" 等字段的 JSON 对象。
|
本节定义了一种机制,使客户端能够及时接收由关联 Web 存储服务器管理的 资源发生变更时的更新。本规范定义了一种通用的 通知数据模型和订阅机制;具体的递送通道在 单独的通知套件规范中描述。
存储可以支持通知。支持通知的存储必须
在其存储描述
资源的 service 数组中公布一个 type 等于 NotificationService 的服务对象。
服务对象必须包含一个 serviceEndpoint 属性,其
值
是订阅端点的 URI。服务对象必须包含一个
subscriptionType 属性,其值是一个字符串数组,每个字符串
标识一种受支持的订阅类型。
服务对象上可以存在其他属性。
{
"@context": ["https://www.w3.org/ns/lws/v1"],
"id": "https://storage.example/",
"type": "Storage",
"service": [{
"type": "NotificationService",
"serviceEndpoint": "https://notification.example/subscriptions",
"subscriptionType": [
"WebhookSubscription"
]
}]
}
服务器不必支持所有订阅类型。
subscriptionType 数组明确公布了哪些类型可用。
通知表示为一个类型为
Notification 的 JSON-LD 文档。信封携带有关递送
上下文的元数据,并封装一个描述该事件的 Activity Streams 2.0 [ACTIVITYSTREAMS-CORE]
[ACTIVITYSTREAMS-VOCABULARY]
活动。
通知信封具有以下属性:
type — 必需。其值必须
为字符串
Notification。
storage — 必需。标识与
该通知关联的 LWS 存储的 URI。
activity — 必需。一个 JSON 对象或一个 JSON
对象数组,
其中每个对象均符合 Activity Streams 2.0 数据模型,并描述已发生的事件。
参见10.2.2 活动属性。
{
"@context": [
"https://www.w3.org/ns/lws/v1",
"https://www.w3.org/ns/activitystreams"
],
"type": "Notification",
"storage": "https://storage.example/",
"activity": {
"id": "ec4dcc48-6581-4232-81c9-584525a98693",
"type": ["Delete"],
"object": {
"id": "https://storage.example/alice/notes/shopping.txt",
"type": ["DataResource"]
},
"origin": "https://storage.example/alice/notes/",
"published": "2026-03-26T10:30:00Z"
}
}
activity 属性中的每个活动对象必须符合
Activity Streams 2.0 数据模型,并且必须包含以下属性:
id — 必需。唯一标识该活动的字符串。
type — 必需。一个至少包含
一种
Activity Streams 2.0 活动类型的字符串数组。
object — 必需。一个标识该通知
所涉及资源的 JSON 对象。object 必须包含
以下
属性:
id — 必需。资源的 URI。
type — 必需。一个至少包含
一种
资源类型的字符串数组。本规范定义的值包括
Container 和 DataResource。还
可以包含其他类型值。
published — 必需。一个符合
[RFC3339] 的日期时间值,指示
该活动发生的时间。
以下属性为可选:
actor — 标识执行该操作的主体的 URI。
target — 标识资源被添加到的容器的 URI。
与 Create 活动一起使用。
origin — 标识资源从中移除的容器的 URI。
与 Delete 活动一起使用。
服务器 必须 支持以下 Activity Streams 2.0 活动类型, 以表示存储资源的更改:
Create — 在容器中创建了一个新资源。
target 字段指示该资源被添加到的容器。
Update — 修改了现有资源的内容或元数据。
Delete — 移除了一个资源。origin 字段
指示该资源从中移除的容器。
还可以支持其他活动类型。
服务器可以通过将活动对象数组作为 activity
属性的值,
将多个活动合并到单个通知
信封中。
{
"@context": [
"https://www.w3.org/ns/lws/v1",
"https://www.w3.org/ns/activitystreams"
],
"type": "Notification",
"storage": "https://storage.example/",
"activity": [
{
"id": "a1b2c3d4-5678-9abc-def0-1234567890ab",
"type": ["Create"],
"object": {
"id": "https://storage.example/alice/notes/meeting.txt",
"type": ["DataResource"]
},
"target": "https://storage.example/alice/notes/",
"published": "2026-03-26T10:30:00Z"
},
{
"id": "b2c3d4e5-6789-abcd-ef01-234567890abc",
"type": ["Update"],
"object": {
"id": "https://storage.example/alice/profile",
"type": ["DataResource"]
},
"published": "2026-03-26T10:30:01Z"
}
]
}
为接收通知,订阅者通过
向 NotificationService 的 serviceEndpoint
发送
经过身份验证的 POST 请求来创建订阅。
请求正文必须符合
application/lws+json 媒体类型。
订阅请求必须包含以下字段:
type — 必需。标识订阅
类型的字符串。
其值必须是
NotificationService 的 subscriptionType
数组中列出的类型之一。
topic — 必需。一个 URI 数组,用于标识
订阅中
包含的资源。
订阅请求可以包含具体 订阅类型所要求的其他字段。
POST /subscriptions HTTP/2
Host: notification.example
Authorization: Bearer <access-token>
Content-Type: application/lws+json
{
"@context": ["https://www.w3.org/ns/lws/v1"],
"type": "WebhookSubscription",
"topic": [
"https://storage.example/alice/notes/",
"https://storage.example/alice/profile"
],
"inbox": "https://receiver.example/hooks/lws"
}
对容器的订阅是递归的:订阅者会接收 关于容器本身以及传递包含 在该容器中的所有资源的通知。对数据资源的订阅仅适用于该 单个资源。
成功创建订阅请求的响应必须符合
application/lws+json 媒体类型,并且必须包含以下
字段:
type — 必需。一个与请求中的
订阅类型相等的字符串。
subscription — 必需。表示该
订阅的 URL。
每个通知套件必须
与一个订阅类型字符串相关联。
订阅类型字符串用于 NotificationService 的
subscriptionType 数组以及订阅请求的 type 字段中。
访问请求和访问授权提供了一种机制,使代理能够请求访问 资源,并使存储控制器能够在已定义的约束下授予访问权限。 用于请求和授予访问权限的端点充当一种专用收件箱。
代理不会 将访问授权作为授权令牌提交给服务器。 相反,访问授权充当一份记录,说明授权了什么、授权给谁, 以及受到哪些约束。当访问授权被创建或撤销(删除)时, 服务器负责调整任何底层访问策略以反映这一 变更。访问授权端点与特定策略 执行层之间的交互不在本规范的范围内。
本节定义的访问配置文件基于 开放数字权利语言(ODRL)中的概念,包括 操作、约束和受让方。其他配置文件可以使用不同的概念框架。
存储可以公布对访问请求和
访问授权的支持
,在其存储描述资源中。当此类支持
被公布时,服务
对象若描述了用于访问请求的端点,则必须具有一个
type,其值等于字符串 AccessRequestService,而
服务对象若描述了用于访问授权的端点,则必须
具有一个
type,其值等于字符串 AccessGrantService。服务
对象应该包含一个 conformsTo 属性,其值是一个
集合
,由一个或多个 URI 组成,用于标识访问配置文件,这些配置文件受该
端点支持。可以包含其他属性:
{
"@context": [
"https://www.w3.org/ns/cid/v1",
"https://www.w3.org/ns/lws/v1"
],
"id": "https://storage.example/",
"type": "Storage",
"service": [
{
"type": "StorageRoot",
"serviceEndpoint": "https://storage.example/root/"
},
{
"type": "AccessRequestService",
"serviceEndpoint": "https://access.example/request/",
"conformsTo": ["https://www.w3.org/ns/lws#AccessProfile"]
},
{
"type": "AccessGrantService",
"serviceEndpoint": "https://access.example/grant/",
"conformsTo": ["https://www.w3.org/ns/lws#AccessProfile"]
}
]
}
访问请求或访问授权是一个数据
对象,用于表达身份和路由属性,以及描述
所请求或获准操作的 access 对象。11.3
访问配置文件中描述了一种使用
ODRL 概念描述访问请求的配置文件。
以下示例展示了一个访问请求。该示例使用 11.4.1 JSON-LD 序列化中定义的 JSON-LD 序列化形式。
{
"@context": [
"https://www.w3.org/ns/lws/v1"
],
"type": ["AccessRequest"],
"inbox": "https://id.example/agent/inbox/",
"storage": "https://storage.example/",
"access": [{
"type": ["AccessPolicy"],
"action": ["read", "create"],
"assignee": "https://id.example/agent",
"target": {
"type": "StorageResource",
"value": ["https://storage.example/root/projects/"]
},
"constraint": [
{
"leftOperand": "purpose",
"operator": "eq",
"rightOperand": "https://purpose.example/collaboration"
},
{
"leftOperand": "dateTime",
"operator": "lteq",
"rightOperand": "2026-06-09T10:00:00Z"
}
]
}]
}
以下示例展示了一个访问授权。该示例使用 11.4.1 JSON-LD 序列化中定义的 JSON-LD 序列化形式。
{
"@context": [
"https://www.w3.org/ns/lws/v1"
],
"type": ["AccessGrant"],
"storage": "https://storage.example/",
"access": [{
"type": ["AccessPolicy"],
"action": ["read"],
"assignee": "https://id.example/agent",
"target": {
"type": "StorageResource",
"value": ["https://storage.example/root/projects/"]
},
"constraint": [
{
"leftOperand": "purpose",
"operator": "eq",
"rightOperand": "https://purpose.example/collaboration"
},
{
"leftOperand": "dateTime",
"operator": "lteq",
"rightOperand": "2026-06-09T10:00:00Z"
}
]
}]
}
type 属性表示文档的类型。
type 属性的值必须是一个或多个
术语和
绝对 URL 字符串。对于
访问请求,该值必须
包含术语 AccessRequest。
对于访问授权,该值必须
包含术语
AccessGrant。还可以包含其他类型值。
此属性为必需。
{
"type": ["AccessGrant"]
}
storage 属性标识访问请求或访问授权所适用的 LWS 存储。
storage 属性的值必须是 URI。
此属性为必需。
{
"storage": "https://storage.example/"
}
inbox 属性指定一个 URI,用于接收与
访问请求或访问授权
相关的通知(参见11.6
通知)。
inbox 属性的值必须是 URI。
此属性为可选。
{
"inbox": "https://id.example/agent/inbox/"
}
access 属性根据服务器支持的配置文件,描述一个或多个
访问配置文件的特征。基于
ODRL 信息模型的
配置文件在11.3
访问配置文件中描述。
access 属性的值必须是包含一个
或多个对象的集合。
此属性为必需。
本规范定义了一个基于
ODRL
信息模型和ODRL
词汇表的访问配置文件,该配置文件
由以下 URL 标识:
https://www.w3.org/ns/lws#AccessProfile。
type 属性表示访问策略的类型。
type 属性的值必须是一个或多个
术语和
绝对 URL 字符串,并且必须包含
术语 AccessPolicy。还可以包含其他类型值。
此属性为必需。
{
"access": [{
"type": ["AccessPolicy"]
}]
}
action 属性描述操作,即主体希望
执行的操作(对于请求),或获准执行的操作(对于授权)。
action 属性的值必须是包含一个
或多个字符串的集合。每个值必须对应服务器能够识别的操作。
必须支持以下值:read、modify、
create 和 delete。read、
modify 和 delete 值由
ODRL
词汇表定义。
create 值由本
规范定义。
这些操作值与 LWS 操作的对应关系如下:
read — 获取资源或其元数据(HTTP GET、HEAD)modify — 修改现有资源(HTTP PUT、PATCH)create — 在容器中创建新资源(HTTP POST)
delete — 移除现有资源(HTTP DELETE)此属性为必需。
{
"access": [{
"action": ["read", "create"]
}]
}
assignee 属性标识请求访问的一方(对于
请求),或被授予访问权限的一方(对于授权)。
assignee 属性的值必须是 URI。公共
访问权限可以使用FOAF
词汇表中的 Agent 类进行
分配,该类由 URI
http://xmlns.com/foaf/0.1/Agent 标识。
此属性为必需。
{
"access": [{
"assignee": "https://id.example/agent"
}]
}
target 属性必须是一个包含以下
属性的对象:
type — 用于标识目标匹配器
类型的术语或
绝对 URL 字符串。
value — 用于标识
目标资源的一个或多个字符串的集合。
本规范定义以下可用于
目标对象的 type 值:
https://www.w3.org/ns/lws#DataResource — 匹配
类型为 lws:DataResource 的资源。
https://www.w3.org/ns/lws#Container — 匹配
类型为 lws:Container 的资源。
https://www.w3.org/ns/lws#StorageResource — 匹配
任意存储资源。
服务器 可以 支持其他类型值。
此属性是可选的。
{
"access": [{
"target": {
"type": "StorageResource",
"value": [
"https://storage.example/root/2025",
"https://storage.example/root/2026"
]
}
}]
}
constraint 属性定义限制或限定所请求或授予
访问权限的条件。此属性使用 ODRL
约束模型。
constraint 属性的值(如果存在)必须是一个
约束对象集合。每个约束对象必须包含以下
属性:
leftOperand — 标识受约束操作数的字符串。
operator — 标识比较运算符的字符串。
rightOperand — 用于比较的值。其类型取决于
leftOperand。
当存在多个约束对象时,所有约束对象都必须 得到满足。
通告支持此配置文件的服务器必须支持以下
leftOperand 值:
client — 表示 HTTP 请求的客户端标识符
format — 表示目标 HTTP
资源的媒体类型
type — 表示资源链接
标头中表达的类型 URL
purpose — 表示所请求或授予
访问权限的预期用途
dateTime — 表示当前日期和时间
此属性为可选。
以下示例说明了这些约束的用法:
以下示例使用值为
purpose 的 leftOperand 来描述所请求或授予
访问权限的预期用途。使用 eq 运算符时,
rightOperand 的值是标识用途的 URI。
使用 isAnyOf 运算符时,rightOperand
的值是一个用途 URI 数组,如果所声明的用途
与列出的任意值匹配,则满足该约束:
{
"access": [{
"constraint": [
{
"leftOperand": "purpose",
"operator": "isAnyOf",
"rightOperand": [
"https://purpose.example/collaboration",
"https://purpose.example/research"
]
}
]
}]
}
以下示例使用值为
client 的 leftOperand,将访问权限限制
为特定客户端应用程序。使用 eq 运算符时,
rightOperand 的值是标识客户端的 URI:
{
"access": [{
"constraint": [
{
"leftOperand": "client",
"operator": "eq",
"rightOperand": "https://app.example/client-id"
}
]
}]
}
以下示例使用值为
format 的 leftOperand,将
访问权限限制为具有特定媒体类型的资源。当使用 eq 运算符
时,rightOperand 值是标识
IANA 媒体类型的字符串。当
使用 isAnyOf 运算符时,rightOperand 值
是一个媒体类型字符串数组;如果资源的
媒体类型与列出的任意值匹配,则允许访问:
{
"access": [{
"constraint": [
{
"leftOperand": "format",
"operator": "isAnyOf",
"rightOperand": ["image/jpeg", "image/png"]
}
]
}]
}
所有存储资源都将包含指示类型值的链接标头:
Link: <https://www.w3.org/ns/lws#DataResource>; rel="type"
Link: <https://type.example/Playlist>; rel="type"
以下示例使用值为
type 的 leftOperand,根据 HTTP
Link 标头中通告的资源类型限制访问权限。
使用 eq 运算符时,rightOperand 的值
是标识资源类型的 URI。使用 isAnyOf 运算符时,
rightOperand 的值是一个类型 URI 数组,如果资源的
类型与列出的任意值匹配,则允许访问:
{
"access": [{
"constraint": [
{
"leftOperand": "type",
"operator": "isAnyOf",
"rightOperand": [
"https://type.example/Playlist",
"https://type.example/Song"
]
}
]
}]
}
以下示例使用值为
dateTime 的 leftOperand,将访问权限限制
在特定时间窗口内。gteq 运算符定义
窗口的开始,lteq 运算符定义窗口的结束。
时间约束的 rightOperand 值是
XML
Schema dateTime 字符串:
{
"access": [{
"constraint": [
{
"leftOperand": "dateTime",
"operator": "gteq",
"rightOperand": "2026-03-09T12:00:00Z"
},
{
"leftOperand": "dateTime",
"operator": "lteq",
"rightOperand": "2026-06-09T10:00:00Z"
}
]
}]
}
当存在多个约束对象时,所有约束对象都必须 得到满足,才能 允许访问。以下示例将访问权限限制为特定日期之前的特定客户端 应用程序:
{
"access": [{
"action": ["read"],
"assignee": "https://id.example/agent",
"target": {
"type": "StorageResource",
"value": ["https://storage.example/root/projects/"]
},
"constraint": [
{
"leftOperand": "dateTime",
"operator": "lteq",
"rightOperand": "2026-06-09T10:00:00Z"
},
{
"leftOperand": "client",
"operator": "eq",
"rightOperand": "https://app.example/client-id"
}
]
}]
}
本节定义的数据模型独立于任何特定的 序列化形式。
本规范为访问请求
和
访问授权定义了一种 JSON-LD 序列化形式。使用此序列化形式的文档必须包含一个
@context 属性,其值是一个包含
https://www.w3.org/ns/lws/v1 的有序集合。还可以
包含其他上下文条目,以定义扩展术语。
与此序列化形式关联的媒体类型为 application/lws+json。
{
"@context": [
"https://www.w3.org/ns/lws/v1"
]
}
访问请求和访问授权端点 是 LWS 容器,并且必须 符合本规范定义的规则。
这些端点至少需要支持 GET 和 POST 操作。建议支持 DELETE 操作。 还可以支持其他操作。
这些端点对请求和响应有效载荷都必须支持 11.4.1 JSON-LD 序列化中定义的 JSON-LD 序列化形式。服务器 可以支持其他序列化形式。
主体通过向
访问请求端点提交 POST 请求来
创建访问请求。存储
控制者通过
向访问授权端点提交
POST 请求来创建访问授权。
成功的 POST 操作必须使用 Location 标头作出响应,
该标头被设置为
新资源的 URL。
POST / — 创建访问请求。GET / — 获取访问请求列表。GET /:id — 获取特定访问请求。DELETE /:id — 取消访问请求。POST / — 创建访问授权。GET / — 获取访问授权列表。GET /:id — 获取特定访问授权。DELETE /:id — 撤销访问授权。
通知提供了一种机制,用于告知主体和存储
控制者有关
访问请求和访问授权的变更。当
访问请求或访问授权上存在
inbox 属性时,服务器应当向该端点递送
通知,将事件告知
受让方。
通知的序列化形式必须符合 LWS 通知数据模型的要求。
递送到 inbox 端点的通知必须符合
LWS 协议中为通知递送定义的要求。
本节描述的通知机制类似于
关联数据通知模式,其中
通过 inbox 属性发现通知端点,并通过向该收件箱发送 POST
请求来递送通知。
服务器应当响应以下事件发送通知:
服务器可以响应其他事件发送通知。
LWS 存储描述必须可使用媒体类型
application/lws+cid 进行序列化。
application/lws+cid 媒体类型标识一种文档,该文档是
W3C 受控标识符文档 [CID-1.0] 的一种特化,并使用 LWS 词汇表进行了扩展。
LWS 容器表示必须支持媒体类型
application/lws+json。
虽然 LWS 容器表示使用 JSON-LD 约定,但 LWS 的约束和要求
使使用特定媒体类型具有合理性。由于 LWS 容器可以被视为 JSON-LD 的受限配置文件,
实现应当将
application/ld+json; profile="https://www.w3.org/ns/lws/v1" 媒体类型视为等同于
application/lws+json。
对于容器表示,媒体类型
application/lws+json、application/ld+json 和
application/json 是等价的:响应主体是同一个 JSON-LD 文档,
符合容器表示结构,该结构定义于8.1 容器
表示中,只有 Content-Type 响应标头不同。服务器必须满足对其中任一媒体类型的请求,并且必须将 Content-Type 响应标头设置为所请求的
媒体类型。
由于容器响应的 Content-Type 取决于请求的
Accept 标头,因此这些响应应当包含
Vary: Accept 标头 [RFC9110]。
注(非规范性):此等价性仅适用于上述三种媒体类型。
与任何 HTTP 资源一样,服务器可以通过标准内容协商 [RFC9110] 提供容器的其他表示(例如
text/turtle);本规范既不要求也不排除此类支持。
某些复合资源(例如容器)可能包含大量资源。 为了允许客户端增量检索列表,服务器应当为成员数量超过服务器所确定 阈值的容器支持 分页。
分页基于链接:服务器通过 HTTP Link 标头提供分页 URI
[RFC8288],
使客户端无需依赖数字偏移量即可遍历完整列表。
当列表分页时,响应主体仅包含当前页面中的项目。复合
资源的 id、type 和 totalItems 属性
反映完整的成员集合,而 items
仅包含当前页面中的资源。
分页 URI 使用以下标准链接关系在 Link 标头中
传递:
rel="first":结果第一页的 URI。分页响应中必须存在。
rel="last":结果最后一页的 URI。分页响应中可以存在。
rel="next":结果下一页的 URI。存在后续
页面时必须存在。在最后一页中必须省略。rel="prev":结果上一页的 URI。存在前面的
页面时可以存在。在第一页中必须省略。所有分页 URI 对客户端而言都是不透明的。客户端不应 构造或修改分页 URI;它们应当使用服务器提供的 URI。
客户端请求复合资源的 URI 以获取第一页。响应包含 分页 Link 标头,客户端跟随这些标头来检索后续页面。服务器也可以支持 通过先前扫描期间获得的分页 URI 直接访问特定页面。
返回分页响应时,服务器必须以 200
OK 响应。响应主体中的 totalItems
属性应当反映所有页面中的项目总数,
而不只是当前页面中的项目数。
请求:
GET /alice/photos/ HTTP/1.1
Authorization: Bearer <token>
Accept: application/lws+json
响应(第一页):
HTTP/1.1 200 OK
Content-Type: application/lws+json
ETag: "photos-page1-etag"
Link: </alice/photos/.meta>; rel="linkset"; type="application/linkset+json"
Link: </alice/>; rel="up"
Link: <https://www.w3.org/ns/lws#Container>; rel="type"
Link: </alice/photos/?page=1>; rel="first"
Link: </alice/photos/?page=3>; rel="last"
Link: </alice/photos/?page=2>; rel="next"
{
"@context": "https://www.w3.org/ns/lws/v1",
"id": "/alice/photos/",
"type": "Container",
"totalItems": 150,
"items": [
{
"type": "DataResource",
"id": "/alice/photos/vacation.jpg",
"format": "image/jpeg",
"size": 248392,
"modified": "2025-11-20T10:30:00Z"
},
{
"type": "DataResource",
"id": "/alice/photos/portrait.png",
"format": "image/png",
"size": 102400,
"modified": "2025-11-21T14:15:00Z"
}
]
}
请求(下一页):
GET /alice/photos/?page=2 HTTP/1.1
Authorization: Bearer <token>
Accept: application/lws+json
响应(中间页):
HTTP/1.1 200 OK
Content-Type: application/lws+json
ETag: "photos-page2-etag"
Link: </alice/photos/.meta>; rel="linkset"; type="application/linkset+json"
Link: </alice/>; rel="up"
Link: <https://www.w3.org/ns/lws#Container>; rel="type"
Link: </alice/photos/?page=1>; rel="first"
Link: </alice/photos/?page=1>; rel="prev"
Link: </alice/photos/?page=3>; rel="next"
Link: </alice/photos/?page=3>; rel="last"
{
"@context": "https://www.w3.org/ns/lws/v1",
"id": "/alice/photos/",
"type": "Container",
"totalItems": 150,
"items": [
{
"type": "DataResource",
"id": "/alice/photos/sunset.jpg",
"format": "image/jpeg",
"size": 315000,
"modified": "2025-11-22T09:00:00Z"
}
]
}
LWS JSON-LD 上下文由 URL https://www.w3.org/ns/lws/v1 标识。
从该 URL 解析得到的文档的 SHA2-256 摘要包含在下表中:
| 上下文 URL 和哈希值 |
|---|
URL: https://www.w3.org/ns/lws/v1 |
| SHA2-256 摘要: 待办:在上下文文档最终确定后加入 JSON-LD 上下文摘要 |
可以使用如下命令验证摘要:
curl -sL -H "Accept: application/ld+json" https://www.w3.org/ns/lws/v1 | openssl dgst -sha256
定义如何在 LWS 协议中标识和寻址资源,包括 URI 方案、资源命名约定和解析机制。本节可能会移至 另一节中;例如“资源访问”
此处有意留空
描述确保 LWS 实现能够跨不同 平台、环境和存储后端工作,同时保持互操作性的注意事项,并提供 支持更换存储提供商的操作能力
此处有意留空
本节为非规范性内容。
正式的安全注意事项章节,涵盖威胁模型、安全要求和 安全部署 LWS 的实现指南。
OAuth 2.0 安全当前最佳实践 [RFC9700] 中描述的建议适用于本 规范。
本节是非规范性的。
传输层安全(TLS)是防止篡改、欺骗和 信息泄露的重要机制。受 TLS 保护的通信可以根据 [RFC6125] 进行验证。 实现方面的安全注意事项可参见“安全使用传输 层安全(TLS)和数据报传输层安全(DTLS)的建议”[RFC9325]。
本节是非规范性的。
持有者令牌和数字凭证容易遭到盗窃和重放。缓解措施包括使用 合理较短的生存期、将令牌绑定到特定受众,以及安全地存储令牌。
aud
声明,可以防止令牌被重放到其他服务器。访问令牌的
受众如果包含
多个服务器,则其中任何一个服务器都可以将其重放到其他服务器;授权
服务器可通过
颁发以单个存储 URI 为受众的令牌来避免这种情况。应用程序若处理的凭证具有
一个
不受限制的受众,则在将这些凭证发送给其他实体时需要谨慎,
尤其是向不受信任的
授权服务器发送时。如果凭证颁发者无法限制令牌的受众,客户端
可以使用
OAuth 2.0 令牌交换 [RFC8693] 等机制来创建
受众受限的凭证,然后再
与不同安全域中的授权服务器交互。
dpop_signing_alg_values_supported 和
dpop_bound_access_tokens_required 参数,这些参数定义于 [RFC9728]。
localStorage、URL
或日志中,都会使该令牌更容易遭到窃取攻击。
本节是非规范性的。
客户端从存储服务器发送的响应中获知授权服务器的位置,而该存储服务器 可能 不受客户端信任。以下注意事项适用于此发现过程。
Link 标头获取存储 URI,该响应来自
存储
服务器。恶意服务器可以提供另一个存储的 URI,导致客户端获取
一个访问令牌
,用于该存储,并将其出示给恶意服务器,后者随后可以重放该令牌。因此,
客户端
需要使用存储描述确认,
所请求的 URI 与
该存储根由同一源提供,然后才能请求访问令牌,具体描述
见
5.2.3
客户端对发现结果的验证。
resource_metadata URL、存储 URI(位于
Link 标头中),以及受保护资源元数据文档中的颁发者标识符,均由
该
存储服务器选择,而客户端检索它们标识的文档。客户端若执行此
检索是代表
其他参与方,例如服务器端应用程序,则会面临服务器端请求伪造风险,具体
描述见
[RFC9728] 的第 7.7 节。预防措施包括
将检索限制为使用 https 方案,以及
阻止发往内部 IP 地址范围的请求。
本节是非规范性的。
本节是非规范性的。
本节为非规范性内容。
LWS 协议的隐私影响,包括数据最小化、用户同意和 隐私保护型实现模式。
凭证携带有关用户和主体的信息。虽然数字签名可以防止篡改, 但客户端或第三方仍有可能读取未加密凭证中的值。
因此,建议凭证颁发者创建仅包含身份验证或授权所必需信息的令牌, 并避免包含敏感属性,除非确有需要。
一般而言,将未加密的凭证数据写入日志是一种反模式。在确有必要的情况下, 实现可以截断凭证或对其进行哈希处理,以保护凭证主体的隐私。
在 JWT 中使用假名标识符时,存储服务器仍可能随时间推移关联来自同一 主体 的请求。 为在这种情况下保护用户隐私,客户端应用程序可以请求批量颁发 JWT,其中每个 JWT 仅使用一次。 这并不能阻止存储服务器使用 JWT 内容的相似性或来源 IP 地址等其他信息 来关联请求。使用假名标识符时,授权服务器需要注意 不要多次颁发相同的标识符。
本节为非规范性内容。
本节为非规范性内容。
actor 属性会
泄露谁进行了变更。在某些上下文中这可能并不适当。
默认情况下应当省略 actor 属性。服务器可以使
是否包含该属性可配置。
本节为非规范性内容。
本规范向 RFC 5785 [RFC5785] 建立的“Well-Known URI”注册表 中添加以下值。
本规范专门注册 application/lws+json 媒体类型,用于
标识符合关联 Web 存储容器格式的文档。
application/lws+json 媒体类型的资源
必须符合
application/json 媒体类型的所有要求,因此也受
[RFC8259] 第 11 节中规定的相同编码
注意事项约束。
请注意,虽然关联 Web 存储格式使用 JSON-LD 约定,但 LWS 实现还存在许多 约束和附加 要求,足以说明使用特定媒体类型的合理性。
由于 LWS 容器可视为 JSON-LD 的受限配置文件,实现应当将
application/ld+json; profile="https://www.w3.org/ns/lws/v1" 媒体类型视为等同于
application/lws+json。
本规范注册 application/lws+cid 媒体类型,用于标识
符合关联 Web 存储协议的存储描述文档,作为
W3C 受控标识符 1.0 文档格式的一种特化形式。
application/lws+cid 媒体类型的资源
必须符合 application/json
媒体类型的所有要求,因此适用
[RFC8259] 第 11 节中规定的相同编码注意事项。
本节为非规范性内容。
本规范大量借鉴了 Solid Protocol [Solid-Protocol],该协议由 Sarven Capadisli、Tim Berners-Lee、Kjetil Kjernsmo、Ruben Verborgh、Justin Bingham、Dmitri Zagidulin 随时间推移编辑而成。
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in: