unludo unludo - 5 months ago 80
AngularJS Question

Dynamic Resource Headers

I would like to have service providing a resource as in the following code:



angular.module('myApp.userService', ['ngResource'])
.factory('UserService', function ($resource)
{
var user = $resource('/api/user', {},
{
connect: { method: 'POST', params: {}, isArray:false }
});
return user;
}


Then when using the
connect
action, I would like to dynamically pass a HTTP header, meaning that it may change for each call. Here is an example, in the controller, please see the comment in the code :

$scope.user = UserService;

$scope.connect = function ( user )
{
var hash = 'Basic ' + Base64Service.encode(user.login + ':' + user.password);

// I would like this header to be computed
// and used by the user resource
// each time I call this function
$scope.user.headers = [{Authorization: hash}];

$scope.user.connect( {},
function()
{
// successful login
$location.path('/connected');
}
,function()
{
console.log('There was an error, please try again');
});
}


Do you know a way to do that, either directly or via a trick?




Final thoughts



Accepted answer does not fully answer the question as the headers are not totally dynamic because the factory returns actually a factory (!) which is not the case in my code.

As $resource is a factory, there is no way to make it dynamic.

I finally destroy the resource object each time the user connects. This way, I have the resource with a header computed when the user connects.

The solution provided by @Stewie is useful for that, so I keep it as accepted.




Here is how I did the connect, which can be used multiple times as the resource is destroyed/recreated when (re)connecting:

this.connect = function (user)
{
self.hash = 'Basic ' + Base64Service.encode(user.login + ':' + user.password);
console.log("CONNECT login:" + user.login + " - pwd:" + user.password + " - hash:" + self.hash);

if (self.userResource)
{
delete self.userResource;
}

self.userResource = $resource('/api/user/login', {}, {
connect: {
method: 'POST',
params: {},
isArray: false,
headers: { Authorization: self.hash }
}
});

var deferred = $q.defer();
self.userResource.connect(user,
function (data)
{
//console.log('--------- user logged in ----- ' + JSON.stringify(data));
// successful login
if (!!self.user)
{
angular.copy(data, self.user);
}
else
{
self.user = data;
}

self.setConnected();

storage.set('user', self);

deferred.resolve(self);
},
function (error)
{
self.user = {};
self.isLogged = false;

storage.set('user', self);

deferred.reject(error);
}
);

return deferred.promise;


};

Answer

Starting from angularjs v1.1.1 and ngResource v.1.1.1 this is possible to accomplish using the headers property of the $resource action object.

You may wrap your resource in a function which accepts custom headers as a parameter and returns a $resource object with your custom headers set at the appropriate action definitions:

PLUNKER

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

app.controller('AppController',
  [
    '$scope',
    'UserService',
    function($scope, UserService) {
      $scope.user = {login: 'doe@example.com', password: '123'};

      $scope.connect = function() {
        // dropping out base64 encoding here, for simplicity
        var hash = 'Basic ' + $scope.user.login + ':' + $scope.user.password;
        $scope.user.headers = [{Authorization: hash}];

        UserService({Authorization: hash}).connect(
          function () {
            $location.url('/connected');
          },
          function () {
            console.log('There was an error, please try again');
          }
        );

      };

    }
  ]
);

app.factory('UserService', function ($resource) {
  return function(customHeaders){
    return $resource('/api/user', {}, {
      connect: { 
        method: 'POST',
        params: {},
        isArray: false,
        headers: customHeaders || {}
      }
    });
  };

});
Comments