web-dev-qa-db-ja.com

例外とエラー応答のマッピング

REST service/ gRPC service /whatever service)を公開するプログラムを想像してください。これは、いくつかのサードパーティライブラリを使用します。これらのライブラリは、何か問題が発生した場合に例外をスローします。たとえば、ユーザーが許可されていないものにアクセスしようとした場合。

ここでの問題は、リクエストが正しいかどうか、またはユーザーに十分な権限があるかどうかを事前に確認できないことです。そのため、サードパーティのライブラリにリクエストを送信し、例外を取得します。

これらの例外をトップレベルでキャッチして、このようなステータスコードにマッピングするのは良い習慣ですか?

var statusCode = HttpStatusCode.InternalServerError;
if (ex is ArgumentException || ex is ResourceDoesntExistException)
    statusCode = HttpStatusCode.BadRequest;
else if (ex is UnauthorizedAccessException)
    statusCode = HttpStatusCode.Forbidden;
else if (ex is CustomerNotFoundException)
    statusCode = HttpStatusCode.NotFound;

次に、エラーオブジェクトを含むステータスコードを返します。

return new ErrorResponse
{
    Error = new ErrorDescription
    {
        Message = ex.Message,
        Type = ex.GetType().Name
    }
};

このアプローチを使用することで得られる利点:

  • プログラムでは、RESTサービスまたはSOAPサービスまたはその他のものを公開するかどうかを気にする必要はありません。単に例外をスローすると、後で正しく処理されました。
  • 例外が意味のある名前と情報を持っている限り、何かがうまくいかない場合、呼び出し元は十分で正しい情報を取得します。
  • ロギングは一元化することもできます。未処理の例外はすべてログに記録され、そこでエラー応答に変換されます。

短所:

  • 少し「ハック」を感じます。

これを行う正しい方法は何ですか?


編集:リクエストされたので、エラーマッピングが非常に似ている、gRPCサービスに対して私が行うことは次のとおりです。

private RpcException GenerateRpcException(Exception ex)
{
    var statusCode = StatusCode.Unknown;

    if (ex is ArgumentException || ex is ResourceDoesntExistException)
        statusCode = StatusCode.InvalidArgument;
    else if (ex is UnauthorizedAccessException)
        statusCode = StatusCode.PermissionDenied;
    else if (ex is CustomerNotFoundException)
        statusCode = StatusCode.NotFound;

    var status = new Status(statusCode, ex.Message);
    var exceptionName = ex.GetType().Name;

    var rpcMetadata = new Metadata
    {
        { "exception_name", exceptionName }
    };
    return new RpcException(status, rpcMetadata);
}
7
CommonGuy

例外の正規化とそれらの適切な処理に問題はありません。クラスErrorResponseは、RESTサービスへの呼び出しを処理するための適切なアプローチのようです。

ここに改善の余地があるものがある場合、それは静的エラー処理アプローチです。可能性のある例外/エラーコードのリストは明日簡単に増加する可能性があり、他の場合はこの速度で巨大なものになる可能性があります。

2つのメソッドbool CanHandle(Exception)およびErrorResponse Handle(Exception)ErrorManagerインターフェイスのリストを保持するErrorHandlerクラスを作成することを検討してください。

例外が発生すると、ErrorManagerは、例外を含むErrorHandlerを呼び出して、登録されている各CanHandleを愚かにチェックします。それがtrueを返すと、チェックが停止し、ErrorHandlerがハンドラーのHandleメソッドを呼び出さないようにします。次に、考えられるErrorHandlerごとにErrorResponseを作成します(例外ごとに作成する必要はありません)。

この動的なアプローチにより、プログラムでErrorHandlerクラスを登録したり、必要に応じてデータベースからロードしたりできるようになります。さらに重要なことは、プログラムでErrorHandlerクラスを登録することを決定した場合、これらのハンドラーのロード方法を変更することを決定したとしても、プログラムに影響をほとんどまたはまったく与えずにそれを行うことができます。本質的には、エラー処理の将来性を保証します。

賢明な言葉:例外のハンドラーが見つからない場合の計画を立ててください。これはおそらくHttpStatusCode.InternalServerError。例外を「常に処理する」ハンドラーを作成することもできますが、技術的にはknownの可能性のある例外の管理について話しているため、予期しない例外は不明な例外であり、ほとんどの場合、他のエラーと同じように扱われるだけでなく、フラグを立てるべきです。

6
Neil

私はあなたのアプローチが好きですが、エラー応答の本文でクライアントに送信されるものも翻訳します。あなたはサードパーティのライブラリを制御できないと言っているので、サードパーティのライブラリが機密情報をクライアントに漏洩するかどうかもわかりません。申し訳ありませんが安全です。内部ログに例外を記録し、クライアントがエラーについて知る必要のある最小限の情報のみを伝達します。

1
user285148