查询所有发布到 nuget.org 的包

旧版 OData V2 API 上一个常见查询模式为枚举所有发布到 nuget.org 的包,按照包发布的时间排序。 需要这类针对 nuget.org 的查询的情况多种多样:

  • 完全复制 nuget.org
  • 检测包发布新版本的时间
  • 查找依赖包的包

执行此操作的老式方法通常依靠按照时间戳对 OData 包实体进行排序并且用 skiptop(页大小)参数跨大型结果集进行分页。 遗憾的是,这种方法有一些缺点:

  • 可能会丢失包,因为查询的对象为常常更改顺序的数据
  • 查询响应时间长,因为查询未优化(最优化的查询支持官方 NuGet 客户端的主流场景)
  • 使用弃用和未记录的 API,意味着将来不保证支持此类查询
  • 不能按照发生的确切顺序重播历史记录

因此,可以遵循以下指南以更加可靠新颖的方法解决上述情况。

概述

本指南的中心是 NuGet API 中称为“目录”的资源。 目录是一种仅追加 API,它允许调用方查看添加到 nuget.org 以及在 nuget.org 中修改和删除的包的完整历史记录。如果你对发布到 nuget.org 的所有包或部分包感兴趣,目录则是随时间流逝随时了解当前可用包集的好方法。

本指南旨在提供高级演练,如果你对目录的详情细节感兴趣,请参阅它的 API 引用文档

以下步骤可以选择任意的编程语言来执行。 如果需要完整的运行示例,请查看下面提及的 C# 示例

反之,按照以下指南生成可靠的目录阅读器。

初始化游标

生成可靠目录阅读器的第一步是实现游标。 有关目录游标设计的完整详细信息,请参阅目录引用文档。 简而言之,游标是时间中的某一点,你在目录中处理事件直到该点。 目录中的事件表示包发布和其他包更改。 如果你关注(从一开始)发布到 NuGet 的所有包,那么将游标初始化为“最小值”时间戳(例如 .NET 中的 DateTime.MinValue)。 如果仅关注现在开始发布的包,则使用当前时间戳作为初始游标值。

对于本指南,我们要将游标初始化为一小时前的时间戳。 现在,只需在内存中保存时间戳。

DateTime cursor = DateTime.UtcNow.AddHours(-1);

确定目录索引 URL

NuGet API 中每个资源(终结点)的位置应使用服务索引发现。 因为本指南重点介绍 nuget.org,所以我们使用 nuget.org 的服务索引。

GET https://api.nuget.org/v3/index.json

服务文档是指包含 nuget.org 上所有资源的 JSON 文档。查找 @type 属性值为 Catalog/3.0.0 的资源。 相关联的 @id 属性值是到目录索引本身的 URL。

查找新的目录叶

使用前面步骤中找到的 @id 属性值下载目录索引:

GET https://api.nuget.org/v3/catalog0/index.json

反序列化目录索引。 筛选出 commitTimeStamp 小于或等于当前游标值的所有目录页对象

对于每个剩余的目录页,使用 @id 属性下载完整文档。

GET https://api.nuget.org/v3/catalog0/page2926.json

反序列化目录页。 筛选出 commitTimeStamp 小于或等于当前游标值的所有目录叶对象

在下载没有筛选出的所有目录页之后,你会具有目录叶对象集,用于表示在游标时间戳和现在时间之间已发布的、未列出、已列出或删除的包。

处理目录叶

此时,可以对目录项执行任何自定义处理。 如果只需要包的 ID 和版本,则可以在页中找到的目录项对象上检查 nuget:idnuget:version 属性。 请务必查看 @type 属性,了解目录项是否涉及现有包或者删除包。

如果对有关包的元数据感兴趣(例如描述、依赖项、.nupkg 大小等),则可以使用 @id 属性提取目录叶文档

GET https://api.nuget.org/v3/catalog0/data/2015.02.01.11.18.40/windowsazure.storage.1.0.0.json

本文档不仅有包元数据资源中包含的所有元数据,而且还有更多内容!

通过该步骤可以实现自定义逻辑。 无论要使用目录叶来做什么,本指南中的其他步骤也以大致相同的方法实现。

下载 .nupkg

如果对下载目录中找到的包的 .nupkg 感兴趣,那么可以使用包内容资源。 但请注意,在目录中找到包和包在包内容资源中可用之间有短暂的延迟。 所以,如果在尝试下载目录中找到的包的 .nupkg 时遇到 404 Not Found,只需稍后进行重试。 由 GitHub 问题 NuGet/NuGetGallery#3455 跟踪此延迟的修复。

向前移动游标

成功处理目录项后,需要确定要保存的新游标值。 为了执行此操作,请找到处理的所有目录项的最大(按最新时间顺序) commitTimeStamp。 这是新的游标值。 将其保存到某些永久存储中,例如数据库、文件系统或 blob 存储。 当需要获取更多目录项时,只需通过从此永久存储中初始化游标值从第一步开始。

如果应用程序引发异常或错误,请勿向前移动游标。 向前移动游标意味着不再需要处理游标前的目录项。

如果某些原因导致处理目录叶的方式有错误,只需及时向后移动游标并允许代码重新处理旧的目录项。

C# 示例代码

因为目录是可以通过 HTTP 获得的 JSON 文档集,所以可以使用有 HTTP 客户端和 JSON 反序列化程序的任意编程语言与之交互。

C# 示例可在 NuGet/示例存储库中获得。

git clone https://github.com/NuGet/Samples.git

目录 SDK

使用目录的最简单方法是使用预发行版 .NET 目录 SDK 包 NuGet.Protocol.Catalog,通过以下 NuGet 包源 URL 在 Azure Artifacts 上可以获取此包:https://pkgs.dev.azure.com/dnceng/public/_packaging/nuget-build/nuget/v3/index.json

可以将此包安装到与 netstandard1.3 或更高版本(例如 .NET Framework 4.6)兼容的项目。

使用此包的示例可在 NuGet.Protocol.Catalog.Sample 项目中的 GitHub 上获得。

示例输出

2017-11-10T22:16:44.8689025+00:00: Found package details leaf for xSkrape.APIWrapper.REST 1.0.2.
2017-11-10T22:16:54.6972769+00:00: Found package details leaf for xSkrape.APIWrapper.REST 1.0.1.
2017-11-10T22:19:20.6385542+00:00: Found package details leaf for Platform.EnUnity 1.0.8.
...
2017-11-10T23:05:04.9695890+00:00: Found package details leaf for xSkrape.APIWrapper.Base 1.0.1.
2017-11-10T23:05:04.9695890+00:00: Found package details leaf for xSkrape.APIWrapper.Base 1.0.2.
2017-11-10T23:07:23.1303569+00:00: Found package details leaf for VeiculoX.Model 0.0.15.
Processing the catalog leafs failed. Retrying.
fail: NuGet.Protocol.Catalog.LoggerCatalogLeafProcessor[0]
      10 catalog commits have been processed. We will now simulate a failure.
warn: NuGet.Protocol.Catalog.CatalogProcessor[0]
      Failed to process leaf https://api.nuget.org/v3/catalog0/data/2017.11.10.23.07.23/veiculox.model.0.0.15.json (VeiculoX.Model 0.0.15, PackageDetails).
warn: NuGet.Protocol.Catalog.CatalogProcessor[0]
      13 out of 59 leaves were left incomplete due to a processing failure.
warn: NuGet.Protocol.Catalog.CatalogProcessor[0]
      1 out of 1 pages were left incomplete due to a processing failure.
2017-11-10T23:07:23.1303569+00:00: Found package details leaf for VeiculoX.Model 0.0.15.
2017-11-10T23:07:33.0212446+00:00: Found package details leaf for VeiculoX.Model 0.0.14.
2017-11-10T23:07:41.6621837+00:00: Found package details leaf for VeiculoX.Model 0.0.13.
2017-11-10T23:09:58.5728614+00:00: Found package details leaf for CreaSoft.Composition.Web.Extensions 1.1.0.
2017-11-10T23:09:58.5728614+00:00: Found package details leaf for DarkXaHTeP.Extensions.Configuration.Consul 0.0.4.
2017-11-10T23:09:58.5728614+00:00: Found package details leaf for xSkrape.APIWrapper.REST.Sample 1.0.3.
2017-11-10T23:10:09.0574930+00:00: Found package details leaf for Microsoft.VisualStudio.Imaging 15.4.27004.
2017-11-10T23:10:09.0574930+00:00: Found package details leaf for Microsoft.VisualStudio.Imaging.Interop.14.0.DesignTime 14.3.25407.
2017-11-10T23:10:09.0574930+00:00: Found package details leaf for Microsoft.VisualStudio.Language.Intellisense 15.4.27004.
2017-11-10T23:10:09.0574930+00:00: Found package details leaf for Microsoft.VisualStudio.Language.StandardClassification 15.4.27004.
2017-11-10T23:10:09.0574930+00:00: Found package details leaf for Microsoft.VisualStudio.ManagedInterfaces 8.0.50727.
2017-11-10T23:10:09.0574930+00:00: Found package details leaf for xSkrape.APIWrapper.REST.Sample 1.0.2.
2017-11-10T23:10:09.0574930+00:00: Found package details leaf for xSkrape.APIWrapper.REST.Sample 1.0.3.

最小示例

有关详细说明与目录交互的较少依赖项的示例,请参阅 CatalogReaderExample 示例项目。 项目面向 netcoreapp2.0 并依赖 NuGet.Protocol 4.4.0(适用于解析服务索引)和 Newtonsoft.Json 9.0.1(适用于 JSON 序列化)。

代码的主要逻辑在 Program.cs 文件中可见。

示例输出

No cursor found. Defaulting to 11/2/2017 9:41:28 PM.
Fetched catalog index https://api.nuget.org/v3/catalog0/index.json.
Fetched catalog page https://api.nuget.org/v3/catalog0/page2935.json.
Processing 69 catalog leaves.
11/2/2017 9:32:35 PM: DotVVM.Compiler.Light 1.1.7 (type is nuget:PackageDetails)
11/2/2017 9:32:35 PM: Momentum.Pm.Api 5.12.181-beta (type is nuget:PackageDetails)
11/2/2017 9:32:44 PM: Momentum.Pm.PortalApi 5.12.181-beta (type is nuget:PackageDetails)
11/2/2017 9:35:14 PM: Genesys.Extensions.Standard 3.17.11.40 (type is nuget:PackageDetails)
11/2/2017 9:35:14 PM: Genesys.Extensions.Core 3.17.11.40 (type is nuget:PackageDetails)
11/2/2017 9:35:14 PM: Halforbit.DataStores.FileStores.Serialization.Bond 1.0.4 (type is nuget:PackageDetails)
11/2/2017 9:35:14 PM: Halforbit.DataStores.FileStores.AmazonS3 1.0.4 (type is nuget:PackageDetails)
11/2/2017 9:35:14 PM: Halforbit.DataStores.DocumentStores.DocumentDb 1.0.6 (type is nuget:PackageDetails)
11/2/2017 9:35:14 PM: Halforbit.DataStores.FileStores.BlobStorage 1.0.5 (type is nuget:PackageDetails)
...
11/2/2017 10:23:54 PM: Cake.GitPackager 0.1.2 (type is nuget:PackageDetails)
11/2/2017 10:23:54 PM: UtilPack.NuGet 2.0.0 (type is nuget:PackageDetails)
11/2/2017 10:23:54 PM: UtilPack.NuGet.AssemblyLoading 2.0.0 (type is nuget:PackageDetails)
11/2/2017 10:26:26 PM: UtilPack.NuGet.Deployment 2.0.0 (type is nuget:PackageDetails)
11/2/2017 10:26:26 PM: UtilPack.NuGet.Common.MSBuild 2.0.0 (type is nuget:PackageDetails)
11/2/2017 10:26:36 PM: InstaClient 1.0.2 (type is nuget:PackageDetails)
11/2/2017 10:26:36 PM: SecureStrConvertor.VARUN_RUSIYA 1.0.0.5 (type is nuget:PackageDetails)
Writing cursor value: 11/2/2017 10:26:36 PM.