如何在 System.CommandLine 中使用中间件
重要
System.CommandLine
目前为预览版,本文档适用于版本 2.0 beta 4。
一些信息与预发行产品相关,相应产品在发行之前可能会进行重大修改。 对于此处提供的信息,Microsoft 不作任何明示或暗示的担保。
本文介绍如何在通过 System.CommandLine
库构建的命令行应用中使用中间件。 中间件的使用是一个高级主题,大多数 System.CommandLine
用户不需要考虑它。
中间件简介
虽然每个命令都有一个处理程序,System.CommandLine
将基于输入路由到该处理程序,但还有一种机制可在调用应用程序逻辑之前短路或更改输入。 在分析和调用之间,有一个可自定义的责任链。 System.CommandLine
的许多内置功能都利用了此功能。 这就是 --help
和 --version
选项短路调用处理程序的方式。
管道中的每个调用都可以基于 ParseResult 采取措施并提前返回,或者选择调用管道中的下一项。 甚至可以在此阶段替换 ParseResult
。 链中的最后一个调用是指定命令的处理程序。
添加到中间件管道
可以通过调用 CommandLineBuilderExtensions.AddMiddleware 添加对此管道的调用。 以下是启用自定义指令的代码示例。 创建名为 rootCommand
的根命令后,代码像往常一样添加选项、自变量和处理程序。 然后添加中间件:
var commandLineBuilder = new CommandLineBuilder(rootCommand);
commandLineBuilder.AddMiddleware(async (context, next) =>
{
if (context.ParseResult.Directives.Contains("just-say-hi"))
{
context.Console.WriteLine("Hi!");
}
else
{
await next(context);
}
});
commandLineBuilder.UseDefaults();
var parser = commandLineBuilder.Build();
await parser.InvokeAsync(args);
在上述代码中,如果在分析结果中找到了指令 [just-say-hi]
,则中间件会写下“Hi!”。 发生这种情况时,不会调用命令的正常处理程序。 不会调用它是因为中间件不调用 next
委托。
在示例中,context
是 InvocationContext,一个单一实例结构,充当整个命令处理过程的“根”。 就功能而言,这是 System.CommandLine
中最强大的结构。 它在中间件中有两个主要用途:
- 它提供对 BindingContext、Parser、Console和 HelpBuilder 的访问,以检索中间件在其自定义逻辑中所需的依赖关系。
- 可以设置 InvocationResult 或 ExitCode 属性,以短路方式终止命令处理。 一个示例是
--help
选项,它以此方式实现。
下面是完整的程序,包括必需的 using
指令。
using System.CommandLine;
using System.CommandLine.Builder;
using System.CommandLine.Parsing;
class Program
{
static async Task Main(string[] args)
{
var delayOption = new Option<int>("--delay");
var messageOption = new Option<string>("--message");
var rootCommand = new RootCommand("Middleware example");
rootCommand.Add(delayOption);
rootCommand.Add(messageOption);
rootCommand.SetHandler((delayOptionValue, messageOptionValue) =>
{
DoRootCommand(delayOptionValue, messageOptionValue);
},
delayOption, messageOption);
var commandLineBuilder = new CommandLineBuilder(rootCommand);
commandLineBuilder.AddMiddleware(async (context, next) =>
{
if (context.ParseResult.Directives.Contains("just-say-hi"))
{
context.Console.WriteLine("Hi!");
}
else
{
await next(context);
}
});
commandLineBuilder.UseDefaults();
var parser = commandLineBuilder.Build();
await parser.InvokeAsync(args);
}
public static void DoRootCommand(int delay, string message)
{
Console.WriteLine($"--delay = {delay}");
Console.WriteLine($"--message = {message}");
}
}
下面是上述代码的示例命令行和生成的输出:
myapp [just-say-hi] --delay 42 --message "Hello world!"
Hi!