Como usar middleware em System.CommandLine

Importante

System.CommandLine está atualmente em PREVIEW, e esta documentação é para a versão 2.0 beta 4. Algumas informações estão relacionadas ao produto de pré-lançamento que pode ser substancialmente modificado antes de ser lançado. A Microsoft não faz garantias, de forma expressa ou implícita, em relação à informação aqui apresentada.

Este artigo explica como trabalhar com middleware em aplicativos de linha de comando criados com a System.CommandLine biblioteca. O uso de middleware é um tópico avançado que a maioria dos System.CommandLine usuários não precisará considerar.

Introdução ao middleware

Embora cada comando tenha um manipulador que será encaminhado com base na entrada, também há um mecanismo para curto-circuito ou alteração da entrada antes que System.CommandLine a lógica do aplicativo seja invocada. Entre a análise e a invocação, há uma cadeia de responsabilidade, que você pode personalizar. Uma série de recursos internos de System.CommandLine fazer uso dessa capacidade. É assim que o --help curto-circuito e --version as opções chamam o seu manipulador.

Cada chamada no pipeline pode tomar medidas com base no ParseResult e retornar antecipadamente, ou optar por chamar o próximo item no pipeline. O ParseResult pode até ser substituído durante esta fase. A última chamada na cadeia é o manipulador para o comando especificado.

Adicionar ao pipeline de middleware

Você pode adicionar uma chamada a esse pipeline chamando CommandLineBuilderExtensions.AddMiddleware. Aqui está um exemplo de código que habilita uma diretiva personalizada. Depois de criar um comando raiz chamado rootCommand, o código como de costume adiciona opções, argumentos e manipuladores. Em seguida, o middleware é adicionado:

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

No código anterior, o middleware escreve "Oi!" se a diretiva [just-say-hi] for encontrada no resultado da análise. Quando isso acontece, o manipulador normal do comando não é invocado. Ele não é invocado porque o middleware não chama o next delegado.

No exemplo, context é InvocationContext, uma estrutura singleton que atua como a "raiz" de todo o processo de manipulação de comandos. Esta é a estrutura mais poderosa em System.CommandLinetermos de capacidades. Existem dois usos principais para ele no middleware:

  • Ele fornece acesso ao BindingContext, Parser, Consolee HelpBuilder para recuperar dependências que um middleware requer para sua lógica personalizada.
  • Você pode definir as propriedades ou ExitCode para encerrar o InvocationResult processamento de comandos de forma curto-circuitada. Um exemplo é a opção, que é implementada --help desta forma.

Aqui está o programa completo, incluindo as diretivas necessárias 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}");
    }
}

Aqui está um exemplo de linha de comando e saída resultante do código anterior:

myapp [just-say-hi] --delay 42 --message "Hello world!"
Hi!

Consulte também

System.CommandLine Visão geral