Search code examples
angularjsangular-resource

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;

};


Solution

  • 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 || {}
          }
        });
      };
    
    });