LoginSignup
13

More than 5 years have passed since last update.

AngularJSのpendingRequestsで二重のサブミットを防止する

Last updated at Posted at 2014-12-19

pendingRequests

AngularJSの$httpにはpendingRequestsというfunctionがあり、これを利用すると既に送ったHTTPリクエストでまだ結果が返ってきてないリクエストを知ることができるようです。

https://code.angularjs.org/1.2.28/docs/api/ng/service/$http #pendingRequests

主にデバッグの目的で使用されると書いてあるのですが、これとInterceptorを組み合わせることでPOSTやPUT,DELETEに対する二重サブミットを防止したいと思います。

と、思ったのですが、既に以下のブログで紹介されておりました。
http://ussy00.hatenablog.com/entry/2014/05/28/192819

ただ、上記のブログでは$httpでリクエスト送信を行った場合が紹介されていますので、
本稿では$resourceでリクエスト送信を行った場合を紹介します。

Interceptorの基本的な利用方法は以下を参照してください。
AngularJSのInterceptorを利用する

Interceptorを作成します。

Interceptor
angular.module('myapp.configurations', ['myapp.controllers'])
.config(function($httpProvider) {
  $httpProvider.interceptors.push(
    function($injector, $rootScope, $q) {        
      return {                                                                    
        request : function(config) {                                              
          var $http = $injector.get('$http');                                     
          var requestToken = config.headers['X-Request-Token'];

          function checkIfDuplicated(config) {                                    
            var duplicated = $http.pendingRequests.filter(                        
              function(pendingConfig) {                                           
                return pendingConfig.headers['X-Request-Token']                   
                    && pendingConfig.headers['X-Request-Token'] === requestToken; 
              }
            );
            return duplicated.length > 0;
          }

          if (requestToken && checkIfDuplicated(config))  {
            return $q.defer().promise;
          }

          return config || $q.when(config);
        }
      };
    }
  );
});                                                                             

次に、HTTPリクエストを送る$resourceを以下のようにします。

$resource(url, {}, { 
  saveWithCheck: {method:'POST',  headers:{'X-Request-Token':'hogehoge'}}
});

解説

Interceptorでリクエストconfigのheader情報からリクエストを特定するトークンを取り出します。
これを利用し、pendingRequestsの配列要素一つ一つに対して付き合わせることで二重サブミットを回避する仕組みです。
headerにトークン情報を設定しているのはさらにサーバーサイドでのトークンチェックを行いたいからですが、ここでは省略します。

リクエスト送信元からInterceptorまでのパラメータのやり取りは、正直どんなやり方でも良いのですが、
$resource$httpと違ってconfigの属性を増やしたりすることができません。

そこで、$resourceactionsを利用してheaderにパラメータを設定することにしています。
(see https://docs.angularjs.org/api/ngResource/service/$resource #Usage)

これにより、
二重サブミットを防止したいリクエストの場合は、saveWithCheckを呼び出し、
そうでないリクエストの場合は、$resouceデフォルトのsaveを呼び出すことで使い分けることができるようになりました。

注意

余談ですが、$resourceactionsに設定できるparamsに注意してください。
当たり前かもしれませんが、paramsにパラメータを設定すると、POSTリクエストなのにそれらのパラメータがURLの後ろにクエリストリングとなって送信されてしまいます。
当初リクエスト送信元からInterceptorまでのパラメータのやり取りをparamsに入れていたのですが、送信したURLをみるとそうなっていたので焦りました。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13