2016-05-25 10 views
9

特定の$httpエラーシナリオをグローバルに傍受して、コントローラがエラーを処理しないようにしたい。私はHTTPインターセプターが私の必要とするものだと思いますが、私のコントローラーにもエラーを処理させる方法はわかりません。

私はこのようなコントローラがあります。

function HomeController($location, $http) { 
    activate(); 

    function activate() { 
     $http.get('non-existent-location') 
      .then(function activateOk(response) { 
       alert('Everything is ok'); 
      }) 
      .catch(function activateError(error) { 
       alert('An error happened'); 
      }); 
    } 
} 

そして、このようなHTTPのインターセプター:

function HttpInterceptor($q, $location) { 
    var service = { 
     responseError: responseError 
    }; 

    return service; 

    function responseError(rejection) { 
     if (rejection.status === 404) { 
      $location.path('/error'); 
     } 
     return $q.reject(rejection); 
    } 
} 

これは動作しますが、ブラウザは「/エラー」パスにリダイレクトと同じくらいでは。しかし、HomeControllerにある約束はでもが実行され、私はそれを望んでいません。

HomeControllerは404エラーを無視するように記述できますが、それは保守できません。 HttpInterceptorを変更して500個のエラーを処理するとしたら、HomeController(そしてその後に追加された可能性がある他のコントローラも同様に$httpを使用)を変更する必要があります。より洗練されたソリューションはありますか?

+0

私はこれを疑問に思いましたが、それを忘れました。 D –

答えて

17
をお試しください

オプション1 - ブレーク/ ca ncel promise chain

HttpInterceptorの小さな変更は、プロミスチェーンを中断/キャンセルすることができます。つまり、コントローラーのactivateOkまたはactivateErrorが実行されません。

function HttpInterceptor($q, $location) { 
    var service = { 
     responseError: responseError 
    }; 

    return service; 

    function responseError(rejection) { 
     if (rejection.status === 404) { 
      $location.path('/error'); 
      return $q(function() { return null; }) 
     } 
     return $q.reject(rejection); 
    } 
} 

ラインreturn $q(function() { return null; })は、約束を破棄します。

"ok"かどうかは議論のテーマです。 「You don't know JS」のカイル・シンプソンの状態:

多くの約束抽象化ライブラリは 約束をキャンセルする機能を提供しますが、これはひどいアイデアです!多くのデベロッパーは、 が外部取り消し機能をネイティブに設計されたことを願っていますが、 という問題は、1人の消費者/オブザーバーがPromise を同じプロミスを観察する他の消費者の能力に影響させるということです。 これは、将来の価値のtrustability(外部不変性)に違反 が、moreverは アンチパターン...

良い「距離でアクション」の実施例でありますか?悪い?私が言うように、それは議論の話題です。私はそれが既存の$http消費者に何の変更も必要としないという事実を好む。

カイルの彼が言うときかなり右:

多くの約束抽象化ライブラリは約束をキャンセルする機能を提供...

例えばブルーバード約束ライブラリが取り消しをサポートしています。 the documentationから:

新しいキャンセルは古い キャンセルが意味を中止していた間、セマンティクスを「気にしない」があります。約束を取り消すと、そのハンドラのコールバックが呼び出されないことを単に意味する を意味します。

オプション2 - 異なる抽象

約束は、比較的広い抽象化です。 Promises/A+ specificationから:

約束非同期動作の最終的な結果を示します。

角度$httpサービスは、非同期のHTTPリクエストの最終的な結果のための約束を返すように約束の$q実装を使用します。

それは$httpが返された約束を飾るtwo deprecated functions.success.errorを、持っている価値は何もありません。これらの関数は、標準的な方法で連鎖可能ではなく、 "HTTP固有の"関数のセットとして多くの価値を追加しないと考えられていたため、非難されました。

しかし、これは、$httpで使用されている基本的な約束を公開しない独自のHTTP抽象化/ラッパーを作ることはできません。このように:

function HttpWrapper($http, $location) { 
    var service = { 
     get: function (getUrl, successCallback, errorCallback) { 
      $http.get(getUrl).then(function (response) { 
       successCallback(response); 
      }, function (rejection) { 
       if (rejection.status === 404) { 
        $location.path('/error'); 
       } else { 
        errorCallback(rejection); 
       } 
      }); 
     } 
    }; 

    return service; 
} 

これは約束を返さないということなので、その消費量は異なり、あまりにも少し作業する必要があります。

HttpWrapper.get('non-existent-location', getSuccess, getError); 

function getSuccess(response) { 
    alert('Everything is ok'); 
} 

function getError(error) { 
    alert('An error happened'); 
} 

を404の場合は、場所は 'に変更されますエラー 'となり、getSuccessgetErrorのコールバックは実行されません。

この実装は、HTTPリクエストをチェーン化する機能がもう利用できないことを意味します。それは容認できる妥協ですか?結果は異なる場合があります...

オプション3 - 彼のコメントをTJに拒否

クレジットを飾る:

あなたが特定のコントローラでのエラー処理が必要な場合、あなたはエラーが処理されたかどうかを確認するために 条件が必要になります インターセプター/サービスなど

でHTTPのインターセプターは、私かどうかを示すために、プロパティhandledと約束拒絶反応を飾ることができますtはエラーを処理しました。

function HttpInterceptor($q, $location) { 
    var service = { 
     responseError: responseError 
    }; 

    return service; 

    function responseError(rejection) { 
     if (rejection.status === 404) { 
      $location.path('/error'); 
      rejection.handled = true; 
     } 

     return $q.reject(rejection); 
    } 
} 

コントローラは、その後、次のようになります

$http.get('non-existent-location') 
    .then(function activateOk(response) { 
     alert('Everything is ok'); 
    }) 
    .catch(function activateError(error) { 
     if (!error.handled) { 
      alert('An error happened'); 
     } 
    }); 

概要

とは異なり、オプション2、オプション3はまだ意味で肯定的であるチェーンの約束への$http消費者のためのオプションを残しそれは機能性を排除していないということです。

オプション2と3のどちらも、「離れた場所での行動」はあまりありません。オプション2の場合、別の抽象化によって、通常の$q実装とは異なる動作が行われることが明らかになります。そして、オプション3については、消費者はそれでも満足する約束を受けます。

多かれ少なかれシナリオを処理するグローバルエラーハンドラを変更しても、コンシューマに変更を加える必要がないため、3つのオプションすべてが保守性基準を満たします。

+0

ニース。よく書かれた答え –

+1

私はあなたのジブのカットが好きです。 – Zach

3

ラップ$httpをご自身のサービスの中に入れてください。このように、エラー処理ロジックを変更する必要がある場合は、すべてのコントローラを変更する必要はありません。

ような何か:

angular.module('test') 
    .factory('http', ['$http', 
     function(http) { 
     return { 
      get: function(getUrl) { 
      return http.get(getUrl).then(function(response) { 
       return response; 
      }, function() { 
       //handle errors here 
      }); 
      }, 
      post: function(postUrl, data) { 
       return http.post(postUrl, data).then(function(response) { 
       return response; 
       }, function() { 
       //handle errors here 
       }); 
      } 
      // other $http wrappers 
     }; 
     }); 
+0

私の例の場合、「htt」に「$ http」を代入すると、ページに「Everything is ok」というアラートが表示され、エラーページにリダイレクトされます。 'activateOk'関数の' response'パラメータは 'undefined'です。これは好ましい結果ではありません。 – Snixtor

+0

@Snixtorあなたの例は無効なURLに当たるので、 'activateOk'を呼び出すことはありません。それは呼び出される 'activateError'になります。私の答えはコピーペーストソリューションではありません。コメント '//ここでエラーを処理する 'がある場合、インターセプタによって処理される' 404'を無視するようなロジックを追加する必要があります。このようなサービスがあれば、おそらくコントローラーにエラーハンドラーは必要ありません。必要な場合は、エラー応答を返す必要があります。それはあなた次第です。 –

+0

申し訳ありませんが、私はあなたが "無効な例"とはどういう意味か分かりません。私が記述した結果は、ここに「ハンドルエラー」の代わりに '$ location.path( '/ error');を置くとどうなるかということです。 「ここでエラーを処理する」で約束の拒絶または解決がなされない限り、約束は未定義の 'response'で解決されます。 – Snixtor

1
application= angular.module('yourmodule', yourdependencies) ;  
application.config(["$provide", "$httpProvider", 
     function (provide, httpProvider){  
      registerInterceptors(provide, httpProvider); 
     }]); 

registerhttpInterceptor = function (provide, httpProvider) { 
       provide.factory("appHttpInterceptor", [ 
        function() { 
         return { 
          responseError: function (rejection) { 
           if (rejection.status == 401) { 
            return "Your custom response"; 
           } 
          } 
         }; 
        } 
       ]); 
       httpProvider.interceptors.push('appHttpInterceptor'); 
      }; 
+0

私はあなたが達成しようとしていることを完全に把握していないと思います。「responseError」からの「あなたのカスタム応答」を返すことは、エラー処理が依然としてコントローラの責任であることを意味します。 – Snixtor

+0

いいえ、このコードはグローバルに存在する可能性があり、モジュールにバインドしています。モジュールを初期化するときに "$ provide"と "http httpProvider"を注入して実行するだけです。 –

+0

ローカルとグローバルの存在は問題ありません。あなたの例は、コントローラの '$ http'約束がオブジェクト"あなたのカスタムレスポンス "で解決されたことを意味します。これは、*コントローラ*が処理する責任になります。私はコントローラがエラーに関する何かを行う責任を負うことを望んでいません。別の方法で見ると、 'activateOk'も' activateError'も実行されません。 – Snixtor

1

This articleは、HTTP呼び出しをインターセプトし、その上でさまざまな操作を実行する方法について説明します。それらの1つはエラー処理です。

上記の記事からクイックコピーペースト...

app.config(function($httpProvider) { 
    $httpProvider.interceptors.push(function($q) { 
    return { 
     // This is the responseError interceptor 
     responseError: function(rejection) { 
     if (rejection.status > 399) { // assuming that any code over 399 is an error 
      $q.reject(rejection) 
     } 

     return rejection; 
     } 
    }; 
    }); 
}); 
+0

この場合、 'userService'の使用は面白い研究です。' $ http'約束が却下されるまで(ユーザーがログインするまで)、基本的に拒否を延期しています。しかしそれ以外の場合は、約束を拒否し、エラーを処理するコントローラに依存しているだけです。これは私が達成しようとしていることには対処していません。 – Snixtor

+0

私はあなたが記事とコードから本質をとったと仮定しました。エラー処理に関連しないコードを削除することができます。 –

+0

私は記事の本質を理解していますが、私が記述したシナリオには触れません。記事のタイトルは実際にはかなりフィットしています: "80/20ガイド"。私は自分のシナリオが80%ではなく20%にあると考えています:) – Snixtor

0

はこの1

工場/ golbalErrorHandler.js

yourmodule.factory('golbalErrorHandler', [function() { 
     var golbalError= { 
      responseError: function(response) { 
       // Golbal Error Handling 
       if (response.status != 200){ 
        console.error(response); 
       } 
      } 
     } 
     return golbalError; 

     }]); 

app.js

yourmodule.config(['$httpProvider', function($httpProvider) { 
     $httpProvider.interceptors.push('golbalErrorHandler'); 
    }]); 
+0

私はAngularにかなり新しいです、戻り値 'golbalError'の使用は何ですか? –

関連する問題