如何在 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 委托。

在示例中,contextInvocationContext,一个单一实例结构,充当整个命令处理过程的“根”。 就功能而言,这是 System.CommandLine 中最强大的结构。 它在中间件中有两个主要用途:

下面是完整的程序,包括必需的 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!

请参阅

System.CommandLine 概述