HttpResponseExceptionをスローするか、Request.CreateErrorResponseを返しますか?


172

ASP.NET Web APIでの例外処理の記事を確認した後、例外をスローするタイミングとエラーレスポンスを返すタイミングについて少し混乱しています。メソッドがドメイン固有のモデルではなくHttpResponseMessage...を返すときに応答を変更できるかどうかも疑問に思っています。

だから、ここで要約すると、私の質問の後にケース番号付きのコードが続きます:

ご質問

ケース#1に関する質問

  1. HttpResponseMessageメッセージをカスタマイズできるように、具体的なドメインモデルではなく常に使用する必要がありますか?
  2. 具体的なドメインモデルを返す場合、メッセージをカスタマイズできますか?

ケース#2、3、4に関する質問

  1. 例外をスローするか、エラー応答を返す必要がありますか?答えが「依存する」である場合、どちらを使用するかについて状況/例を挙げられますか。
  2. 投げHttpResponseExceptionvs はどう違いRequest.CreateErrorResponseますか?クライアントへの出力は同じようです...
  3. HttpErrorエラーに応答メッセージを「ラップ」するために常に使用する必要がありますか(例外がスローされるか、エラー応答が返されるか)。

ケースサンプル

// CASE #1
public Customer Get(string id)
{
    var customer = _customerService.GetById(id);
    if (customer == null)
    {
        var notFoundResponse = new HttpResponseMessage(HttpStatusCode.NotFound);
        throw new HttpResponseException(notFoundResponse);
    }
    //var response = Request.CreateResponse(HttpStatusCode.OK, customer);
    //response.Content.Headers.Expires = new DateTimeOffset(DateTime.Now.AddSeconds(300));
    return customer;
}        

// CASE #2
public HttpResponseMessage Get(string id)
{
    var customer = _customerService.GetById(id);
    if (customer == null)
    {
        var notFoundResponse = new HttpResponseMessage(HttpStatusCode.NotFound);
        throw new HttpResponseException(notFoundResponse);
    }
    var response = Request.CreateResponse(HttpStatusCode.OK, customer);
    response.Content.Headers.Expires = new DateTimeOffset(DateTime.Now.AddSeconds(300));
    return response;
}

// CASE #3
public HttpResponseMessage Get(string id)
{
    var customer = _customerService.GetById(id);
    if (customer == null)
    {
        var message = String.Format("customer with id: {0} was not found", id);
        var errorResponse = Request.CreateErrorResponse(HttpStatusCode.NotFound, message);
        throw new HttpResponseException(errorResponse);
    }
    var response = Request.CreateResponse(HttpStatusCode.OK, customer);
    response.Content.Headers.Expires = new DateTimeOffset(DateTime.Now.AddSeconds(300));
    return response;
}

// CASE #4
public HttpResponseMessage Get(string id)
{
    var customer = _customerService.GetById(id);
    if (customer == null)
    {
        var message = String.Format("customer with id: {0} was not found", id);
        var httpError = new HttpError(message);
        return Request.CreateErrorResponse(HttpStatusCode.NotFound, httpError);
    }
    var response = Request.CreateResponse(HttpStatusCode.OK, customer);
    response.Content.Headers.Expires = new DateTimeOffset(DateTime.Now.AddSeconds(300));
    return response;
}

更新

ケース#2、3、4をさらに示すために、次のコードスニペットは、顧客が見つからないときに「発生する可能性がある」いくつかのオプションを強調しています...

if (customer == null)
{
    // which of these 4 options is the best strategy for Web API?

    // option 1 (throw)
    var notFoundMessage = new HttpResponseMessage(HttpStatusCode.NotFound);
    throw new HttpResponseException(notFoundMessage);

    // option 2 (throw w/ HttpError)
    var message = String.Format("Customer with id: {0} was not found", id);
    var httpError = new HttpError(message);
    var errorResponse = Request.CreateErrorResponse(HttpStatusCode.NotFound, httpError);
    throw new HttpResponseException(errorResponse);

    // option 3 (return)
    var message = String.Format("Customer with id: {0} was not found", id);
    return Request.CreateErrorResponse(HttpStatusCode.NotFound, message);
    // option 4 (return w/ HttpError)
    var message = String.Format("Customer with id: {0} was not found", id);
    var httpError = new HttpError(message);
    return Request.CreateErrorResponse(HttpStatusCode.NotFound, httpError);
}

6
@Mike Wassonリンクされた記事の著者として、あなたはどちらのアプローチをとりますか?
zam6ak 2012

回答:


102

私が取ったアプローチは、APIコントローラーアクションから例外をスローし、例外を処理し、アクション実行コンテキストに適切な応答を設定する例外フィルターを登録することです。

フィルターは、フィルターをグローバル構成に登録する前に、特定のタイプの例外のハンドラーを登録する手段を提供する、流れるようなインターフェースを公開します。

このフィルターを使用すると、コントローラーアクション全体に分散するのではなく、一元化された例外処理が可能になります。ただし、コントローラーアクション内で例外をキャッチし、その特定の例外の処理を集中化することが意味をなさない場合に特定の応答を返す場合があります。

フィルターの登録例:

GlobalConfiguration.Configuration.Filters.Add(
    new UnhandledExceptionFilterAttribute()
    .Register<KeyNotFoundException>(HttpStatusCode.NotFound)

    .Register<SecurityException>(HttpStatusCode.Forbidden)

    .Register<SqlException>(
        (exception, request) =>
        {
            var sqlException = exception as SqlException;

            if (sqlException.Number > 50000)
            {
                var response            = request.CreateResponse(HttpStatusCode.BadRequest);
                response.ReasonPhrase   = sqlException.Message.Replace(Environment.NewLine, String.Empty);

                return response;
            }
            else
            {
                return request.CreateResponse(HttpStatusCode.InternalServerError);
            }
        }
    )
);

UnhandledExceptionFilterAttributeクラス:

using System;
using System.Collections.Concurrent;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Web.Http.Filters;

namespace Sample
{
    /// <summary>
    /// Represents the an attribute that provides a filter for unhandled exceptions.
    /// </summary>
    public class UnhandledExceptionFilterAttribute : ExceptionFilterAttribute
    {
        #region UnhandledExceptionFilterAttribute()
        /// <summary>
        /// Initializes a new instance of the <see cref="UnhandledExceptionFilterAttribute"/> class.
        /// </summary>
        public UnhandledExceptionFilterAttribute() : base()
        {

        }
        #endregion

        #region DefaultHandler
        /// <summary>
        /// Gets a delegate method that returns an <see cref="HttpResponseMessage"/> 
        /// that describes the supplied exception.
        /// </summary>
        /// <value>
        /// A <see cref="Func{Exception, HttpRequestMessage, HttpResponseMessage}"/> delegate method that returns 
        /// an <see cref="HttpResponseMessage"/> that describes the supplied exception.
        /// </value>
        private static Func<Exception, HttpRequestMessage, HttpResponseMessage> DefaultHandler = (exception, request) =>
        {
            if(exception == null)
            {
                return null;
            }

            var response            = request.CreateResponse<string>(
                HttpStatusCode.InternalServerError, GetContentOf(exception)
            );
            response.ReasonPhrase   = exception.Message.Replace(Environment.NewLine, String.Empty);

            return response;
        };
        #endregion

        #region GetContentOf
        /// <summary>
        /// Gets a delegate method that extracts information from the specified exception.
        /// </summary>
        /// <value>
        /// A <see cref="Func{Exception, String}"/> delegate method that extracts information 
        /// from the specified exception.
        /// </value>
        private static Func<Exception, string> GetContentOf = (exception) =>
        {
            if (exception == null)
            {
                return String.Empty;
            }

            var result  = new StringBuilder();

            result.AppendLine(exception.Message);
            result.AppendLine();

            Exception innerException = exception.InnerException;
            while (innerException != null)
            {
                result.AppendLine(innerException.Message);
                result.AppendLine();
                innerException = innerException.InnerException;
            }

            #if DEBUG
            result.AppendLine(exception.StackTrace);
            #endif

            return result.ToString();
        };
        #endregion

        #region Handlers
        /// <summary>
        /// Gets the exception handlers registered with this filter.
        /// </summary>
        /// <value>
        /// A <see cref="ConcurrentDictionary{Type, Tuple}"/> collection that contains 
        /// the exception handlers registered with this filter.
        /// </value>
        protected ConcurrentDictionary<Type, Tuple<HttpStatusCode?, Func<Exception, HttpRequestMessage, HttpResponseMessage>>> Handlers
        {
            get
            {
                return _filterHandlers;
            }
        }
        private readonly ConcurrentDictionary<Type, Tuple<HttpStatusCode?, Func<Exception, HttpRequestMessage, HttpResponseMessage>>> _filterHandlers = new ConcurrentDictionary<Type, Tuple<HttpStatusCode?, Func<Exception, HttpRequestMessage, HttpResponseMessage>>>();
        #endregion

        #region OnException(HttpActionExecutedContext actionExecutedContext)
        /// <summary>
        /// Raises the exception event.
        /// </summary>
        /// <param name="actionExecutedContext">The context for the action.</param>
        public override void OnException(HttpActionExecutedContext actionExecutedContext)
        {
            if(actionExecutedContext == null || actionExecutedContext.Exception == null)
            {
                return;
            }

            var type    = actionExecutedContext.Exception.GetType();

            Tuple<HttpStatusCode?, Func<Exception, HttpRequestMessage, HttpResponseMessage>> registration = null;

            if (this.Handlers.TryGetValue(type, out registration))
            {
                var statusCode  = registration.Item1;
                var handler     = registration.Item2;

                var response    = handler(
                    actionExecutedContext.Exception.GetBaseException(), 
                    actionExecutedContext.Request
                );

                // Use registered status code if available
                if (statusCode.HasValue)
                {
                    response.StatusCode = statusCode.Value;
                }

                actionExecutedContext.Response  = response;
            }
            else
            {
                // If no exception handler registered for the exception type, fallback to default handler
                actionExecutedContext.Response  = DefaultHandler(
                    actionExecutedContext.Exception.GetBaseException(), actionExecutedContext.Request
                );
            }
        }
        #endregion

        #region Register<TException>(HttpStatusCode statusCode)
        /// <summary>
        /// Registers an exception handler that returns the specified status code for exceptions of type <typeparamref name="TException"/>.
        /// </summary>
        /// <typeparam name="TException">The type of exception to register a handler for.</typeparam>
        /// <param name="statusCode">The HTTP status code to return for exceptions of type <typeparamref name="TException"/>.</param>
        /// <returns>
        /// This <see cref="UnhandledExceptionFilterAttribute"/> after the exception handler has been added.
        /// </returns>
        public UnhandledExceptionFilterAttribute Register<TException>(HttpStatusCode statusCode) 
            where TException : Exception
        {

            var type    = typeof(TException);
            var item    = new Tuple<HttpStatusCode?, Func<Exception, HttpRequestMessage, HttpResponseMessage>>(
                statusCode, DefaultHandler
            );

            if (!this.Handlers.TryAdd(type, item))
            {
                Tuple<HttpStatusCode?, Func<Exception, HttpRequestMessage, HttpResponseMessage>> oldItem = null;

                if (this.Handlers.TryRemove(type, out oldItem))
                {
                    this.Handlers.TryAdd(type, item);
                }
            }

            return this;
        }
        #endregion

        #region Register<TException>(Func<Exception, HttpRequestMessage, HttpResponseMessage> handler)
        /// <summary>
        /// Registers the specified exception <paramref name="handler"/> for exceptions of type <typeparamref name="TException"/>.
        /// </summary>
        /// <typeparam name="TException">The type of exception to register the <paramref name="handler"/> for.</typeparam>
        /// <param name="handler">The exception handler responsible for exceptions of type <typeparamref name="TException"/>.</param>
        /// <returns>
        /// This <see cref="UnhandledExceptionFilterAttribute"/> after the exception <paramref name="handler"/> 
        /// has been added.
        /// </returns>
        /// <exception cref="ArgumentNullException">The <paramref name="handler"/> is <see langword="null"/>.</exception>
        public UnhandledExceptionFilterAttribute Register<TException>(Func<Exception, HttpRequestMessage, HttpResponseMessage> handler) 
            where TException : Exception
        {
            if(handler == null)
            {
              throw new ArgumentNullException("handler");
            }

            var type    = typeof(TException);
            var item    = new Tuple<HttpStatusCode?, Func<Exception, HttpRequestMessage, HttpResponseMessage>>(
                null, handler
            );

            if (!this.Handlers.TryAdd(type, item))
            {
                Tuple<HttpStatusCode?, Func<Exception, HttpRequestMessage, HttpResponseMessage>> oldItem = null;

                if (this.Handlers.TryRemove(type, out oldItem))
                {
                    this.Handlers.TryAdd(type, item);
                }
            }

            return this;
        }
        #endregion

        #region Unregister<TException>()
        /// <summary>
        /// Unregisters the exception handler for exceptions of type <typeparamref name="TException"/>.
        /// </summary>
        /// <typeparam name="TException">The type of exception to unregister handlers for.</typeparam>
        /// <returns>
        /// This <see cref="UnhandledExceptionFilterAttribute"/> after the exception handler 
        /// for exceptions of type <typeparamref name="TException"/> has been removed.
        /// </returns>
        public UnhandledExceptionFilterAttribute Unregister<TException>()
            where TException : Exception
        {
            Tuple<HttpStatusCode?, Func<Exception, HttpRequestMessage, HttpResponseMessage>> item = null;

            this.Handlers.TryRemove(typeof(TException), out item);

            return this;
        }
        #endregion
    }
}

ソースコードもここにあります


うわー!:)これは、小規模なプロジェクトでは少し多いかもしれませんが、それでも非常に良いです...ところで、なぜDefaultHandlerでCreateErrorResponseではなくCreateResponseを使うのですか?
zam6ak 2012

エラーの詳細(本文でシリアル化)を理由句から分離しようとしました。ただし、モデルバインディングの場合のように、より適切な場合はCreateErrorResponseを使用できます。
反抗

1
フィルターは1行のコードで登録できるため、ほとんどすべてのプロジェクトタイプに適していると思います。内部のNuGetフィードで公開されているクラスライブラリにフィルターがあるため、開発者は簡単に使用できます。
2012

警備員は何を使用していますか(自社製またはサードパーティ)。
zam6ak 2012

Hoemgrown、上の例ではその使用を削除しました。Guardクラスは、アサーションが満たされていることを防止または検証する一連の静的メソッドを提供します。実装が必要な場合は、codepaste.net / 5oc1if(Guard)およびcodepaste.net/nsrsei(DelegateInfo)を参照してください。
2012

23

HttpResponseMessageを返さず、代わりにエンティティ/モデルクラスを直接返す場合は、次のユーティリティ関数をコントローラーに追加すると便利です。

private void ThrowResponseException(HttpStatusCode statusCode, string message)
{
    var errorResponse = Request.CreateErrorResponse(statusCode, message);
    throw new HttpResponseException(errorResponse);
}

適切なステータスコードとメッセージを指定して呼び出すだけです


4
これは正解です。本文にはキーと値のペアとして「メッセージ」という形式が付属しています。これは通常、他のフレームワークや言語で行われている方法です
MobileMon

このアプローチについて少し質問があります。angularJSページで{{}}構文を使用してメッセージを消費しています。キャリッジリターンを残すと、メッセージにn \ r \として表示されます。それらを保存する正しい方法は何ですか?
直美

私はこのアプローチを試みました。私はそうthrow new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, "Invalid Request Format!"))しましたが、Fiddlerでは、ステータス500(400ではない)が表示されます。なぜだと思いますか?
サム

違いは、エラーの親関数です。以下は、アプリケーションの例外のThrowResponseExceptionです。しかし、例外をスローする本当の機能でなければなりません...
セルジュ

15

ケース#1

  1. 必ずしもそうとは限りませんが、パイプラインには応答を変更する他の場所(アクションフィルター、メッセージハンドラー)があります。
  2. 上記を参照してください。ただし、アクションがドメインモデルを返す場合、アクションの応答変更することはできません。

事例#2-4

  1. HttpResponseExceptionをスローする主な理由は次のとおりです。
    • ドメインモデルを返すが、エラーケースを処理する必要がある場合
    • エラーを例外として扱うことにより、コントローラーロジックを簡素化する
  2. これらは同等でなければなりません。HttpResponseExceptionは、HTTP応答として返されるHttpResponseMessageをカプセル化します。

    たとえば、ケース#2は次のように書き直すことができます。

    public HttpResponseMessage Get(string id)
    {
        HttpResponseMessage response;
        var customer = _customerService.GetById(id);
        if (customer == null)
        {
            response = new HttpResponseMessage(HttpStatusCode.NotFound);
        }
        else
        {
            response = Request.CreateResponse(HttpStatusCode.OK, customer);
            response.Content.Headers.Expires = new DateTimeOffset(DateTime.Now.AddSeconds(300));
        }
        return response;
    }

    ...しかし、コントローラーロジックがより複雑な場合、例外をスローするとコードフローが簡略化される可能性があります。

  3. HttpErrorは、レスポンスの本文に一貫した形式を提供し、JSON / XMLなどにシリアル化できますが、必須ではありません。たとえば、エンティティ本体を応答に含めたくない場合や、他の形式が必要な場合があります。


私が取ったアプローチは、APIコントローラーアクションから例外をスローすることで、例外を処理し、アクション実行コンテキストに適切な応答を設定する例外フィルターを登録しました。フィルターは「プラグイン可能」で、フィルターをグローバル構成に登録する前に、特定のタイプの例外のハンドラーを登録できます。これにより、コントローラー全体に例外を分散する代わりに、集中的な例外処理を行うことができます。
反対側

@Oppositional例外フィルターを共有する可能性はありますか?おそらく要点として、またはCodePasteなどのコード共有サイトで?
ペイジクック

@マイク・ワッソンは、「エラーを返す」が「例外を投げる」よりも一般的なアプローチだと思いますか?最終的に同じ結果になる可能性があることを機能的に理解していますが、なぜコントローラロジック全体をtry / catchに含め、エラー応答を適切に返すだけではないのでしょうか。
zam6ak 2012

15

HttpResponseExceptionを投げるか、エラーのHttpResponesMessageを返さないでください- 以外の意図はしている場合、要求終わるその正確な結果を

HttpResponseExceptionは他の例外と同じように処理さません。それらは例外フィルターで捕らえられません。それらは例外ハンドラーでキャッチされません。これらは、現在のコードの実行フローを終了するときにHttpResponseMessageを挿入する簡単な方法です。

コードがこの特別な未処理に依存するインフラストラクチャコードでない限り、HttpResponseExceptionタイプの使用は避けください

HttpResponseMessageも例外ではありません。現在のコードの実行フローは終了しません。それらは例外としてフィルタリングできません。例外として記録することはできません。それらは有効な結果を表します-500の応答でさえ「有効な非例外応答」です!


生活をシンプルにする:

例外/エラーのケースがある場合は、先に進んで通常の.NET例外、またはステータスコードなどの必要な「httpエラー/応答」プロパティを含む(HttpResponseExceptionから派生していない)カスタマイズされたアプリケーション例外タイプを通常の例外に従ってスローします。取り扱い

Exception Filters / Exception Handlers / Exception Loggersを使用して、これらの例外的なケースで適切なことを行います:ステータスコードの変更/追加?追跡識別子を追加しますか?スタックトレースを含めますか?ログ?

HttpResponseExceptionを回避することで、「例外的なケース」の処理が統一され、公開されたパイプラインの一部として処理できるようになります。たとえば、 'NotFound'を404に、 'ArgumentException'を400に、 'NullReference'を500に、アプリケーションレベルの例外を使用して簡単かつ均一に変換できます。ただし、拡張性により、エラーログなどの「基本」を提供できます。


2
ArgumentExceptionコントローラーのsが論理的に400になる理由を理解ArgumentExceptionしていますが、スタックのsの深いところはどうですか?これらを400に変換することは必ずしも適切ではありませんが、すべてArgumentExceptionのを400に一括変換するフィルターがある場合、それを回避する唯一の方法は、コントローラーで例外をキャッチし、他のものを再スローすることです。フィルターなどの例外処理を統一する目的を無効にする。
cmeeren 2017

@cmeeren私が扱っていたコードでは、そのほとんどが例外をキャッチし、それを各WebメソッドのHttpResponse [Exception / Message]に変換しました。どちらの場合も同じです。つまり、内部例外で何かを行うことが懸念される場合は、キャッチされた内部例外で「何か」を実行します。その結果、適切なラップ例外がスローされ、さらに上に処理されます。スタック。
user2864740 2017

@cmeeren更新後、ほとんどのWebエントリポイントは、特殊な派生(適切な応答コードを持つ、または適切な応答コードにマップされているHttpResponseException)を使用エラーにスローします。ユニフォームハンドラーは、いくつかのスタックインスペクションを実行して(厄介ですが、注意して動作します)、例外がどのレベルで発生したかを判断できます。より洗練された処理がないケースの99%をカバーします。または、内部エラーに対して500で応答します。HttpResponseExceptionの要点は、有用なパイプライン処理をバイパスすることです。
user2864740 2017

9

、またはその他のエラーステータスコードのHttpResponseException代わりに使用するもう1つのケースはResponse.CreateResponse(HttpStatusCode.NotFound)、アクションフィルタにトランザクションがあり、クライアントにエラー応答を返すときにトランザクションをロールバックする場合です。

を使用しResponse.CreateResponseてもトランザクションはロールバックされませんが、例外をスローするとロールバックされます。


3

私の経験では、webapi 2メソッドでHttpResponseMessageを返す代わりにHttpResponseExceptionをスローした場合、呼び出しがIIS Expressにすぐに行われるとタイムアウトするか、200が返されますが、htmlエラーが発生することを指摘しておきます応答。これをテストする最も簡単な方法は、HttpResponseExceptionをスローするメソッドに対して$ .ajax呼び出しを行い、ajaxのerrorCallBackで別のメソッドまたは単純なhttpページをすぐに呼び出すことです。imediate呼び出しが失敗することがわかります。エラーコールバックにブレークポイントまたはsettimeout()を追加すると、2番目の呼び出しが1〜2秒遅れてサーバーに回復する時間が与えられ、正しく機能します。

更新:奇妙なAjax接続タイムアウトの根本的な原因は、ajax呼び出しが十分に速く行われた場合に、同じtcp接続が使用されることです。HttpResonseMessageを返すか、ブラウザのajax呼び出しに返されるHTTPResponseExceptionをスローすることで、401エラーイーサを発生させていました。しかし、その呼び出しとともに、Startup.Auth.vbでapp.UserCookieAuthenticationが有効になっているためにMSがObject Not Foundエラーを返していたため、応答をインターセプトしてリダイレクトを追加しようとしましたが、オブジェクトのインスタンスではなくオブジェクトでエラーが発生しました。このエラーはhtmlでしたが、事後の応答に追加されたため、ajax呼び出しが十分に速く行われ、同じTCP接続が使用された場合にのみ、ブラウザーに返され、次の呼び出しの前に追加されました。何らかの理由でChromeがタイムアウトしただけで、フィドラーはjsonとhtmのミックスのせいでパッキングしましたが、Firefoxは実際のエラーを返しました。奇妙ですが、パケットスニファまたはFirefoxがこれを追跡する唯一の方法でした。

また、自動ヘルプを生成するためにWeb APIヘルプを使用していて、HttpResponseMessageを返す場合は、

[System.Web.Http.Description.ResponseType(typeof(CustomReturnedType))] 

ヘルプが正しく生成されるように、メソッドの属性。その後

return Request.CreateResponse<CustomReturnedType>(objCustomeReturnedType) 

またはエラー時

return Request.CreateErrorResponse( System.Net.HttpStatusCode.InternalServerError, new Exception("An Error Ocurred"));

これが、HttpResponseExceptionをスローした直後にランダムなタイムアウトまたはサーバーが利用できない可能性がある誰かを助けることを願っています。

また、HttpResponseExceptionを返すと、返されるエラーが単一ページのアプリでAuthTokenを更新する必要がある場合に便利な未処理の例外でVisual Studioが中断しないという追加の利点があります。

更新:IIS Expressのタイムアウトに関するステートメントを撤回しています。これは、Ajax 1.8が$ .ajax()を返し、$。ajax。()。then()を返すため、クライアント側のajaxコールバックの誤りでした。どちらもpromiseを返しますが、同じchained promiseではありませんthen()は新しいpromiseを返し、実行の順序が間違っていました。したがって、then()のプロミスが完了すると、スクリプトのタイムアウトが発生しました。奇妙な問題はあるが、IISのエクスプレス問題はキーボードと椅子の間の問題ではない。



0

エラー状況では、クライアントがハッピーパスオブジェクトの代わりに要求した形式で、特定のエラー詳細クラスを返したいと思っていました。

コントローラメソッドにドメイン固有のハッピーパスオブジェクトを返させ、それ以外の場合は例外をスローさせたい。

私が抱えていた問題は、HttpResponseExceptionコンストラクターがドメインオブジェクトを許可しないことでした。

これは私が最終的に思いついたものです

public ProviderCollection GetProviders(string providerName)
{
   try
   {
      return _providerPresenter.GetProviders(providerName);
   }
   catch (BadInputValidationException badInputValidationException)
   {
     throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.BadRequest,
                                          badInputValidationException.Result));
   }
}

Resultエラーの詳細を含むクラスですProviderCollectionが、私のハッピーパスの結果です。


0

私は反対の答えが好き

とにかく、継承された例外をキャッチする方法が必要でしたが、その解決策は私のニーズをすべて満たしていません。

だから私は彼がOnExceptionを処理する方法を変更してしまい、これは私のバージョンです

public override void OnException(HttpActionExecutedContext actionExecutedContext) {
   if (actionExecutedContext == null || actionExecutedContext.Exception == null) {
      return;
   }

   var type = actionExecutedContext.Exception.GetType();

   Tuple<HttpStatusCode?, Func<Exception, HttpRequestMessage, HttpResponseMessage>> registration = null;

   if (!this.Handlers.TryGetValue(type, out registration)) {
      //tento di vedere se ho registrato qualche eccezione che eredita dal tipo di eccezione sollevata (in ordine di registrazione)
      foreach (var item in this.Handlers.Keys) {
         if (type.IsSubclassOf(item)) {
            registration = this.Handlers[item];
            break;
         }
      }
   }

   //se ho trovato un tipo compatibile, uso la sua gestione
   if (registration != null) {
      var statusCode = registration.Item1;
      var handler = registration.Item2;

      var response = handler(
         actionExecutedContext.Exception.GetBaseException(),
         actionExecutedContext.Request
      );

      // Use registered status code if available
      if (statusCode.HasValue) {
         response.StatusCode = statusCode.Value;
      }

      actionExecutedContext.Response = response;
   }
   else {
      // If no exception handler registered for the exception type, fallback to default handler
      actionExecutedContext.Response = DefaultHandler(actionExecutedContext.Exception.GetBaseException(), actionExecutedContext.Request
      );
   }
}

コアはこのループで、例外タイプが登録されたタイプのサブクラスであるかどうかを確認します。

foreach (var item in this.Handlers.Keys) {
    if (type.IsSubclassOf(item)) {
        registration = this.Handlers[item];
        break;
    }
}

my2cents

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.