62

APIからリソースをリクエストするときに認証トークンを送信したい。

$resourceを使用してサービスを実装しました。

factory('Todo', ['$resource', function($resource) {
 return $resource('http://localhost:port/todos.json', {port:":3001"} , {
   query: {method: 'GET', isArray: true}
 });
}])

そして、私は認証トークンを保存するサービスを持っています:

factory('TokenHandler', function() {
  var tokenHandler = {};
  var token = "none";

  tokenHandler.set = function( newToken ) {
    token = newToken;
  };
  tokenHandler.get = function() {
    return token;
  };

  return tokenHandler;
});

tokenHandler.getサービスを介して送信されるすべてのリクエストでトークンを送信したいと思いTodoます。特定のアクションの呼び出しに入れることで送信できました。たとえば、これは機能します。

Todo.query( {access_token : tokenHandler.get()} );

ただし、access_tokenはTodo呼び出しごとに送信する必要があるため、サービスのパラメーターとして定義することをお勧めします。そしてDRYを改善するために。ただし、ファクトリ内のすべてが1回だけ実行されるため、ファクトリを定義する前にaccess_tokenが使用可能である必要があり、後で変更することはできません。

動的に更新されたリクエストパラメータをサービスに配置する方法はありますか?

4

8 に答える 8

60

アンディ・ジョスリンに感謝。私は、リソース アクションをラップするという彼のアイデアを選びました。リソースのサービスは次のようになります。

.factory('Todo', ['$resource', 'TokenHandler', function($resource, tokenHandler) {
  var resource = $resource('http://localhost:port/todos/:id', {
    port:":3001",
    id:'@id'
    }, {
      update: {method: 'PUT'}
    });

  resource = tokenHandler.wrapActions( resource, ["query", "update"] );

  return resource;
}])

ご覧のとおり、最初はリソースが通常の方法で定義されています。私の例では、これには というカスタム アクションが含まれupdateます。tokenHandler.wrapAction()その後、リソースとアクションの配列をパラメーターとして受け取るメソッドの戻りによって、リソースが上書きされます。

ご想像のとおり、後者のメソッドは実際にはアクションをラップして、すべてのリクエストに認証トークンを含め、変更されたリソースを返します。それでは、そのためのコードを見てみましょう。

.factory('TokenHandler', function() {
  var tokenHandler = {};
  var token = "none";

  tokenHandler.set = function( newToken ) {
    token = newToken;
  };

  tokenHandler.get = function() {
    return token;
  };

  // wrap given actions of a resource to send auth token with every
  // request
  tokenHandler.wrapActions = function( resource, actions ) {
    // copy original resource
    var wrappedResource = resource;
    for (var i=0; i < actions.length; i++) {
      tokenWrapper( wrappedResource, actions[i] );
    };
    // return modified copy of resource
    return wrappedResource;
  };

  // wraps resource action to send request with auth token
  var tokenWrapper = function( resource, action ) {
    // copy original action
    resource['_' + action]  = resource[action];
    // create new action wrapping the original and sending token
    resource[action] = function( data, success, error){
      return resource['_' + action](
        angular.extend({}, data || {}, {access_token: tokenHandler.get()}),
        success,
        error
      );
    };
  };

  return tokenHandler;
});

ご覧のとおり、wrapActions()メソッドはそのパラメーターからリソースのコピーを作成し、actions配列をループ処理して、tokenWrapper()アクションごとに別の関数を呼び出します。最後に、リソースの変更されたコピーを返します。

このtokenWrapperメソッドはまず、既存のリソース アクションのコピーを作成します。このコピーには末尾のアンダースコアがあります。とquery()なり_query()ます。その後、新しいメソッドが元のメソッドを上書きしますquery()_query()Andy Joslin が提案したように、この新しいメソッドは wrapsし、そのアクションを介して送信されるすべてのリクエストに認証トークンを提供します。

このアプローチの良い点は、すべての angularjs リソース (get、query、save など) に付属する定義済みのアクションを再定義することなく使用できることです。コードの残りの部分 (コントローラー内など) では、デフォルトのアクション名を使用できます。

于 2012-06-24T21:20:18.517 に答える
9

そのためのラッパー関数を作成できます。

app.factory('Todo', function($resource, TokenHandler) {
    var res= $resource('http://localhost:port/todos.json', {
        port: ':3001',
    }, {
        _query: {method: 'GET', isArray: true}
    });

    res.query = function(data, success, error) {
        //We put a {} on the first parameter of extend so it won't edit data
        return res._query(
            angular.extend({}, data || {}, {access_token: TokenHandler.get()}),
            success,
            error
        );
    };

    return res;
})
于 2012-06-24T18:35:59.020 に答える
1

受け入れられた回答に続いて、Todo オブジェクトでトークンを設定するためにリソースを拡張することを提案します。

.factory('Todo', ['$resource', 'TokenHandler', function($resource, tokenHandler) {
  var resource = $resource('http://localhost:port/todos/:id', {
    port:":3001",
    id:'@id'
    }, {
      update: {method: 'PUT'}
    });

  resource = tokenHandler.wrapActions( resource, ["query", "update"] );
  resource.prototype.setToken = function setTodoToken(newToken) {
    tokenHandler.set(newToken);
  };
  return resource;
}]);

そうすれば、Todo オブジェクトを使用するたびに TokenHandler をインポートする必要がなくなり、以下を使用できます。

todo.setToken(theNewToken);

私が行うもう1つの変更は、デフォルトのアクションが空の場合に許可することですwrapActions:

if (!actions || actions.length === 0) {
  actions = [];
  for (i in resource) {
    if (i !== 'bind') {
      actions.push(i);
    }
  }
}
于 2014-10-06T12:52:19.300 に答える
1

私はあなたのすべての質問を誤解しているかもしれません (私を修正してください :) ) しかし、具体的にaccess_tokenfor every リクエストの追加に対処するために、TokenHandlerモジュールをモジュールに注入しようとしましたTodoか?

// app
var app = angular.module('app', ['ngResource']);

// token handler
app.factory('TokenHandler', function() { /* ... */ });

// inject the TokenHandler
app.factory('Todo', function($resource, TokenHandler) {
    // get the token
    var token = TokenHandler.get();
    // and add it as a default param
    return $resource('http://localhost:port/todos.json', {
        port: ':3001',
        access_token : token
    });
})

呼び出すと、URLTodo.query()に追加されます。?token=noneまたは、トークンのプレースホルダーを追加したい場合は、もちろんそれも可能です:

http://localhost:port/todos.json/:token

お役に立てれば :)

于 2012-06-24T14:09:31.787 に答える