Compartilhar via


Como usar middleware no System.CommandLine

Importante

Atualmente, System.CommandLine está em VERSÃO PRÉVIA, e essa documentação é para a versão 2.0 beta 4. Algumas informações estão relacionadas a produtos de pré-lançamento que poderão ser substancialmente modificados antes do lançamento. A Microsoft não oferece garantias, expressas ou implícitas, das informações aqui fornecidas.

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

Introdução ao middleware

Embora cada comando tenha um manipulador para o qual System.CommandLine roteará com base na entrada, há também um mecanismo para curto-circuitar ou alterar a entrada antes que a lógica do aplicativo seja invocada. Entre a análise e a invocação, há uma cadeia de responsabilidade, que você pode personalizar. Vários recursos integrados de System.CommandLine fazem uso desse recurso. É assim que as opções --help e --version fazem chamadas de curto-circuito para seu manipulador.

Cada chamada no pipeline pode realizar uma ação com base em ParseResult e retornar antecipadamente ou optar por chamar o próximo item no pipeline. ParseResult pode até ser substituído durante esta fase. A última chamada na cadeia é o manipulador do comando especificado.

Adicionar ao pipeline de middleware

Você pode adicionar uma chamada a este 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 normalmente 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 delegado next.

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.CommandLine, em termos de recursos. Há dois usos principais para ele no middleware:

  • Ele fornece acesso a BindingContext, Parser, Console e HelpBuilder para recuperar dependências que um middleware requer para sua lógica customizada.
  • Você pode definir as propriedades InvocationResult ou ExitCode para encerrar o processamento do comando em curto-circuito. Um exemplo é a opção --help, que é implementada dessa maneira.

Aqui está o programa completo, incluindo as diretivas using obrigatórias.

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 a saída resultante do código anterior:

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

Confira também

System.CommandLine overview