使用 nuget server 的 API 来实现搜索安装 nuget 包

使用 nuget server 的 API 来实现搜索安装 nuget 包

Intro

nuget 现在几乎是 dotnet 开发不可缺少的一部分了,还没有用过 nuget 的就有点落后时代了,还不快用起来

nuget 是 dotnet 里的包管理机制,类似于前端的 npm ,php 的 composer,java 里的 maven ...

nuget 定义了一套关于 nuget server 的规范,使得用户可以自己实现一个 nuget server

也正是这些规范,使得我们可以根据这些规范来实现 nuget server 的包管理的功能,今天主要介绍一下,根据 nuget server 的 api 规范使用原始的 HTTP 请求来实现 nuget 包的搜索和使用 nuget 提供的客户端 SDK 来实现 nuget 包的搜索和下载

Nuget Server Api

Nuget 协议介绍

nuget 的协议有好几个版本,目前主要用的是 v3,开源的 nuget server Baget 也实现了基于 nuget protocal v3 的规范

我们添加 nuget 源的时候会指定一个 source url,类似 https://api.nuget.org/v3/index.json 这样的,这通常被称为 Service Index,是一个 nuget 源的入口,有点类似于 Identity Server 里的发现文档,通过这个地址可以获取到一系列的资源的地址

有一些资源是协议规范里定义的必须要实现的,有一些是可选的,具体参考官方文档,以后随着版本变化,可能会有差异,目前 nuget.org 提供的资源如下:

Nuget.org 提供了两种搜索的方式,

一个是 SearchQuery,会根据包名称、 tag、description 等信息去匹配关键词,

一个是 SearchAutocomplete 根据包名称的前缀去匹配包的名称

获取某个 nuget 包的版本信息,可以使用 PackageBaseAddress 来获取

ServiceIndex 返回的信息示例如下:

返回的信息会有一个 resources 的数组,会包含各种不同类型的资源,对应的 @id 就是调用这种类型的API要用到的地址,下面来看一个搜索的示例

在每个 API 的文档页面可以看到会使用的 @type,调用这个 API 的时候应该使用这些 @type 对应的资源

这里的 @id 就是上面的 resource 对应的 @id

参数说明:

q 搜索时所用的关键词,

skip/take 用来分页显示查询结果,

prelease 用来指定是否限制预览版的 package,true 包含预览版的 nuget 包,false 只包含已经正式发布的 nuget 包

semVerLevel 是用来指定包的语义版本

The semVerLevel query parameter is used to opt-in to SemVer 2.0.0 packages. If this query parameter is excluded, only packages with SemVer 1.0.0 compatible versions will be returned (with the standard NuGet versioning caveats, such as version strings with 4 integer pieces). If semVerLevel=2.0.0 is provided, both SemVer 1.0.0 and SemVer 2.0.0 compatible packages will be returned. See the SemVer 2.0.0 support for nuget.org for more information

packageType 用来指定 nuget 包的类型,目前支持的类型包括 Dependency(默认)项目依赖项,DotnetTool(dotnetcore 2.1 引入的 dotnet cli tool),Template (dotnet new 用) 自定义的项目模板

其他的 API 可以自行参考官方文档:https://docs.microsoft.com/en-us/nuget/api/service-index

Packages

SearchQuery 返回的信息比较多而且可能并不准确,适用于不清楚包的名称的时候使用,如果知道 nuget 包的名称(PackageId) ,可以使用 SearchAutocomplete 来搜索,这样更精准,返回的信息也更简单,只有匹配的 package 名称

通过原始 api 调用的方式实现 nuget 包的搜索

using var httpClient = new HttpClient(new NoProxyHttpClientHandler());
// loadServiceIndex
var serviceIndexResponse = await httpClient.GetStringAsync(NugetServiceIndex);
var serviceIndexObject = JObject.Parse(serviceIndexResponse);var keyword = "weihanli";//https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource
var queryEndpoint = serviceIndexObject["resources"].First(x => x["@type"].Value<string>() == "SearchQueryService")["@id"].Value<string>();
var queryUrl = $"{queryEndpoint}?q={keyword}&skip=0&take=5&prerelease=false&semVerLevel=2.0.0";
var queryResponse = await httpClient.GetStringAsync(queryUrl);
Console.WriteLine($"formatted queryResponse:");
Console.WriteLine($"{JObject.Parse(queryResponse).ToString(Formatting.Indented)}");// https://docs.microsoft.com/en-us/nuget/api/search-autocomplete-service-resource
var autoCompleteQueryEndpoint = serviceIndexObject["resources"].First(x => x["@type"].Value<string>() == "SearchAutocompleteService")["@id"].Value<string>();
var autoCompleteQueryUrl = $"{autoCompleteQueryEndpoint}?q={keyword}&skip=0&take=5&prerelease=false&semVerLevel=2.0.0";
var autoCompleteQueryResponse = await httpClient.GetStringAsync(autoCompleteQueryUrl);
Console.WriteLine($"formatted autoCompleteQueryResponse:");
Console.WriteLine($"{JObject.Parse(autoCompleteQueryResponse).ToString(Formatting.Indented)}");

output 示例:

Query 返回示例

Autocomplete 返回结果

从上面我们可以看到 Query 接口返回了很多的信息,Autocomplete 接口只返回了 package 的名称,返回的信息更为简洁,所以如果可以使用 Autocomplete 的方式就尽可能使用 Autocomplete 的方式

Package Versions

前面我们提到了可以使用 PackageBaseAddress 来查询某个 nuget 包的版本信息,文档地址:https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource,来看一下示例:

using (var httpClient = new HttpClient(new NoProxyHttpClientHandler()))
{// loadServiceIndexvar serviceIndexResponse = await httpClient.GetStringAsync(NugetServiceIndex);var serviceIndexObject = JObject.Parse(serviceIndexResponse);// https://docs.microsoft.com/en-us/nuget/api/package-base-address-resourcevar packageVersionsEndpoint = serviceIndexObject["resources"].First(x => x["@type"].Value<string>() == "PackageBaseAddress/3.0.0")["@id"].Value<string>();var packageVersionsQueryUrl = $"{packageVersionsEndpoint}/dbtool.core/index.json";var packageVersionsQueryResponse = await httpClient.GetStringAsync(packageVersionsQueryUrl);Console.WriteLine("DbTool.Core versions:");Console.WriteLine(JObject.Parse(packageVersionsQueryResponse).ToString(Formatting.Indented));
}

output 示例:

注:api 地址中的 packageId 要转小写

Nuget Client SDK

除了上面的根据 api 自己调用,我们还可以使用 nuget 提供的客户端 sdk 实现上述功能,这里就不详细介绍了,有需要可能查阅官方文档:https://docs.microsoft.com/en-us/nuget/reference/nuget-client-sdk

下面给出一个使用示例:

var packageId = "WeihanLi.Common";
var packageVersion = new NuGetVersion("1.0.38");var logger = NullLogger.Instance;
var cache = new SourceCacheContext();
// 在 SDK 的概念里,每一个 nuget 源是一个 repository
var repository = Repository.Factory.GetCoreV3("https://api.nuget.org/v3/index.json");// SearchQuery
{var resource = await repository.GetResourceAsync<PackageSearchResource>();var searchFilter = new SearchFilter(includePrerelease: false);var results = await resource.SearchAsync("weihanli",searchFilter,skip: 0,take: 20,logger,CancellationToken.None);foreach (var result in results){Console.WriteLine($"Found package {result.Identity.Id} {result.Identity.Version}");}
}
// SearchAutoComplete
{var autoCompleteResource = await repository.GetResourceAsync<AutoCompleteResource>();var packages =await autoCompleteResource.IdStartsWith("WeihanLi", false, logger, CancellationToken.None);foreach (var package in packages){Console.WriteLine($"Found Package {package}");}
}
//
{// get package versionsvar findPackageByIdResource = await repository.GetResourceAsync<FindPackageByIdResource>();var versions = await findPackageByIdResource.GetAllVersionsAsync(packageId,cache,logger,CancellationToken.None);foreach (var version in versions){Console.WriteLine($"Found version {version}");}
}

More

你可以使用 nuget sdk 方便的实现 nuget 包的下载安装,内部实现了签名校验等,这样就可以把本地不存在的 nuget 包下载到本地了,

实现示例:

{var pkgDownloadContext = new PackageDownloadContext(cache);var downloadRes = await repository.GetResourceAsync<DownloadResource>();var downloadResult = await RetryHelper.TryInvokeAsync(async () =>await downloadRes.GetDownloadResourceResultAsync(new PackageIdentity(packageId, packageVersion),pkgDownloadContext,@"C:\Users\liweihan\.nuget\packages", // nuget globalPackagesFolderlogger,CancellationToken.None), r => true);Console.WriteLine(downloadResult.Status.ToString());
}

最后提供一个解析 nuget globalPackagesFolder 的两种思路:

一个是前面有篇文章介绍的,有个默认的配置文件,然后就是默认的配置,写了一个解析的方法示例,支持 Windows/Linux/Mac:

{var packagesFolder = Environment.GetEnvironmentVariable("NUGET_PACKAGES");if (string.IsNullOrEmpty(packagesFolder)){// Nuget globalPackagesFolder resolveif (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)){var defaultConfigFilePath =$@"{Environment.GetEnvironmentVariable("APPDATA")}\NuGet\NuGet.Config";if (File.Exists(defaultConfigFilePath)){var doc = new XmlDocument();doc.Load(defaultConfigFilePath);var node = doc.SelectSingleNode("/configuration/config/add[@key='globalPackagesFolder']");if (node != null){packagesFolder = node.Attributes["value"]?.Value;}}if (string.IsNullOrEmpty(packagesFolder)){packagesFolder = $@"{Environment.GetEnvironmentVariable("USERPROFILE")}\.nuget\packages";}}else{var defaultConfigFilePath =$@"{Environment.GetEnvironmentVariable("HOME")}/.config/NuGet/NuGet.Config";if (File.Exists(defaultConfigFilePath)){var doc = new XmlDocument();doc.Load(defaultConfigFilePath);var node = doc.SelectSingleNode("/configuration/config/add[@key='globalPackagesFolder']");if (node != null){packagesFolder = node.Attributes["value"]?.Value;}}if (string.IsNullOrEmpty(packagesFolder)){defaultConfigFilePath = $@"{Environment.GetEnvironmentVariable("HOME")}/.nuget/NuGet/NuGet.Config";if (File.Exists(defaultConfigFilePath)){var doc = new XmlDocument();doc.Load(defaultConfigFilePath);var node = doc.SelectSingleNode("/configuration/config/add[@key='globalPackagesFolder']");if (node != null){packagesFolder = node.Value;}}}if (string.IsNullOrEmpty(packagesFolder)){packagesFolder = $@"{Environment.GetEnvironmentVariable("HOME")}/.nuget/packages";}}}Console.WriteLine($"globalPackagesFolder: {packagesFolder}");
}

另一个是可以根据 nuget 提供的一个命令查询 nuget locals global-packages -l,通过命令输出获取

Reference

  • https://github.com/WeihanLi/SamplesInPractice/blob/master/NugetSample/RawApiSample.cs

  • https://github.com/WeihanLi/SamplesInPractice/blob/master/NugetSample/NugetClientSdkSample.cs

  • https://docs.microsoft.com/en-us/nuget/reference/nuget-client-sdk

  • https://docs.microsoft.com/en-us/nuget/create-packages/set-package-type

  • https://docs.microsoft.com/en-us/nuget/api/overview

  • https://docs.microsoft.com/en-us/nuget/api/search-autocomplete-service-resource

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/309083.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Kubernetes:通过自动化节省IT预算的4种方法

导语大多数CIO和IT领导者都熟悉反复执行的任务“事半功倍”。在许多组织中&#xff0c;这都是现实&#xff0c;在各种其他因素&#xff08;例如宏观经济状况&#xff09;的影响下&#xff0c;这种趋势往往会起伏不定。正文有时&#xff0c;“用更少的钱做更多的事”可能会更准确…

C# 9.0 终于来了, Top-level programs 和 Partial Methods 两大新特性探究

一&#xff1a;背景1. 讲故事.NET 5 终于在 6月25日 发布了第六个预览版&#xff0c;随之而来的是更多的新特性加入到了 C# 9 Preview 中&#xff0c;这个系列也可以继续往下写了&#xff0c;废话不多说&#xff0c;今天来看一下 Top-level programs 和 Extending Partial Meth…

4位BCD计数器设计

状态转换: 输出函数: 4位BCD计数器设计:

[Mvp.Blazor] 集成Ids4,实现统一授权认证

&#xff08;又一个客户端集成了IdentityServer4&#xff09;还是很开心的&#xff0c;目前已经有六个开源项目都集成到了Ids4认证中心了。1、Blazor系列文章回顾书接上文&#xff0c;关于Blazor学习呢&#xff0c;我也发了几篇文章了&#xff0c;我一般写东西都喜欢偏实战&…

重学ASP.NET Core 中的标记帮助程序

标记帮助程序是什么标记帮助程序使服务器端代码可以在 Razor 文件中参与创建和呈现 HTML 元素。 例如&#xff0c;内置的 ImageTagHelper 可以将版本号追加到图片名称。 每当图片发生变化时&#xff0c;服务器都会为图像生成一个新的唯一版本号&#xff0c;因此客户端总能获得…

小型数字系统---运动码表设计

运动码表设计中码表控制器是最难的&#xff0c;所以主要介绍如何设计码表控制器。 我们先给这个时序电路设计状态以及编码 先设计状态转换函数 首先画出状态图: (其中我们让其一到000状态就立马变为001状态&#xff0c;110状态存储完数据立马变成100状态) 根据状态图&#…

用 aforge.net 小试一下验证码识别

今天来小玩一下 aforge.net 套用官方的话就是一个专门为开发者和研究者基于C#框架设计的&#xff0c;这个框架提供了不同的类库和关于类库的资源&#xff0c;还有很多应用程序例子&#xff0c;包括计算机视觉与人工智能&#xff0c;图像处理&#xff0c;神经网络&#xff0c;遗…

国标转区位码电路设计及汉字显示

区位码国际码-2020H&#xff0c;由于采用加法器实现&#xff0c;因此用补码形式进行表示&#xff0c;区位码 国际码FFFF-2020H0001H国际码dfe0。 从网上找下机内码转化的软件&#xff0c;将一段自选文字输入该软件&#xff0c;进行十六进制的机内码转化。由于一个汉字是由四个十…

【WPF】DataGrid多表头的样式设计

需求在使用WPF开发时&#xff0c;使用DataGrid列表显示数据时&#xff0c;有些字段可以进行分组显示&#xff0c;用于更好的表达它们之间存在的某种关系&#xff0c;因此就考虑到要对DataGrid的表头进行扩展&#xff0c;可以显示多行表头&#xff0c;让这些有关联的字段内容显示…

如何使用ABP进行软件开发之基础概览

ABP框架简述1&#xff09;简介在.NET众多的技术框架中&#xff0c;ABP框架&#xff08;本系列中指aspnetboilerplate项目&#xff09;以其独特的魅力吸引了一群优秀开发者广泛的使用。在该框架的赋能之下&#xff0c;开发者可根据需求通过官方网站【https://aspnetboilerplate.…

模2加法,模2减法,模2除法

十进制的除法&#xff0c;大家都会做&#xff1a;列个竖式&#xff0c;商&#xff0c;写在上面&#xff0c;上个几&#xff0c;再用被除数减去积&#xff0c;求得余数…。 二进制的除法&#xff0c;和十进制的计算方法相同&#xff0c;也要列出个竖式计算。 二进制的除法&…

《Unit Testing》2.1 伦敦学派如何做隔离

针对单元测试的定义&#xff0c;主要有两种看法&#xff1a;经典学派。经典学派之所以经典&#xff0c;是因为这原本就是人们做单元测试和测试驱动开发的方式伦敦学派。伦敦学派扎根于伦敦的编程社区。单元测试的定义单元测试有很多定义&#xff0c;但是所有的定义都有三个重要…

C++实现数组模拟链表(实现链表的增删功能)

代码如下: #include <iostream> using namespace std; const int N 100;struct Node {int data;int next; };class ArrayList { private:Node node[N];int maxSize;//数组容量int idx;//接下来要插入的结点的下标int len;//链表长度public:void initList(){node[0].nex…

Gartner:容器采用将迅速增长,但不会很快有利可图

导语容器的未来可期&#xff0c;到2024年&#xff0c;所有应用程序中的15&#xff05;将在容器中运行&#xff0c;而这一数据今天仅为5&#xff05;&#xff0c;但当前变现还比较难。正文近日&#xff0c;Gartner公司发表其首次为软件容器管理软件和服务市场做的预测。Gartner说…

【Azure Show】|第三期 人工智能大咖与您分享!嘉宾陈海平胡浩陈堰平

欢迎来到Azure Show!Azure ShowHello,大家好&#xff0c;又来到新的一期的Azure Show!本期是人工智能专场&#xff0c;我们邀请到微软Data&AI的解决方案架构师陈堰平&#xff0c;Tensorflow.NET 作者陈海平&#xff0c;还有微软人工智能方向最有价值专家胡浩和大家分享人工…

深入探究ASP.NET Core异常处理中间件

前言全局异常处理是我们编程过程中不可或缺的重要环节。有了全局异常处理机制给我们带来了很多便捷&#xff0c;首先我们不用满屏幕处理程序可能出现的异常&#xff0c;其次我们可以对异常进行统一的处理&#xff0c;比如收集异常信息或者返回统一的格式等等。ASP.NET Core为我…