Command-line tool (CLI)
Command-line tool (CLI) is a graphical user interface popularized the most widely used user interface before, it usually does not support a mouse, a user input through a keyboard command, after the computer receives the instruction to be executed.
Generally believed, command-line tool (CLI) no graphical user interface (GUI) so user-friendly operation. Because the software command-line tools typically require users to remember command of the operation, however, due to the characteristics of its own, the command-line resource to save the computer system than the graphical user interface tool. Under the premise of memorizing commands, use the command-line tool often than the use of a graphical user interface operating speed faster. Therefore, the operating system's graphical user interface, all retain the optional command-line tool.
In addition, the command line tool (CLI) should be used in a tool box i.e., no need to install any dependencies.
Some familiar CLI tool as follows:
1. dotnet cli
2. cli view
3. angular cli
4. aws cli
5. azure cli
Instruction Design
This article will use the .Net Core (version 3.1.102) to prepare a CLI tools, configuration management and entry (item) management (call WebApi realization), as follows:
Description Framework
The main framework written using CLI is CommandLineUtils , it mainly has the following advantages:
1. Good grammar design
2. Supports Dependency Injection
3. Support generic host
WebAPI
Providing cli api make calls to achieve entry (item) CRUD:
[Route("api/items")] [ApiController] public class ItemsController : ControllerBase { private readonly IMemoryCache _cache; private readonly string _key = "items"; public ItemsController(IMemoryCache memoryCache) { _cache = memoryCache; } [HttpGet] public IActionResult List() { var items = _cache.Get<List<Item>>(_key); return Ok(items); } [HttpGet("{id}")] public IActionResult Get(string id) { var item = _cache.Get<List<Item>>(_key).FirstOrDefault(n => n.Id == id); return Ok(item); } [HttpPost] public IActionResult Create(ItemForm form) { var items = _cache.Get<List<Item>>(_key) ?? new List<Item>(); var item = new Item { Id = Guid.NewGuid().ToString("N"), Name = form.Name, Age = form.Age }; items.Add(item); _cache.Set(_key, items); return Ok(item); } [HttpDelete("{id}")] public IActionResult Delete(string id) { var items = _cache.Get<List<Item>>(_key); var item = items?.SingleOrDefault(n => n.Id == id); if (item == null) { return NotFound(); } items.Remove(item); _cache.Set(_key, items); return Ok(); } }
CLI
1. Program - function entry
[HelpOption (Inherited = to true )] // display command help, command and let the child also inherits this setting [the Command (the Description = " A Tool to Communicate with Web API " ), // instructions describe the Subcommand ( typeof (ConfigCommand), typeof (ItemCommand))] // sub-instruction class Program { public static int the Main ( String [] args) {
// configure dependency injection var serviceCollection = new new serviceCollection (); serviceCollection.AddSingleton (PhysicalConsole.Singleton); serviceCollection.AddSingleton<IConfigService, ConfigService>(); serviceCollection.AddHttpClient<IItemClient, ItemClient>(); var services = serviceCollection.BuildServiceProvider(); var app = new CommandLineApplication<Program>(); app.Conventions .UseDefaultConventions() .UseConstructorInjection(services); var console = (IConsole)services.GetService(typeof(IConsole)); try { return app.Execute(args); } catch (UnrecognizedCommandParsingException ex) //处理未定义指令 { console.WriteLine(ex.Message); return -1; } }
//指令逻辑 private int OnExecute(CommandLineApplication app, IConsole console) { console.WriteLine("Please specify a command."); app.ShowHelp(); return 1; } }
2. ConfigCommand and ItemCommand - achieved relatively simple, mainly instruction description and the corresponding sub-instruction specifies
[Command("config", Description = "Manage config"), Subcommand(typeof(GetCommand), typeof(SetCommand))] public class ConfigCommand { private int OnExecute(CommandLineApplication app, IConsole console) { console.Error.WriteLine("Please submit a sub command."); app.ShowHelp(); return 1; } } [Command("item", Description = "Manage item"), Subcommand(typeof(CreateCommand), typeof(GetCommand), typeof(ListCommand), typeof(DeleteCommand))] public class ItemCommand { private int OnExecute(CommandLineApplication app, IConsole console) { console.Error.WriteLine("Please submit a sub command."); app.ShowHelp(); return 1; } }
3. ConfigService - specific configuration management, mainly reading and writing files
public interface IConfigService { void Set(); Config Get(); } public class ConfigService: IConfigService { private readonly IConsole _console; private readonly string _directoryName; private readonly string _fileName; public ConfigService(IConsole console) { _console = console; _directoryName = ".api-cli"; _fileName = "config.json " ; } public void the Set () { var Directory = Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.UserProfile), _directoryName); IF (! Directory.Exists (Directory)) { Directory.CreateDirectory (Directory) ; } var config = new new config {
// pop-up interactive box, which allows the user input, the default value set HTTP: // localhost: 5000 / Endpoint = Prompt.GetString ( " the Specify The Endpoint: " , " HTTP: // localhost: 5000 / ") }; if (!config.Endpoint.EndsWith("/")) { config.Endpoint += "/"; } var filePath = Path.Combine(directory, _fileName); using (var outputFile = new StreamWriter(filePath, false, Encoding.UTF8)) { outputFile.WriteLine(JsonConvert.SerializeObject(config, Formatting.Indented)); } _console.WriteLine($"Config saved in {filePath}."); } public Config Get() { var filePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), _directoryName, _fileName); if (File.Exists(filePath)) { var content = File.ReadAllText(filePath); try { var config = JsonConvert.DeserializeObject<Config>(content); return config; } catch { _console.WriteLine("The config is invalid, please use 'config set' command to reset one."); } } else { _console.WriteLine("Config is not existed, please use 'config set' command to set one."); } return null; } }
4. ItemClient - specific implementation calls Web Api, the use of HttpClientFactory way
public interface IItemClient { Task<string> Create(ItemForm form); Task<string> Get(string id); Task<string> List(); Task<string> Delete(string id); } public class ItemClient : IItemClient { public HttpClient Client { get; } public ItemClient(HttpClient client, IConfigService configService) { var config = configService.Get(); if (config == null) { return; } client.BaseAddress = new Uri(config.Endpoint); Client = client; } public async Task<string> Create(ItemForm form) { var content = new StringContent(JsonConvert.SerializeObject(form), Encoding.UTF8, "application/json"); var result = await Client.PostAsync("/api/items", content); if (result.IsSuccessStatusCode) { var stream = await result.Content.ReadAsStreamAsync(); var item = Deserialize<Item>(stream); return $"Item created, info:{item}"; } return "Error occur, please again later."; } public async Task<string> Get(string id) { var result = await Client.GetAsync($"/api/items/{id}"); if (result.IsSuccessStatusCode) { var stream = await result.Content.ReadAsStreamAsync(); var item = Deserialize<Item>(stream); var response = new StringBuilder(); response.AppendLine($"{"Id".PadRight(40, ' ')}{"Name".PadRight(20, ' ')}Age"); response.AppendLine($"{item.Id.PadRight(40, ' ')}{item.Name.PadRight(20, ' ')}{item.Age}"); return response.ToString(); } return "Error occur, please again later."; } public async Task<string> List() { var result = await Client.GetAsync($"/api/items"); if (result.IsSuccessStatusCode) { var stream = await result.Content.ReadAsStreamAsync(); var items = Deserialize<List<Item>>(stream); var response = new StringBuilder(); response.AppendLine($"{"Id".PadRight(40, ' ')}{"Name".PadRight(20, ' ')}Age"); if (items != null && items.Count > 0) { foreach (var item in items) { response.AppendLine($"{item.Id.PadRight(40, ' ')}{item.Name.PadRight(20, ' ')}{item.Age}"); } } return response.ToString(); } return "Error occur, please again later."; } public async Task<string> Delete(string id) { var result = await Client.DeleteAsync($"/api/items/{id}"); if (result.IsSuccessStatusCode) { return $"Item {id} deleted."; } if (result.StatusCode == HttpStatusCode.NotFound) { return $"Item {id} not found."; } return "Error occur, please again later."; } private static T Deserialize<T>(Stream stream) { using var reader = new JsonTextReader(new StreamReader(stream)); var serializer = new JsonSerializer(); return (T)serializer.Deserialize(reader, typeof(T)); } }
How to publish
Set the name of the publisher (AssemblyName) in the project file:
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<AssemblyName>api-cli</AssemblyName>
</PropertyGroup>
Into the console program directory:
cd src/NetCoreCLI
Linux uses the release version:
dotnet publish -c Release -r linux-x64 /p:PublishSingleFile=true
Release version of Windows use:
dotnet publish -c Release -r win-x64 /p:PublishSingleFile=true
Use MAC released version:
dotnet publish -c Release -r osx-x64 /p:PublishSingleFile=true
Examples of Use
Linux environment used here as an example.
1. docker way to start web api
.Net core of the environment is not installed on the virtual machine 2
3. A copy of the compiled CLI tool to a virtual machine, authorization and move to the PATH (if you do not move, you can call by ./api-cli way)
the API-X + the chmod the sudo CLI # authorizing
the sudo Music Videos. / API-CLI / usr / local / bin / CLI API-moved to the PATH #
4. Set Profile: api-cli config set
5. Review the configuration file: api-cli config get
6. Create entries: api-cli item create
7. The list of entries: api-cli item list
8. Get Entry: api-cli item get
9. To delete an entry: api-cli item delete
10. Instruction Help: api-cli -h, api-cli config -h, api-cli item -h
11. The error command: api-cli xxx
Source Address
https://github.com/ErikXu/NetCoreCLI
Reference material
https://medium.com/swlh/build-a-command-line-interface-cli-program-with-net-core-428c4c85221