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;
});
Todo
サービスを介して送信されるすべてのリクエストで、tokenHandler.get
からトークンを送信したいと思います。特定のアクションの呼び出しに入れることで送信できました。たとえば、これは動作します:
Todo.query( {access_token : tokenHandler.get()} );
ただし、access_tokenはTodo
サービスのパラメーターとして定義することをお勧めします。これは、呼び出しごとに送信する必要があるためです。そして、乾燥を改善する。ただし、ファクトリ内のすべてが1回だけ実行されるため、ファクトリを定義する前にaccess_tokenを使用可能にする必要があり、その後は変更できません。
サービスに動的に更新された要求パラメーターを配置する方法はありますか?
アンディ・ジョスリンに感謝します。リソースアクションをラップするという彼のアイデアを選びました。リソースのサービスは次のようになります。
_.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
methodは、まず既存のリソースアクションのコピーを作成します。このコピーには末尾にアンダースコアが付いています。したがって、query()
は_query()
になります。その後、新しいメソッドが元のquery()
メソッドを上書きします。この新しいメソッドは、Andy Joslinによって提案された_query()
をラップして、そのアクションを介して送信されるすべてのリクエストで認証トークンを提供します。
このアプローチの良い点は、すべてのanglejsリソースに付属する定義済みのアクション(get、query、saveなど)を、再定義することなく使用できることです。そして、残りのコード(たとえば、コントローラー内)では、デフォルトのアクション名を使用できます。
別の方法は、「マジック」認証ヘッダーを現在のOAuthトークンに置き換えるHTTPインターセプターを使用することです。以下のコードはOAuth固有ですが、それを修正することは読者にとって簡単な練習です。
// Injects an HTTP interceptor that replaces a "Bearer" authorization header
// with the current Bearer token.
module.factory('oauthHttpInterceptor', function (OAuth) {
return {
request: function (config) {
// This is just example logic, you could check the URL (for example)
if (config.headers.Authorization === 'Bearer') {
config.headers.Authorization = 'Bearer ' + btoa(OAuth.accessToken);
}
return config;
}
};
});
module.config(function ($httpProvider) {
$httpProvider.interceptors.Push('oauthHttpInterceptor');
});
私はこのアプローチが本当に好きです:
http://blog.brunoscopelliti.com/authentication-to-a-restful-web-service-in-an-angularjs-web-app
トークンは常に、ラッパーを必要とせずにリクエストヘッダー内で自動的に送信されます。
// Define a new http header
$http.defaults.headers.common['auth-token'] = 'C3PO R2D2';
ラッパー関数を作成できます。
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;
})
私もこの問題に対処しなければなりませんでした。私はそれがエレガントなソリューションであるとは思わないが、それは機能し、2行のコードがあります:
たとえば、SessionServiceでの認証後にサーバーからトークンを取得するとします。次に、この種のメソッドを呼び出します。
angular.module('xxx.sessionService', ['ngResource']).
factory('SessionService', function( $http, $rootScope) {
//...
function setHttpProviderCommonHeaderToken(token){
$http.defaults.headers.common['X-AUTH-TOKEN'] = token;
}
});
その後、$ resourceおよび$ httpからのすべてのリクエストのヘッダーにトークンが含まれます。
別の解決策は、追加のパラメーターでバインドされたリソースの新しいインスタンスを返すresource.bind(additionalParamDefaults)を使用することです
var myResource = $resource(url, {id: '@_id'});
var myResourceProtectedByToken = myResource.bind({ access_token : function(){
return tokenHandler.get();
}});
return myResourceProtectedByToken;
Access_token関数は、リソースのアクションが呼び出されるたびに呼び出されます。
私はあなたの質問をすべて誤解しているかもしれません(私を修正してください:))が、すべてのリクエストにaccess_token
を追加することに特に対処するために、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
});
})
Todo.query()
を呼び出すと、?token=none
がURLに追加されます。または、トークンプレースホルダーを追加する場合は、もちろんそれもできます。
http://localhost:port/todos.json/:token
お役に立てれば :)
受け入れられた答えに続いて、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);
私が行う別の変更は、wrapActions
で空の場合にデフォルトのアクションを許可することです。
_if (!actions || actions.length === 0) {
actions = [];
for (i in resource) {
if (i !== 'bind') {
actions.Push(i);
}
}
}
_