使用.Net Core编写命令行工具(CLI)的方法

2020-03-16 16:01:28丽君

命令行工具(CLI)

  命令行工具(CLI)是在图形用户界面得到普及之前使用最为广泛的用户界面,它通常不支持鼠标,用户通过键盘输入指令,计算机接收到指令后,予以执行。

  通常认为,命令行工具(CLI)没有图形用户界面(GUI)那么方便用户操作。因为,命令行工具的软件通常需要用户记忆操作的命令,但是,由于其本身的特点,命令行工具要较图形用户界面节约计算机系统的资源。在熟记命令的前提下,使用命令行工具往往要较使用图形用户界面的操作速度要快。所以,图形用户界面的操作系统中,都保留着可选的命令行工具。

  另外,命令行工具(CLI)应该是一个开箱即用的工具,不需要安装任何依赖。

  一些熟悉的CLI工具如下:

  1. dotnet cli

  2. vue cli

  3.angular cli

  4. aws cli

  5.azure cli

指令设计

  本文将使用.Net Core(版本3.1.102)编写一个CLI工具,实现配置管理以及条目(item)管理(调用WebApi实现),详情如下:

  

框架说明

  编写CLI使用的主要框架是CommandLineUtils,它主要有以下优势:

  1. 良好的语法设计

  2. 支持依赖注入

  3. 支持generic host

WebApi

  提供api让cli调用,实现条目(item)的增删改查:

[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 - 函数入口

[HelpOption(Inherited = true)] //显示指令帮助,并且让子指令也继承此设置
[Command(Description = "A tool to communicate with web api"), //指令描述
 Subcommand(typeof(ConfigCommand), typeof(ItemCommand))] //子指令
class Program
{
  public static int Main(string[] args)
  {
    //配置依赖注入
    var serviceCollection = 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;
  }
}