Verwenden von Middleware in System.CommandLine

Wichtig

System.CommandLine befindet sich derzeit in der VORSCHAU, und diese Dokumentation gilt für Version 2.0 Beta 4. Einige Informationen beziehen sich auf Vorabversionen des Produkts, die vor dem Release ggf. grundlegend überarbeitet werden. Microsoft übernimmt hinsichtlich der hier bereitgestellten Informationen keine Gewährleistungen, seien sie ausdrücklich oder konkludent.

In diesem Artikel wird erläutert, wie Sie mit Middleware in Befehlszeilen-Apps arbeiten, die mit der System.CommandLine-Bibliothek erstellt werden. Die Verwendung von Middleware ist ein erweitertes Thema, das die meisten System.CommandLine-Benutzer*innen nicht berücksichtigen müssen.

Einführung in Middleware

Jeder Befehl verfügt zwar über einen Handler, der basierend auf der Eingabe zu System.CommandLine weitergeleitet wird, aber es gibt auch einen Mechanismus zum Kurzschließen oder Ändern der Eingabe, bevor Ihre Anwendungslogik aufgerufen wird. Zwischen der Analyse und dem Aufruf gibt es eine Verantwortungskette, die Sie anpassen können. Eine Reihe integrierter Funktionen von System.CommandLine nutzen diese Funktionalität. So rufen die Optionen --help und --version Kurzschlussaufrufe an Ihren Handler auf.

Jeder Aufruf in der Pipeline kann eine Aktion basierend auf dem ParseResult ausführen und frühzeitig zurückgeben oder das nächste Element in der Pipeline aufrufen. Der ParseResult kann sogar während dieser Phase ersetzt werden. Der letzte Aufruf in der Kette ist der Handler für den angegebenen Befehl.

Hinzufügen zur Middlewarepipeline

Sie können einen Aufruf dieser Pipeline hinzufügen, indem Sie CommandLineBuilderExtensions.AddMiddleware aufrufen. Hier sehen Sie ein Beispiel für Code, der eine benutzerdefinierte Anweisung aktiviert. Nach dem Erstellen eines Stammbefehls mit dem Namen rootCommand fügt der Code wie üblich Optionen, Argumente und Handler hinzu. Dann wird Middleware hinzugefügt:

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

Im vorherigen Code schreibt Middleware „Hallo!“, wenn die Anweisung [just-say-hi] im Analyseergebnis gefunden wird. In diesem Fall wird der normale Handler des Befehls nicht aufgerufen. Sie wird nicht aufgerufen, weil Middleware den next-Delegaten nicht aufruft.

Im Beispiel ist contextInvocationContext, eine Singletonstruktur die als „Stamm“ des gesamten Befehlsverarbeitungsprozesses fungiert. Dies ist die leistungsstärkste Struktur in System.CommandLine, was die Funktionen angeht. Es gibt zwei Standard-Verwendungsmöglichkeiten in Middleware:

  • Sie bietet Zugriff auf BindingContext, Parser, Console und HelpBuilder zum Abrufen von Abhängigkeiten, die eine Middleware-Instanz für ihre benutzerdefinierte Logik benötigt.
  • Sie können die Eigenschaften InvocationResult oder ExitCode festlegen, um die Befehlsverarbeitung in einem Kurzschluss zu beenden. Ein Beispiel ist die --help-Option, die auf diese Weise implementiert wird.

Hier sehen Sie das vollständige Programm, einschließlich der erforderlichen using-Anweisungen.

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

Hier sehen Sie ein Beispiel für die Befehlszeile und die daraus resultierende Ausgabe des vorangegangenen Codes:

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

Weitere Informationen

System.CommandLine – Übersicht