Written using .Net Core command-line tool (CLI)

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://docs.microsoft.com/en-us/dotnet/core/rid-catalog#using-rids](https://docs.microsoft.com/en-us/dotnet/core/rid-catalog#using-rids

  https://medium.com/swlh/build-a-command-line-interface-cli-program-with-net-core-428c4c85221

Guess you like

Origin www.cnblogs.com/Erik_Xu/p/12374379.html