目录
目录是用于记录针对包源所有包操作(例如创建和删除)的资源。 目录资源在服务索引中为 Catalog
类型。 可使用此资源查询所有已发布的包。
注意
由于官方 NuGet 客户端不使用目录,因此并非所有包源均会实现目录。
注意
目前,nuget.org 目录在中国不可用。 有关更多详细信息,请参阅 NuGet/NuGetGallery#4949。
版本控制
使用以下 @type
值:
值@type | 说明 |
---|---|
目录/3.0.0 | 初始版本 |
基 URL
以下 API 的入口点 URL 是与上述资源 @type
值关联的 @id
属性的值。 本主题使用占位符 URL {@id}
。
HTTP 方法
目录资源中找到的所有 URL 均仅支持 HTTP 方法 GET
和 HEAD
。
目录索引
目录索引是已知位置的文档,其中包含按时间顺序排序的目录项列表。 它是目录资源的入口点。
索引由目录页组成。 每个目录页均包含目录项。 每个目录项均表示在某个时间点与单个包相关的某一事件。 目录项可表示从包源中创建、取消列出、重新列出或删除的包。 通过按时间顺序处理目录项,客户端可生成存在于 V3 包源上的每个包的最新视图。
简而言之,目录 Blob 具有以下分层结构:
- 索引:目录的入口点。
- 页面:对目录项的分组。
- 叶:表示目录项的文档,它是单个包的状态的快照。
每个目录对象均有一个名为 commitTimeStamp
的属性,它表示此项添加到目录的时间。 目录项会分批以名为“提交”的方式添加到目录页中。 同一提交中的所有目录项均有相同的提交时间戳 (commitTimeStamp
) 和提交 ID (commitId
)。 放置于同一提交中的目录项表示同一时间点前后在包源中出现的事件。 目录提交内部不会进行排序。
由于每个包 ID 和版本均唯一,因此每个提交中永不会存在多个目录项。 这可确保单个包的目录项在提交时间戳方面始终可明确进行排序。
每个 commitTimeStamp
的目录永不会有多个提交。 换言之,commitId
会与 commitTimeStamp
重复。
较之包元数据资源(按包 ID 编制索引),目录仅按时间编制索引(且可查询)。
目录项始终以单调递增的方式按时间顺序添加到目录中。 这意味着,如果在 X 时间添加目录提交,则在时间小于或等于 X 时永不会添加任何目录提交。
以下请求会提取目录索引。
GET {@id}
目录索引是一个 JSON 文档,其中包含具有以下属性的对象:
名称 | 类型 | 必须 | 说明 |
---|---|---|---|
commitId | string | 是 | 与最新提交关联的唯一 ID |
commitTimeStamp | string | 是 | 最新提交的时间戳 |
count | integer | 是 | 索引中的页面数 |
items | 对象数组 | 是 | 对象数组,其中每个对象均表示一个页面 |
items
数组中的每个元素均为一个对象,其中包含有关每个页面的部分最少详细信息。 这些页面对象不含目录叶(项)。 此数组中元素的顺序未进行定义。 页面可由客户端在内存中使用其 commitTimeStamp
属性来排序。
引入新页面时,count
会递增,而新对象则会显示在 items
数组中。
将项添加到目录时,索引的 commitId
会更改且 commitTimeStamp
会增大。 这两个属性本质上是 items
数组中所有页面 commitId
和 commitTimeStamp
值的摘要。
索引中的目录页对象
在目录索引的 items
属性中找到的目录页对象具有以下属性:
名称 | 类型 | 必须 | 注释 |
---|---|---|---|
@id | string | 是 | 待提取目录页的 URL |
commitId | string | 是 | 与此页面中的最新提交关联的唯一 ID |
commitTimeStamp | string | 是 | 此页面中最新提交的时间戳 |
count | integer | 是 | 目录页中的项数 |
较之包元数据资源(在某些情况下会将叶内联到索引中),目录叶永不会内联到索引中,且始终必须用页面的 @id
URL 来提取。
示例请求
GET https://api.nuget.org/v3/catalog0/index.json
示例响应
{
"commitId": "3d698852-eefb-48ed-8f55-9ee357540d20",
"commitTimeStamp": "2017-10-31T23:33:17.0954363Z",
"count": 3,
"items": [
{
"@id": "https://api.nuget.org/v3/catalog0/page0.json",
"commitId": "3a4df280-3d86-458e-a713-4c91ca261fef",
"commitTimeStamp": "2015-02-01T06:30:11.7477681Z",
"count": 540
},
{
"@id": "https://api.nuget.org/v3/catalog0/page1.json",
"commitId": "8bcd3cbf-74f0-47a2-a7ae-b7ecc50005d3",
"commitTimeStamp": "2015-02-01T06:39:53.9553899Z",
"count": 540
},
{
"@id": "https://api.nuget.org/v3/catalog0/page2.json",
"commitId": "3d698852-eefb-48ed-8f55-9ee357540d20",
"commitTimeStamp": "2017-10-31T23:33:17.0954363Z",
"count": 47
}
]
}
目录页
目录页是目录项的集合。 它是使用目录索引中找到的某一 @id
值来提取的文档。 目录页的 URL 不应可预测,而应仅使用目录索引来发现。
新目录项仅会以最高提交时间戳添加到目录索引中的页面,或是添加到新页面。 将具有较高提交时间戳的页面添加到目录后,永不会向旧页面进行添加或更改旧页面。
目录页文档是指具有以下属性的 JSON 对象:
名称 | 类型 | 必须 | 说明 |
---|---|---|---|
commitId | string | 是 | 与此页面中的最新提交关联的唯一 ID |
commitTimeStamp | string | 是 | 此页面中最新提交的时间戳 |
count | integer | 是 | 页面中的项数 |
items | 对象数组 | 是 | 此页面中的目录项 |
parent | string | 是 | 目录索引的 URL |
items
数组中的每个元素均为一个对象,其中包含有关目录项的部分最少详细信息。 这些项对象不含目录项的所有数据。 页面的 items
数组中项目的顺序未进行定义。 项目可由客户端在内存中使用其 commitTimeStamp
属性来排序。
页面中的目录项数由服务器实现定义。 对于 nuget.org,每个页面中最多有 550 个项目,但某些页面的实际项数可能会较小,具体取决于该时间点下一提交批次的大小。
引入新项时,count
会递增,且新的目录项对象会显示在 items
数组中。
随着更多项目添加到页面,commitId
会更改且 commitTimeStamp
会增大。 这两个属性本质上是 items
数组中所有 commitId
和 commitTimeStamp
值的摘要。
页面中的目录项对象
在目录页面的 items
属性中找到的目录项对象具有以下属性:
名称 | 类型 | 必须 | 注释 |
---|---|---|---|
@id | string | 是 | 用于提取目录项的 URL |
@type | string | 是 | 目录项的类型 |
commitId | string | 是 | 与此目录项关联的提交 ID |
commitTimeStamp | string | 是 | 此目录项的提交时间戳 |
nuget:id | string | 是 | 与此叶相关的包 ID |
nuget:version | string | 是 | 与此叶相关的包版本 |
@type
值为以下两个值之一:
nuget:PackageDetails
:它对应于目录叶文档中的PackageDetails
类型。nuget:PackageDelete
:它对应于目录叶文档中的PackageDelete
类型。
有关每种类型的含义的更多详细信息,请参阅下方的相应项目类型。
示例请求
GET https://api.nuget.org/v3/catalog0/page2926.json
示例响应
{
"commitId": "616117f5-d9dd-4664-82b9-74d87169bbe9",
"commitTimeStamp": "2017-10-31T23:30:32.4197849Z",
"count": 5,
"parent": "https://api.nuget.org/v3/catalog0/index.json",
"items": [
{
"@id": "https://api.nuget.org/v3/catalog0/data/2017.10.31.23.30.32/util.biz.payments.0.0.4-preview.json",
"@type": "nuget:PackageDetails",
"commitId": "616117f5-d9dd-4664-82b9-74d87169bbe9",
"commitTimeStamp": "2017-10-31T23:30:32.4197849Z",
"nuget:id": "Util.Biz.Payments",
"nuget:version": "0.0.4-preview"
},
{
"@id": "https://api.nuget.org/v3/catalog0/data/2017.10.31.23.28.02/util.biz.0.0.4-preview.json",
"@type": "nuget:PackageDetails",
"commitId": "820340b2-97e3-4f93-b82e-bc85550a6560",
"commitTimeStamp": "2017-10-31T23:28:02.788239Z",
"nuget:id": "Util.Biz",
"nuget:version": "0.0.4-preview"
},
{
"@id": "https://api.nuget.org/v3/catalog0/data/2017.10.31.22.31.22/sourcecode.clay.data.1.0.0-preview1-00258.json",
"@type": "nuget:PackageDetails",
"commitId": "cae34527-ffc7-4e96-884f-7cf95a32dbdd",
"commitTimeStamp": "2017-10-31T22:31:22.5169519Z",
"nuget:id": "SourceCode.Clay.Data",
"nuget:version": "1.0.0-preview1-00258"
},
{
"@id": "https://api.nuget.org/v3/catalog0/data/2017.10.31.22.31.22/sourcecode.clay.1.0.0-preview1-00258.json",
"@type": "nuget:PackageDetails",
"commitId": "cae34527-ffc7-4e96-884f-7cf95a32dbdd",
"commitTimeStamp": "2017-10-31T22:31:22.5169519Z",
"nuget:id": "SourceCode.Clay",
"nuget:version": "1.0.0-preview1-00258"
},
{
"@id": "https://api.nuget.org/v3/catalog0/data/2017.10.31.22.31.22/sourcecode.clay.json.1.0.0-preview1-00258.json",
"@type": "nuget:PackageDetails",
"commitId": "cae34527-ffc7-4e96-884f-7cf95a32dbdd",
"commitTimeStamp": "2017-10-31T22:31:22.5169519Z",
"nuget:id": "SourceCode.Clay.Json",
"nuget:version": "1.0.0-preview1-00258"
}
]
}
目录叶
目录叶包含在某个时间点有关特定包 ID 和版本的元数据。 它是使用在 @id
目录页中找到的值来提取的文档。 目录叶的 URL 不应可预测,而应仅使用目录页来发现。
目录叶文档是指具有以下属性的 JSON 对象:
名称 | 类型 | 必须 | 说明 |
---|---|---|---|
@type | 字符串或字符串数组 | 是 | 目录项的类型 |
catalog:commitId | string | 是 | 与此目录项关联的提交 ID |
catalog:commitTimeStamp | string | 是 | 此目录项的提交时间戳 |
id | string | 是 | 目录项的包 ID |
published | string | 是 | 包目录项的发布日期 |
版本 | string | 是 | 目录项的包版本 |
项目类型
@type
属性为字符串或字符串数组。 为方便起见,如果 @type
值为字符串,则应将其视为大小为 1 的任意数组。 并非 @type
的所有可能值均会进行记录。 但是,每个目录项只有以下两个字符串类型值之一:
PackageDetails
:表示包元数据的快照PackageDelete
:表示已删除的包
包详细信息目录项
具有 PackageDetails
类型的目录项包含特定包的包元数据快照(ID 与版本的组合)。 当包源遇到以下任一情况时,会生成包详细信息目录项:
- 推送包。
- 重新列出包。
- 取消列出包。
- 弃用包。
- 取消弃用包。
- 重排包。
- 包的漏洞状态被更新。
包重排是一种管理手段,它实质上是生成现有包的虚假推送,而不更改包本身。 在 nuget.org 中,修复使用目录的某一后台作业中的 bug 后,便会使用重排。
使用目录项的客户端不应尝试确定哪些场景生成了目录项。 相反,客户端应仅使用目录项中包含的元数据来更新任一维护的视图或索引。 此外,应正常(以幂等方式)处理重复或冗余的目录项。
除包含在目录叶上的属性外,包详细信息目录项还具有以下属性。
名称 | 类型 | 必须 | 说明 |
---|---|---|---|
作者 | string | 否 | |
created | string | 否 | 首次创建包的时间戳。 回退属性:published 。 |
dependencyGroups | 对象数组 | 否 | 包的依赖项,且这些依赖项按目标框架分组(与包元数据资源的格式相同) |
弃用 | object | 否 | 与包关联的弃用(与包元数据资源的格式相同) |
description | string | 否 | |
iconUrl | string | 否 | |
isPrerelease | boolean | 否 | 包版本是否为预发行。 可从 version 检测到。 |
language | string | 否 | |
licenseUrl | string | 否 | |
列出 | boolean | 否 | 是否已列出包 |
minClientVersion | string | 否 | |
packageHash | string | 是 | 包的哈希值(采用标准 base 64 进行编码) |
packageHashAlgorithm | string | 是 | |
packageSize | integer | 是 | 包 .nupkg 的大小(以字节为单位) |
packageTypes | 对象数组 | 否 | 作者指定的包类型。 |
projectUrl | string | 否 | |
releaseNotes | string | 否 | |
requireLicenseAgreement | boolean | 否 | 假设 false 已排除 |
summary | string | 否 | |
标记 | 字符串数组 | 否 | |
title | string | 否 | |
verbatimVersion | string | 否 | 最初在 .nuspec 中找到的版本字符串 |
vulnerabilities | 对象数组 | 否 | 包的安全漏洞 |
包 version
属性为规范化后的完整版本字符串。 这意味着,可在此处包含 SemVer 2.0.0 生成数据。
created
时间戳是指包源首次收到包的时间,它通常为目录项提交时间戳之前的短时间。
packageHashAlgorithm
由服务器实现所定义的字符串,它表示用于生成 packageHash
的哈希算法。 nuget.org 始终使用 SHA512
的 packageHashAlgorithm
值。
仅当作者已指定包类型时,packageTypes
属性才会存在。 如果存在,它始终至少会有一个(1 个)条目。 packageTypes
数组中的每个项目均为具有以下属性的 JSON 对象:
名称 | 类型 | 必须 | 说明 |
---|---|---|---|
name | 字符串 | 是 | 包类型的名称。 |
版本 | string | 否 | 包类型的版本。 仅当作者在 nuspec 中显式指定版本时才存在。 |
published
时间戳是指上次列出包的时间。
注意
在 nuget.org 中,当包未列出时,published
值将设为 1900 年。
漏洞
一个 vulnerability
对象数组。 每个漏洞均具有以下属性:
名称 | 类型 | 必须 | 说明 |
---|---|---|---|
advisoryUrl | string | 是 | 针对包的安全公告位置 |
severity | string | 是 | 公告的严重性:“0”= 低、“1”= 中等、“2”= 高、“3”= 严重 |
如果 severity
属性包含此处所列值以外的值,则公告的严重性将视为“低”。
示例请求
GET https://api.nuget.org/v3/catalog0/data/2015.02.01.11.18.40/windowsazure.storage.1.0.0.json
示例响应
{
"@type": [
"PackageDetails",
"catalog:Permalink"
],
"authors": "NuGet.org Team",
"catalog:commitId": "49fe04d8-5694-45a5-9822-3be61bda871b",
"catalog:commitTimeStamp": "2015-02-01T11:18:40.8589193Z",
"created": "2011-12-02T20:21:23.74Z",
"description": "This package is an example for the V3 protocol.",
"deprecation": {
"reasons": [
"Legacy",
"HasCriticalBugs",
"Other"
],
"message": "This package is an example--it should not be used!",
"alternatePackage": {
"id": "Newtonsoft.JSON",
"range": "12.0.2"
}
},
"iconUrl": "https://www.nuget.org/Content/gallery/img/default-package-icon.svg",
"id": "NuGet.Protocol.V3.Example",
"isPrerelease": false,
"language": "en-US",
"licenseUrl": "http://www.opensource.org/licenses/ms-pl",
"packageHash": "2edCwKLcbcgFJpsAwa883BLtOy8bZpWwbQpiIb71E74k5t2f2WzXEGWbPwntRleUEgSrcxJrh9Orm/TAmgO4NQ==",
"packageHashAlgorithm": "SHA512",
"packageSize": 118348,
"packageTypes": [
{
"@id": "https://api.nuget.org/v3/catalog0/data/2015.02.01.11.18.40/windowsazure.storage.1.0.0.json#packagetypes/DotnetTool",
"@type": "PackageType",
"name": "DotnetTool"
}
],
"projectUrl": "https://github.com/NuGet/NuGetGallery",
"published": "1900-01-01T00:00:00Z",
"requireLicenseAcceptance": false,
"title": "NuGet V3 Protocol Example",
"version": "1.0.0",
"dependencyGroups": [
{
"@id": "https://api.nuget.org/v3/catalog0/data/2015.02.01.11.18.40/windowsazure.storage.1.0.0.json#dependencygroup",
"@type": "PackageDependencyGroup",
"dependencies": [
{
"@id": "https://api.nuget.org/v3/catalog0/data/2015.02.01.11.18.40/windowsazure.storage.1.0.0.json#dependencygroup/aspnet.suppressformsredirect",
"@type": "PackageDependency",
"id": "aspnet.suppressformsredirect",
"range": "[0.0.1.4, )"
},
{
"@id": "https://api.nuget.org/v3/catalog0/data/2015.02.01.11.18.40/windowsazure.storage.1.0.0.json#dependencygroup/webactivator",
"@type": "PackageDependency",
"id": "WebActivator",
"range": "[1.4.4, )"
},
{
"@id": "https://api.nuget.org/v3/catalog0/data/2015.02.01.11.18.40/windowsazure.storage.1.0.0.json#dependencygroup/webapi.all",
"@type": "PackageDependency",
"id": "WebApi.All",
"range": "[0.5.0, )"
}
],
"targetFramework": ".NETFramework4.6"
}
],
"tags": [
"NuGet",
"V3",
"Protocol",
"Example"
],
"vulnerabilities": [
{
"@id": "https://api.nuget.org/v3/catalog0/data/2015.02.01.11.18.40/windowsazure.storage.1.0.0.json#vulnerability/GitHub/999",
"@type": "Vulnerability",
"advisoryUrl": "https://github.com/advisories/ABCD-1234-5678-9012",
"severity": "2"
}
]
}
包删除目录项
PackageDelete
类型的目录项包含一组最少信息,从而向目录客户端表明已从包源中删除包,且不可再用于任何包操作(如还原)。
注意
可使用同一包 ID 和版本删除某一包,然后后续再重新发布此包。 在 nuget.org 中,这是一个极为罕见的情况,因为它会打破官方客户端的假设,即:包 ID 和版本暗指特定的包内容。 有关 nuget.org 上包删除的详细信息,请参阅我们的策略。
除所有目录叶上包含的属性外,包删除目录项没有其他属性。
version
属性是指在包 .nuspec 中找到的原始版本字符串。
published
属性是指删除包的时间,它通常为目录项的提交时间戳之前的短时间。
示例请求
GET https://api.nuget.org/v3/catalog0/data/2017.11.02.00.40.00/netstandard1.4_lib.1.0.0-test.json
示例响应
{
"@type": [
"PackageDelete",
"catalog:Permalink"
],
"catalog:commitId": "19fec5b4-9335-4e4b-bd50-8d5d3f734597",
"catalog:commitTimeStamp": "2017-11-02T00:40:00.1969812Z",
"id": "netstandard1.4_lib",
"originalId": "netstandard1.4_lib",
"published": "2017-11-02T00:37:43.7181952Z",
"version": "1.0.0-test"
}
游标
概述
本节将介绍一个客户端概念,尽管它不一定由协议授权,但应是所有实际目录客户端实现的其中一部分。
由于目录是按时间编制索引的仅追加数据结构,因此客户端应在本地存储游标,从而表示客户端已处理目录项的最近时间点。 请注意,切勿使用客户端的计算机时钟来生成此游标值。 相反,该值应来自目录对象的 commitTimestamp
值。
每当客户端要处理包源上的新事件时,它只需查询包含大于其所存储游标的提交时间戳的所有目录项的目录。 客户端成功处理所有新目录项后,它会记录作为其新游标值来处理的目录项的最新提交时间戳。
通过此方法,客户端可确保永不错过包源上出现的包事件。 此外,客户端不必在游标的已记录提交时间戳之前重新处理旧事件。
这一强大的游标概念被用于众多 nuget.org 后台作业,并用于使 V3 API 自身保持最新。
初始值
当目录客户端首次启动时(因此没有游标值),它应使用 .NET 的 System.DateTimeOffset.MinValue
的默认游标值或是某种类似的最小可表示时间戳概念。
循环访问目录项
若要查询要处理的下一组目录项,客户端应:
- 从本地存储提取已记录的游标值。
- 下载并反序列化目录索引。
- 查找具有大于游标的提交时间戳的所有目录页。
- 声明要处理的目录项的空列表。
- 对于在步骤 3 中匹配的每个目录页:
- 下载并反序列化目录页。
- 查找具有大于游标的提交时间戳的所有目录项。
- 将所有匹配的目录项添加到步骤 4 中声明的列表。
- 按提交时间戳对目录项列表进行排序。
- 按顺序处理每个目录项:
- 下载并反序列化目录项。
- 适当地回应目录项的类型。
- 以特定于客户端的方式处理目录项文档。
- 将最后一个目录项的提交时间戳记录为新游标值。
通过此基本算法,客户端实现可生成包源上提供的所有包的完整视图。 客户端需定期仅执行此算法,才能始终了解包源的最新更改。
从属游标
假设有两个目录客户端具有固有依赖项,其中一个客户端的输出依赖于另一客户端的输出。
示例
例如,在 nuget.org 上,某一新发布的包应先显示在包元数据资源中,然后才能显示在搜索资源中。 这是因为官方 NuGet 客户端执行的“还原”操作会使用包元数据资源。 如果客户使用搜索服务来发现包,则应能使用包元数据资源成功还原此包。 换言之,搜索资源依赖于包元数据资源。 每个资源均有一个目录客户端后台作业来更新该资源。 每个客户端均有自己的游标。
由于这两个资源均从目录进行构建,因此更新搜索资源的目录客户端的游标不得超出包元数据目录客户端的游标。
算法
若要实现此限制,只需将以上算法修改为:
- 从本地存储提取已记录的游标值。
- 下载并反序列化目录索引。
- 查找提交时间戳大于此游标且小于或等于依赖项的游标的所有目录页。
- 声明要处理的目录项的空列表。
- 对于在步骤 3 中匹配的每个目录页:
- 下载并反序列化目录页。
- 查找提交时间戳大于此游标且小于或等于依赖项的游标的所有目录项。
- 将所有匹配的目录项添加到步骤 4 中声明的列表。
- 按提交时间戳对目录项列表进行排序。
- 按顺序处理每个目录项:
- 下载并反序列化目录项。
- 适当地回应目录项的类型。
- 以特定于客户端的方式处理目录项文档。
- 将最后一个目录项的提交时间戳记录为新游标值。
通过使用此修改后的算法,可生成一系列从属目录客户端,而这些客户端会生成自己的特定索引、项目等。