AngularJS:如何发送具有$资源请求的authentication令牌?
我想从我的API请求资源时发送一个授权令牌。
我没有使用$ resource实现一个服务:
factory('Todo', ['$resource', function($resource) { return $resource('http://localhost:port/todos.json', {port:":3001"} , { query: {method: 'GET', isArray: true} }); }])
我有一个存储auth令牌的服务:
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
服务中的一个参数,因为它必须随每次调用一起发送。 并改善干燥。 但是工厂里的所有东西都只执行一次,所以access_token在定义工厂之前必须可用,之后不能更改。
有没有办法把一个dynamic更新的请求参数在服务中?
感谢Andy Joslin。 我select了包装资源行动的想法。 现在资源的服务如下所示:
.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()
方法的返回覆盖,该方法将资源和一系列操作作为参数。
就像你期望的那样,后一种方法实际上是将操作包装为在每个请求中包含auth令牌,并返回一个已修改的资源。 那么让我们来看看这个代码:
.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()
方法从它的参数中创build资源的一个副本,并通过actions
数组循环来为每个动作调用另一个函数tokenWrapper()
。 最后,它返回资源的修改副本。
tokenWrapper
方法首先创build一个预先存在的资源操作的副本。 该副本具有尾部下划线。 所以query()
变成_query()
。 之后一个新的方法覆盖原来的query()
方法。 正如Andy Joslin所build议的,这个新的方法包装了_query()
,为每个通过该动作发送的请求提供auth令牌。
这种方法的好处是,我们仍然可以使用每个angularjs资源(get,query,save等)附带的预定义操作,而不必重新定义它们。 在代码的其余部分(例如在控制器中),我们可以使用默认的操作名称。
另一种方法是使用HTTP拦截器,用当前的OAuth令牌replace“神奇”授权标头。 下面的代码是特定于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';
你可以为它创build一个包装函数。
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; })
我也必须处理这个问题。 我不认为如果这是一个优雅的解决scheme,但它的工作,并有2行代码:
例如,在SessionService中进行身份validation之后,我想你会从服务器获取令牌。 然后,调用这种方法:
angular.module('xxx.sessionService', ['ngResource']). factory('SessionService', function( $http, $rootScope) { //... function setHttpProviderCommonHeaderToken(token){ $http.defaults.headers.common['X-AUTH-TOKEN'] = token; } });
在这之后,$ resource和$ http的所有请求都会在其头部有令牌。
另一个解决scheme是使用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
希望这可以帮助 :)
遵循您接受的答案,我会build议扩展资源以设置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); } } }