使用.Net Core编写命令行工具(CLI)的方法
命令行工具(CLI)
命令行工具(CLI)是在图形用户界面得到普及之前使用最为广泛的用户界面,它通常不支持鼠标,用户通过键盘输入指令,计算机接收到指令后,予以执行。
通常认为,命令行工具(CLI)没有图形用户界面(GUI)那么方便用户操作。因为,命令行工具的软件通常需要用户记忆操作的命令,但是,由于其本身的特点,命令行工具要较图形用户界面节约计算机系统的资源。在熟记命令的前提下,使用命令行工具往往要较使用图形用户界面的操作速度要快。所以,图形用户界面的操作系统中,都保留着可选的命令行工具。
另外,命令行工具(CLI)应该是一个开箱即用的工具,不需要安装任何依赖。
一些熟悉的CLI工具如下:
1.dotnetcli
2.vuecli
3.angularcli
4.awscli
5.azurecli
指令设计
本文将使用.NetCore(版本3.1.102)编写一个CLI工具,实现配置管理以及条目(item)管理(调用WebApi实现),详情如下:
框架说明
编写CLI使用的主要框架是CommandLineUtils,它主要有以下优势:
1.良好的语法设计
2.支持依赖注入
3.支持generichost
WebApi
提供api让cli调用,实现条目(item)的增删改查:
[Route("api/items")] [ApiController] publicclassItemsController:ControllerBase { privatereadonlyIMemoryCache_cache; privatereadonlystring_key="items"; publicItemsController(IMemoryCachememoryCache) { _cache=memoryCache; } [HttpGet] publicIActionResultList() { varitems=_cache.Get>(_key); returnOk(items); } [HttpGet("{id}")] publicIActionResultGet(stringid) { varitem=_cache.Get
>(_key).FirstOrDefault(n=>n.Id==id); returnOk(item); } [HttpPost] publicIActionResultCreate(ItemFormform) { varitems=_cache.Get
>(_key)??newList
- (); varitem=newItem { Id=Guid.NewGuid().ToString("N"), Name=form.Name, Age=form.Age }; items.Add(item); _cache.Set(_key,items); returnOk(item); } [HttpDelete("{id}")] publicIActionResultDelete(stringid) { varitems=_cache.Get
>(_key); varitem=items?.SingleOrDefault(n=>n.Id==id); if(item==null) { returnNotFound(); } items.Remove(item); _cache.Set(_key,items); returnOk(); } }
CLI
1.Program-函数入口
[HelpOption(Inherited=true)]//显示指令帮助,并且让子指令也继承此设置 [Command(Description="Atooltocommunicatewithwebapi"),//指令描述 Subcommand(typeof(ConfigCommand),typeof(ItemCommand))]//子指令 classProgram { publicstaticintMain(string[]args) { //配置依赖注入 varserviceCollection=newServiceCollection(); serviceCollection.AddSingleton(PhysicalConsole.Singleton); serviceCollection.AddSingleton(); serviceCollection.AddHttpClient (); varservices=serviceCollection.BuildServiceProvider(); varapp=newCommandLineApplication (); app.Conventions .UseDefaultConventions() .UseConstructorInjection(services); varconsole=(IConsole)services.GetService(typeof(IConsole)); try { returnapp.Execute(args); } catch(UnrecognizedCommandParsingExceptionex)//处理未定义指令 { console.WriteLine(ex.Message); return-1; } } //指令逻辑 privateintOnExecute(CommandLineApplicationapp,IConsoleconsole) { console.WriteLine("Pleasespecifyacommand."); app.ShowHelp(); return1; } }
2.ConfigCommand和ItemCommand-实现的功能比较简单,主要是指令描述以及指定对应的子指令
[Command("config",Description="Manageconfig"), Subcommand(typeof(GetCommand),typeof(SetCommand))] publicclassConfigCommand { privateintOnExecute(CommandLineApplicationapp,IConsoleconsole) { console.Error.WriteLine("Pleasesubmitasubcommand."); app.ShowHelp(); return1; } } [Command("item",Description="Manageitem"), Subcommand(typeof(CreateCommand),typeof(GetCommand),typeof(ListCommand),typeof(DeleteCommand))] publicclassItemCommand { privateintOnExecute(CommandLineApplicationapp,IConsoleconsole) { console.Error.WriteLine("Pleasesubmitasubcommand."); app.ShowHelp(); return1; } }
3.ConfigService-配置管理的具体实现,主要是文件读写
publicinterfaceIConfigService { voidSet(); ConfigGet(); } publicclassConfigService:IConfigService { privatereadonlyIConsole_console; privatereadonlystring_directoryName; privatereadonlystring_fileName; publicConfigService(IConsoleconsole) { _console=console; _directoryName=".api-cli"; _fileName="config.json"; } publicvoidSet() { vardirectory=Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),_directoryName); if(!Directory.Exists(directory)) { Directory.CreateDirectory(directory); } varconfig=newConfig { //弹出交互框,让用户输入,设置默认值为http://localhost:5000/ Endpoint=Prompt.GetString("Specifytheendpoint:","http://localhost:5000/") }; if(!config.Endpoint.EndsWith("/")) { config.Endpoint+="/"; } varfilePath=Path.Combine(directory,_fileName); using(varoutputFile=newStreamWriter(filePath,false,Encoding.UTF8)) { outputFile.WriteLine(JsonConvert.SerializeObject(config,Formatting.Indented)); } _console.WriteLine($"Configsavedin{filePath}."); } publicConfigGet() { varfilePath=Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),_directoryName,_fileName); if(File.Exists(filePath)) { varcontent=File.ReadAllText(filePath); try { varconfig=JsonConvert.DeserializeObject(content); returnconfig; } catch { _console.WriteLine("Theconfigisinvalid,pleaseuse'configset'commandtoresetone."); } } else { _console.WriteLine("Configisnotexisted,pleaseuse'configset'commandtosetone."); } returnnull; } }
4.ItemClient-调用WebApi的具体实现,使用HttpClientFactory的方式
publicinterfaceIItemClient { TaskCreate(ItemFormform); Task Get(stringid); Task List(); Task Delete(stringid); } publicclassItemClient:IItemClient { publicHttpClientClient{get;} publicItemClient(HttpClientclient,IConfigServiceconfigService) { varconfig=configService.Get(); if(config==null) { return; } client.BaseAddress=newUri(config.Endpoint); Client=client; } publicasyncTask Create(ItemFormform) { varcontent=newStringContent(JsonConvert.SerializeObject(form),Encoding.UTF8,"application/json"); varresult=awaitClient.PostAsync("/api/items",content); if(result.IsSuccessStatusCode) { varstream=awaitresult.Content.ReadAsStreamAsync(); varitem=Deserialize - (stream); return$"Itemcreated,info:{item}"; } return"Erroroccur,pleaseagainlater."; } publicasyncTask
Get(stringid) { varresult=awaitClient.GetAsync($"/api/items/{id}"); if(result.IsSuccessStatusCode) { varstream=awaitresult.Content.ReadAsStreamAsync(); varitem=Deserialize - (stream); varresponse=newStringBuilder(); response.AppendLine($"{"Id".PadRight(40,'')}{"Name".PadRight(20,'')}Age"); response.AppendLine($"{item.Id.PadRight(40,'')}{item.Name.PadRight(20,'')}{item.Age}"); returnresponse.ToString(); } return"Erroroccur,pleaseagainlater."; } publicasyncTask
List() { varresult=awaitClient.GetAsync($"/api/items"); if(result.IsSuccessStatusCode) { varstream=awaitresult.Content.ReadAsStreamAsync(); varitems=Deserialize >(stream); varresponse=newStringBuilder(); response.AppendLine($"{"Id".PadRight(40,'')}{"Name".PadRight(20,'')}Age"); if(items!=null&&items.Count>0) { foreach(variteminitems) { response.AppendLine($"{item.Id.PadRight(40,'')}{item.Name.PadRight(20,'')}{item.Age}"); } } returnresponse.ToString(); } return"Erroroccur,pleaseagainlater."; } publicasyncTask
Delete(stringid) { varresult=awaitClient.DeleteAsync($"/api/items/{id}"); if(result.IsSuccessStatusCode) { return$"Item{id}deleted."; } if(result.StatusCode==HttpStatusCode.NotFound) { return$"Item{id}notfound."; } return"Erroroccur,pleaseagainlater."; } privatestaticTDeserialize (Streamstream) { usingvarreader=newJsonTextReader(newStreamReader(stream)); varserializer=newJsonSerializer(); return(T)serializer.Deserialize(reader,typeof(T)); } }
如何发布
在项目文件中设置发布程序的名称(AssemblyName):
Exe netcoreapp3.1 api-cli
进入控制台程序目录:
cdsrc/NetCoreCLI
发布Linux使用版本:
dotnetpublish-cRelease-rlinux-x64/p:PublishSingleFile=true
发布Windows使用版本:
dotnetpublish-cRelease-rwin-x64/p:PublishSingleFile=true
发布MAC使用版本:
dotnetpublish-cRelease-rosx-x64/p:PublishSingleFile=true
使用示例
这里使用Linux作为示例环境。
1.以docker的方式启动webapi
2.虚拟机上没有安装.netcore的环境
3.把编译好的CLI工具拷贝到虚拟机上,授权并移动到PATH中(如果不移动,可以通过./api-cli的方式调用)
sudochmod+xapi-cli#授权 sudomv./api-cli/usr/local/bin/api-cli#移动到PATH
4.设置配置文件:api-cliconfigset
5.查看配置文件:api-cliconfigget
6.创建条目:api-cliitemcreate
7.条目列表:api-cliitemlist
8.获取条目:api-cliitemget
9.删除条目:api-cliitemdelete
10.指令帮助:api-cli-h,api-cliconfig-h,api-cliitem-h
11.错误指令:api-clixxx
源码地址
https://github.com/ErikXu/NetCoreCLI
参考资料
https://docs.microsoft.com/en-us/dotnet/core/rid-catalog
https://medium.com/swlh/build-a-command-line-interface-cli-program-with-net-core-428c4c85221
到此这篇关于使用.NetCore编写命令行工具(CLI)的方法的文章就介绍到这了,更多相关.NetCore命令行工具内容请搜索毛票票以前的文章或继续浏览下面的相关文章希望大家以后多多支持毛票票!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。