AngularJS: как отправить токен аутентификации с запросами $resource?
Я хочу отправить маркер аутентификации при запросе ресурса из моего 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()} );
но я бы предпочел определить маркер доступа в качестве параметра Todo
сервис, так как он должен быть отправлен с каждым вызовом. И улучшить сухой.
Но все на фабрике выполняется только один раз, поэтому access_token должен быть доступен до определения фабрики, и он не может измениться впоследствии.
есть ли способ поместить динамически обновляемый параметр запроса в службу?
8 ответов:
спасибо Энди Джослин. Я выбрал его идею обертывания действий ресурса. Сервис для ресурса теперь выглядит так:
.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()
для каждого действия. В конце концов он возвращает измененную копию ресурса.The
tokenWrapper
метод прежде всего создает копию уже существующего ресурса действие. Эта копия имеет конечное подчеркивание. Так чтоquery()
становится_query()
. После этого новый метод перезаписывает исходныйquery()
метод. Этот новый метод обертывания_query()
, как предложил Энди Джослин, чтобы предоставить маркер аутентификации с каждым запросом отправить через это действие.хорошая вещь с этим подходом заключается в том, что мы все еще можем использовать предопределенные действия, которые поставляются с каждым ресурсом angularjs (get, query, save и т. д.), без необходимости переопределять их. А в остальном код (например, в контроллерах) мы можем использовать имя действия по умолчанию.
другой способ-использовать перехватчик HTTP, который заменяет заголовок авторизации "magic" текущим маркером OAuth. Приведенный ниже код является специфичным для 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 будут иметь токен в своем заголовке.
другим решением было бы использовать ресурс.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; }]);
таким образом, нет необходимости импортировать TokenHandler каждый раз, когда вы хотите использовать объект Todo, и вы можете использовать:
todo.setToken(theNewToken);
еще одно изменение, которое я бы сделал, это разрешить действия по умолчанию, если они пусты в
wrapActions
:if (!actions || actions.length === 0) { actions = []; for (i in resource) { if (i !== 'bind') { actions.push(i); } } }