使用 nuget server 的 API 来实现搜索安装 nuget 包
使用 nuget server 的 API 來實(shí)現(xiàn)搜索安裝 nuget 包
Intro
nuget 現(xiàn)在幾乎是 dotnet 開發(fā)不可缺少的一部分了,還沒有用過 nuget 的就有點(diǎn)落后時(shí)代了,還不快用起來
nuget 是 dotnet 里的包管理機(jī)制,類似于前端的 npm ,php 的 composer,java 里的 maven ...
nuget 定義了一套關(guān)于 nuget server 的規(guī)范,使得用戶可以自己實(shí)現(xiàn)一個(gè) nuget server
也正是這些規(guī)范,使得我們可以根據(jù)這些規(guī)范來實(shí)現(xiàn) nuget server 的包管理的功能,今天主要介紹一下,根據(jù) nuget server 的 api 規(guī)范使用原始的 HTTP 請(qǐng)求來實(shí)現(xiàn) nuget 包的搜索和使用 nuget 提供的客戶端 SDK 來實(shí)現(xiàn) nuget 包的搜索和下載
Nuget Server Api
Nuget 協(xié)議介紹
nuget 的協(xié)議有好幾個(gè)版本,目前主要用的是 v3,開源的 nuget server Baget 也實(shí)現(xiàn)了基于 nuget protocal v3 的規(guī)范
我們添加 nuget 源的時(shí)候會(huì)指定一個(gè) source url,類似 https://api.nuget.org/v3/index.json 這樣的,這通常被稱為 Service Index,是一個(gè) nuget 源的入口,有點(diǎn)類似于 Identity Server 里的發(fā)現(xiàn)文檔,通過這個(gè)地址可以獲取到一系列的資源的地址
有一些資源是協(xié)議規(guī)范里定義的必須要實(shí)現(xiàn)的,有一些是可選的,具體參考官方文檔,以后隨著版本變化,可能會(huì)有差異,目前 nuget.org 提供的資源如下:
Nuget.org 提供了兩種搜索的方式,
一個(gè)是 SearchQuery,會(huì)根據(jù)包名稱、 tag、description 等信息去匹配關(guān)鍵詞,
一個(gè)是 SearchAutocomplete 根據(jù)包名稱的前綴去匹配包的名稱
獲取某個(gè) nuget 包的版本信息,可以使用 PackageBaseAddress 來獲取
ServiceIndex 返回的信息示例如下:
返回的信息會(huì)有一個(gè) resources 的數(shù)組,會(huì)包含各種不同類型的資源,對(duì)應(yīng)的 @id 就是調(diào)用這種類型的API要用到的地址,下面來看一個(gè)搜索的示例
在每個(gè) API 的文檔頁面可以看到會(huì)使用的 @type,調(diào)用這個(gè) API 的時(shí)候應(yīng)該使用這些 @type 對(duì)應(yīng)的資源
這里的 @id 就是上面的 resource 對(duì)應(yīng)的 @id,
參數(shù)說明:
q 搜索時(shí)所用的關(guān)鍵詞,
skip/take 用來分頁顯示查詢結(jié)果,
prelease 用來指定是否限制預(yù)覽版的 package,true 包含預(yù)覽版的 nuget 包,false 只包含已經(jīng)正式發(fā)布的 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(默認(rèn))項(xiàng)目依賴項(xiàng),DotnetTool(dotnetcore 2.1 引入的 dotnet cli tool),Template (dotnet new 用) 自定義的項(xiàng)目模板
其他的 API 可以自行參考官方文檔:https://docs.microsoft.com/en-us/nuget/api/service-index
Packages
SearchQuery 返回的信息比較多而且可能并不準(zhǔn)確,適用于不清楚包的名稱的時(shí)候使用,如果知道 nuget 包的名稱(PackageId) ,可以使用 SearchAutocomplete 來搜索,這樣更精準(zhǔn),返回的信息也更簡單,只有匹配的 package 名稱
通過原始 api 調(diào)用的方式實(shí)現(xiàn) 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 返回結(jié)果
從上面我們可以看到 Query 接口返回了很多的信息,Autocomplete 接口只返回了 package 的名稱,返回的信息更為簡潔,所以如果可以使用 Autocomplete 的方式就盡可能使用 Autocomplete 的方式
Package Versions
前面我們提到了可以使用 PackageBaseAddress 來查詢某個(gè) 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 要轉(zhuǎn)小寫
Nuget Client SDK
除了上面的根據(jù) api 自己調(diào)用,我們還可以使用 nuget 提供的客戶端 sdk 實(shí)現(xiàn)上述功能,這里就不詳細(xì)介紹了,有需要可能查閱官方文檔:https://docs.microsoft.com/en-us/nuget/reference/nuget-client-sdk
下面給出一個(gè)使用示例:
var?packageId?=?"WeihanLi.Common"; var?packageVersion?=?new?NuGetVersion("1.0.38");var?logger?=?NullLogger.Instance; var?cache?=?new?SourceCacheContext(); //?在?SDK?的概念里,每一個(gè)?nuget?源是一個(gè)?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 方便的實(shí)現(xiàn) nuget 包的下載安裝,內(nèi)部實(shí)現(xiàn)了簽名校驗(yàn)等,這樣就可以把本地不存在的 nuget 包下載到本地了,
實(shí)現(xiàn)示例:
{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()); }最后提供一個(gè)解析 nuget globalPackagesFolder 的兩種思路:
一個(gè)是前面有篇文章介紹的,有個(gè)默認(rèn)的配置文件,然后就是默認(rèn)的配置,寫了一個(gè)解析的方法示例,支持 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}"); }另一個(gè)是可以根據(jù) nuget 提供的一個(gè)命令查詢 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
總結(jié)
以上是生活随笔為你收集整理的使用 nuget server 的 API 来实现搜索安装 nuget 包的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 关于技术文章“标题党”一事我想说两句
- 下一篇: Kubernetes:通过自动化节省IT