dotnet / dotnet/command-line-api

Gracefully handle exceptions in command handlers

オープン
#1,655 コメント 5 件 リアクション 0 件 担当者 0 名 GitHub で見る
主要言語
C#
スター
3.7k
フォーク
428
PR マージ指標
30日以内にマージされた PR はありません

説明

When an exception occurs in a command handler the entire stack trace is barfed out into stderr and I'd like to not have that behavior for explicitly handled exceptions. Consider a handler that tries to read a file but the filename is not found. The standard libraries throw a FileNotFoundException which I'd like to catch and display just the message and not the entire stack trace. For unhandled exceptions I think the current behavior of the stack trace is fine since by definition it is unhandled.

I looks like I can get the invocation context and set the invocation result to a custom IInvocationResult. The problem is there doesn't appear to be (or I missed it 😅) an ErrorResult for setting a user-defined error that the handler failed. I made my own that is essentially a copy of `ParseErrorResult` but you can specify the message in the ctor.

```c#
public class ErrorResult : IInvocationResult
{
private readonly string _errorMessage;
private readonly int _errorExitCode;

public ErrorResult(string errorMessage, int errorExitCode = 1)
{
_errorMessage = errorMessage;
_errorExitCode = errorExitCode;
}

public void Apply(InvocationContext context)
{
context.Console.ResetTerminalForegroundColor();
context.Console.SetTerminalForegroundRed();

context.Console.Error.WriteLine(_errorMessage);
context.Console.Error.WriteLine();

context.ExitCode = _errorExitCode;

context.Console.ResetTerminalForegroundColor();
}
}
```

The main issue I ran into was that `ResetTerminalForegroundColor` and `ResetTerminalForegroundColor` are internal extension methods and those also rely on the internal `Platform` static class. I copied them into my code base for now.

Assuming those APIs were public, ErrorResult now works and you can use it like in the following trivial example:

```c#
void Handler(InvocationContext ctx, string filename)
{
try
{
Console.WriteLine(File.ReadAllText(filename));
}
catch (FileNotFoundException e)
{
ctx.InvocationResult = new ErrorResult(e.Message, 42);
}
}
```

Now when the handled exception occurs, it doesnt bubble up and kill the application but instead just prints (in red) to stderr.

I'm not familiar with the details of how System.CommandLine works. Do you think this is a valid approach for handling exceptions in a handler?

コントリビューションガイド

コントリビューションガイドを開く

調査の方向性

まず、InvocationContext、IInvocationResult、ParseErrorResult 周辺の invocation-result パスを読み、次に issue で言及されている内部のターミナルカラー用メソッドと Platform 依存関係を調査します。処理済み例外と未処理例外、stderr 出力、終了コードに対するプロジェクトでサポートされているアプローチが明確に定義され、検証されれば作業は完了です。

索引モデルが issue の本文から書いたものです。

評価

技術スタック
csharp
領域
cli
issue の種類
機能追加
難易度
5/5
見積もり時間
1週間以上
活発さ
停滞
明瞭さ
説明が足りない
初心者へのやさしさ
30/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。