Angular.js: Make dynamic headers work with @value annotation in $resource

Created on 1 May 2014  路  6Comments  路  Source: angular/angular.js

type: feature request

the idea would be to allow @value annotations in resource definitions like:

return $resource('my/api/url', {}, {
            update: {method: 'PUT', headers: { 'If-Match': '@version' }},
        });

i'm too new to javascript and to angularjs so i can't make a proper implementation but for now i hacked the _/src/ngResource/resource.js_ file with the following code to make this work (inserted the custom code on line 557):

if (hasBody) httpConfig.data = data;
route.setUrlParams(httpConfig,
    extend({}, extractParams(data, action.params || {}), params),
    action.url);


//******************************************************
//custom code to make dynamic headers work
var getHeaders = function (config, params) {

    var result = extend({}, config.headers);

    var headerParams = extend({}, config.headers);

    forEach(headerParams, function (value, key) {
        if (value.charAt(0) == '@') {
            result[key] = params[value.substr(1)];
        }
    });

    return result;
}

httpConfig.headers = getHeaders(httpConfig, params);
//******************************************************

maybe someone can take this and make a proper implementation out of it

ngResource feature

Most helpful comment

Actually it's not possible to alter header in transformRequest because headersGetter only returns a copy of the actual headers that will be sent. Changing the copy is useless.

You can dynamically pass your ETag version in header like this:

return $resource('my/api/url', {}, {
  update: {
    method: 'PUT',
    headers: {
      'If-Match': function(config) {
        return config.data.version;
      }
    }
  }
});

All 6 comments

While it would be convenient to use @value annotations in resource definitions, it is already possible to set the headers to custom values from the properties of the data object by including the transformRequest param on the action argument of $resource:

return $resource('my/api/url', {}, {
    update : {
        method : 'PUT',
        headers : {
            'If-Match' : ''
        },
        transformRequest : function (data, headersGetter, status) {
            var headers = headersGetter();
            headers["If-Match"] = data.version;
            return JSON.stringify(data);
        }
    },
});

+1 @shellyfld !!

Actually it's not possible to alter header in transformRequest because headersGetter only returns a copy of the actual headers that will be sent. Changing the copy is useless.

You can dynamically pass your ETag version in header like this:

return $resource('my/api/url', {}, {
  update: {
    method: 'PUT',
    headers: {
      'If-Match': function(config) {
        return config.data.version;
      }
    }
  }
});

The solution above works great for methods that sends data (PUT and PATCH) but fails for DELETE because config.data is not defined on this method.

One of the ways to handle If-Match header on DELETE is by using ng-resource-etag, a fork of ng-resource to support eTag on If-Match header.

This only works for the first request your resource sends though. All subsequent requests will have the same header value set initially, as I understand.

transformRequest : function (data, headersGetter, status) { var headers = headersGetter(); headers["If-Match"] = data.version; return JSON.stringify(data); }

that doesn't work for me...

"register": {
                method:"POST",
                params:{
                    param1: "register",
                    param2:'@param2'
                    //hParam1:'@hParam1' //Header Parameter
                },
                headers:{
                    'g-Recaptcha-Response':''
                },
                transformRequest : function (data, headersGetter, status) {
                    var headers = headersGetter();
                    headers["g-Recaptcha-Response"] = data.hParam1;
                    return JSON.stringify(data);
                }

data is the model, and does not contain data within the first argument:

accountService.register({hParam1:privateData.rcToken}, user).$promise

Was this page helpful?
0 / 5 - 0 ratings