溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何使用.Net Core編寫命令行工具

發(fā)布時(shí)間:2021-06-11 15:19:09 來(lái)源:億速云 閱讀:275 作者:小新 欄目:開發(fā)技術(shù)

這篇文章將為大家詳細(xì)講解有關(guān)如何使用.Net Core編寫命令行工具,小編覺得挺實(shí)用的,因此分享給大家做個(gè)參考,希望大家閱讀完這篇文章后可以有所收獲。

命令行工具(CLI)

  命令行工具(CLI)是在圖形用戶界面得到普及之前使用最為廣泛的用戶界面,它通常不支持鼠標(biāo),用戶通過(guò)鍵盤輸入指令,計(jì)算機(jī)接收到指令后,予以執(zhí)行。

  通常認(rèn)為,命令行工具(CLI)沒有圖形用戶界面(GUI)那么方便用戶操作。因?yàn)椋钚泄ぞ叩能浖ǔP枰脩粲洃洸僮鞯拿?,但是,由于其本身的特點(diǎn),命令行工具要較圖形用戶界面節(jié)約計(jì)算機(jī)系統(tǒng)的資源。在熟記命令的前提下,使用命令行工具往往要較使用圖形用戶界面的操作速度要快。所以,圖形用戶界面的操作系統(tǒng)中,都保留著可選的命令行工具。

  另外,命令行工具(CLI)應(yīng)該是一個(gè)開箱即用的工具,不需要安裝任何依賴。

  一些熟悉的CLI工具如下:

  1. dotnet cli

  2. vue cli

  3.angular cli

  4. aws cli

  5.azure cli

指令設(shè)計(jì)

  本文將使用.Net Core(版本3.1.102)編寫一個(gè)CLI工具,實(shí)現(xiàn)配置管理以及條目(item)管理(調(diào)用WebApi實(shí)現(xiàn)),詳情如下:

  如何使用.Net Core編寫命令行工具

框架說(shuō)明

  編寫CLI使用的主要框架是CommandLineUtils,它主要有以下優(yōu)勢(shì):

  1. 良好的語(yǔ)法設(shè)計(jì)

  2. 支持依賴注入

  3. 支持generic host

WebApi

  提供api讓cli調(diào)用,實(shí)現(xiàn)條目(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 - 函數(shù)入口

[HelpOption(Inherited = true)] //顯示指令幫助,并且讓子指令也繼承此設(shè)置
[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;
  }
}

  2. ConfigCommand和ItemCommand - 實(shí)現(xiàn)的功能比較簡(jiǎn)單,主要是指令描述以及指定對(duì)應(yīng)的子指令

[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 - 配置管理的具體實(shí)現(xiàn),主要是文件讀寫

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 Set()
  {
    var directory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), _directoryName);
    if (!Directory.Exists(directory))
    {
      Directory.CreateDirectory(directory);
    }

    var config = new Config
    {
      //彈出交互框,讓用戶輸入,設(shè)置默認(rèn)值為http://localhost:5000/
      Endpoint = Prompt.GetString("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 - 調(diào)用Web Api的具體實(shí)現(xiàn),使用HttpClientFactory的方式

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));
  }
}

如何發(fā)布

  在項(xiàng)目文件中設(shè)置發(fā)布程序的名稱(AssemblyName):

 <PropertyGroup>
   <OutputType>Exe</OutputType>
   <TargetFramework>netcoreapp3.1</TargetFramework>
   <AssemblyName>api-cli</AssemblyName>
  </PropertyGroup>

  進(jìn)入控制臺(tái)程序目錄:

cd src/NetCoreCLI

  發(fā)布Linux使用版本:

 dotnet publish -c Release -r linux-x64 /p:PublishSingleFile=true

  發(fā)布Windows使用版本:

dotnet publish -c Release -r win-x64 /p:PublishSingleFile=true

  發(fā)布MAC使用版本:

 dotnet publish -c Release -r osx-x64 /p:PublishSingleFile=true

使用示例

  這里使用Linux作為示例環(huán)境。

  1. 以docker的方式啟動(dòng)web api

如何使用.Net Core編寫命令行工具

  2. 虛擬機(jī)上沒有安裝.net core的環(huán)境

如何使用.Net Core編寫命令行工具

  3. 把編譯好的CLI工具拷貝到虛擬機(jī)上,授權(quán)并移動(dòng)到PATH中(如果不移動(dòng),可以通過(guò)./api-cli的方式調(diào)用)

sudo chmod +x api-cli #授權(quán)
sudo mv ./api-cli /usr/local/bin/api-cli #移動(dòng)到PATH

  4. 設(shè)置配置文件:api-cli config set

如何使用.Net Core編寫命令行工具

  5. 查看配置文件:api-cli config get

如何使用.Net Core編寫命令行工具

  6. 創(chuàng)建條目:api-cli item create

如何使用.Net Core編寫命令行工具

  7. 條目列表:api-cli item list

如何使用.Net Core編寫命令行工具

  8. 獲取條目:api-cli item get

如何使用.Net Core編寫命令行工具

  9. 刪除條目:api-cli item delete

如何使用.Net Core編寫命令行工具

  10. 指令幫助:api-cli -h, api-cli config -h, api-cli item -h

如何使用.Net Core編寫命令行工具

如何使用.Net Core編寫命令行工具

如何使用.Net Core編寫命令行工具

  11. 錯(cuò)誤指令:api-cli xxx

如何使用.Net Core編寫命令行工具

關(guān)于“如何使用.Net Core編寫命令行工具”這篇文章就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,使各位可以學(xué)到更多知識(shí),如果覺得文章不錯(cuò),請(qǐng)把它分享出去讓更多的人看到。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI