Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c5f3a41

Browse files
committed
feat:$xhr: provide access to $xhr header defaults
$xhr header defaults are now exposed as $xhr.defaults.headers.common and $xhr.default.headers.<httpmethod>. This allows applications to configure their defaults as needed. This commit doesn't allow headers to be set per request, only per application. Per request change would require api change, which I tried to avoid *for now*.
1 parent d3fb5b4 commit c5f3a41

File tree

5 files changed

+184
-54
lines changed

5 files changed

+184
-54
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
- New [ng:disabled], [ng:selected], [ng:checked], [ng:multiple] and [ng:readonly] directives.
77
- Added support for string representation of month and day in [date] filter.
88
- Added support for `prepend()` to [jqLite].
9+
- Added support for configurable HTTP header defaults for the [$xhr] service.
910

1011

1112
### Bug Fixes

src/Browser.js

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,6 @@ var XHR = window.XMLHttpRequest || function () {
88
throw new Error("This browser does not support XMLHttpRequest.");
99
};
1010

11-
// default xhr headers
12-
var XHR_HEADERS = {
13-
DEFAULT: {
14-
"Accept": "application/json, text/plain, */*",
15-
"X-Requested-With": "XMLHttpRequest"
16-
},
17-
POST: {'Content-Type': 'application/x-www-form-urlencoded'}
18-
};
1911

2012
/**
2113
* @private
@@ -108,8 +100,7 @@ function Browser(window, document, body, XHR, $log) {
108100
} else {
109101
var xhr = new XHR();
110102
xhr.open(method, url, true);
111-
forEach(extend({}, XHR_HEADERS.DEFAULT, XHR_HEADERS[uppercase(method)] || {}, headers || {}),
112-
function(value, key) {
103+
forEach(headers, function(value, key) {
113104
if (value) xhr.setRequestHeader(key, value);
114105
});
115106
xhr.onreadystatechange = function() {

src/service/xhr.js

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,22 @@
2424
* and process it in application specific way, or resume normal execution by calling the
2525
* request callback method.
2626
*
27+
* # HTTP Headers
28+
* The $xhr service will automatically add certain http headers to all requests. These defaults can
29+
* be fully configured by accessing the `$xhr.defaults.headers` configuration object, which
30+
* currently contains this default configuration:
31+
*
32+
* - `$xhr.defaults.headers.common` (headers that are common for all requests):
33+
* - `Accept: application/json, text/plain, *\/*`
34+
* - `X-Requested-With: XMLHttpRequest`
35+
* - `$xhr.defaults.headers.post` (header defaults for HTTP POST requests):
36+
* - `Content-Type: application/x-www-form-urlencoded`
37+
*
38+
* To add or overwrite these defaults, simple add or remove a property from this configuration
39+
* object. To add headers for an HTTP method other than POST, simple create a new object with name
40+
* equal to the lowercased http method name, e.g. `$xhr.defaults.headers.get['My-Header']='value'`.
41+
*
42+
*
2743
* # Security Considerations
2844
* When designing web applications your design needs to consider security threats from
2945
* {@link http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx
@@ -126,7 +142,21 @@
126142
</doc:example>
127143
*/
128144
angularServiceInject('$xhr', function($browser, $error, $log, $updateView){
129-
return function(method, url, post, callback){
145+
146+
var xhrHeaderDefaults = {
147+
common: {
148+
"Accept": "application/json, text/plain, */*",
149+
"X-Requested-With": "XMLHttpRequest"
150+
},
151+
post: {'Content-Type': 'application/x-www-form-urlencoded'},
152+
get: {}, // all these empty properties are needed so that client apps can just do:
153+
head: {}, // $xhr.defaults.headers.head.foo="bar" without having to create head object
154+
put: {}, // it also means that if we add a header for these methods in the future, it
155+
'delete': {}, // won't be easily silently lost due to an object assignment.
156+
patch: {}
157+
};
158+
159+
function xhr(method, url, post, callback){
130160
if (isFunction(post)) {
131161
callback = post;
132162
post = null;
@@ -155,8 +185,12 @@ angularServiceInject('$xhr', function($browser, $error, $log, $updateView){
155185
} finally {
156186
$updateView();
157187
}
158-
}, {
159-
'X-XSRF-TOKEN': $browser.cookies()['XSRF-TOKEN']
160-
});
188+
}, extend({'X-XSRF-TOKEN': $browser.cookies()['XSRF-TOKEN']},
189+
xhrHeaderDefaults.common,
190+
xhrHeaderDefaults[lowercase(method)]));
161191
};
192+
193+
xhr.defaults = {headers: xhrHeaderDefaults};
194+
195+
return xhr;
162196
}, ['$browser', '$xhr.error', '$log', '$updateView']);

test/BrowserSpecs.js

Lines changed: 19 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -100,31 +100,6 @@ describe('browser', function(){
100100
});
101101
});
102102

103-
it('should set headers for all requests', function(){
104-
var code, response, headers = {};
105-
browser.xhr('GET', 'URL', 'POST', function(c,r){
106-
code = c;
107-
response = r;
108-
}, {'X-header': 'value'});
109-
110-
expect(xhr.method).toEqual('GET');
111-
expect(xhr.url).toEqual('URL');
112-
expect(xhr.post).toEqual('POST');
113-
expect(xhr.headers).toEqual({
114-
"Accept": "application/json, text/plain, */*",
115-
"X-Requested-With": "XMLHttpRequest",
116-
"X-header":"value"
117-
});
118-
119-
xhr.status = 202;
120-
xhr.responseText = 'RESPONSE';
121-
xhr.readyState = 4;
122-
xhr.onreadystatechange();
123-
124-
expect(code).toEqual(202);
125-
expect(response).toEqual('RESPONSE');
126-
});
127-
128103
it('should normalize IE\'s 1223 status code into 204', function() {
129104
var callback = jasmine.createSpy('XHR');
130105

@@ -138,24 +113,28 @@ describe('browser', function(){
138113
expect(callback.argsForCall[0][0]).toEqual(204);
139114
});
140115

141-
it('should not set Content-type header for GET requests', function() {
142-
browser.xhr('GET', 'URL', 'POST-DATA', function(c, r) {});
143-
144-
expect(xhr.headers['Content-Type']).not.toBeDefined();
145-
});
146-
147-
it('should set Content-type header for POST requests', function() {
148-
browser.xhr('POST', 'URL', 'POST-DATA', function(c, r) {});
116+
it('should set only the requested headers', function() {
117+
var code, response, headers = {};
118+
browser.xhr('POST', 'URL', null, function(c,r){
119+
code = c;
120+
response = r;
121+
}, {'X-header1': 'value1', 'X-header2': 'value2'});
149122

150-
expect(xhr.headers['Content-Type']).toBeDefined();
151-
expect(xhr.headers['Content-Type']).toEqual('application/x-www-form-urlencoded');
152-
});
123+
expect(xhr.method).toEqual('POST');
124+
expect(xhr.url).toEqual('URL');
125+
expect(xhr.post).toEqual('');
126+
expect(xhr.headers).toEqual({
127+
"X-header1":"value1",
128+
"X-header2":"value2"
129+
});
153130

154-
it('should set default headers for custom methods', function() {
155-
browser.xhr('CUSTOM', 'URL', 'POST-DATA', function(c, r) {});
131+
xhr.status = 202;
132+
xhr.responseText = 'RESPONSE';
133+
xhr.readyState = 4;
134+
xhr.onreadystatechange();
156135

157-
expect(xhr.headers['Accept']).toEqual('application/json, text/plain, */*');
158-
expect(xhr.headers['X-Requested-With']).toEqual('XMLHttpRequest');
136+
expect(code).toEqual(202);
137+
expect(response).toEqual('RESPONSE');
159138
});
160139
});
161140

test/service/xhrSpec.js

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,131 @@ describe('$xhr', function() {
102102
expect(response).toEqual([1, 'abc', {foo:'bar'}]);
103103
});
104104

105+
106+
describe('http headers', function() {
107+
108+
describe('default headers', function() {
109+
110+
it('should set default headers for GET request', function(){
111+
var callback = jasmine.createSpy('callback');
112+
113+
$browserXhr.expectGET('URL', '', {'Accept': 'application/json, text/plain, */*',
114+
'X-Requested-With': 'XMLHttpRequest'}).
115+
respond(234, 'OK');
116+
117+
$xhr('GET', 'URL', callback);
118+
$browserXhr.flush();
119+
expect(callback).toHaveBeenCalled();
120+
});
121+
122+
123+
it('should set default headers for POST request', function(){
124+
var callback = jasmine.createSpy('callback');
125+
126+
$browserXhr.expectPOST('URL', 'xx', {'Accept': 'application/json, text/plain, */*',
127+
'X-Requested-With': 'XMLHttpRequest',
128+
'Content-Type': 'application/x-www-form-urlencoded'}).
129+
respond(200, 'OK');
130+
131+
$xhr('POST', 'URL', 'xx', callback);
132+
$browserXhr.flush();
133+
expect(callback).toHaveBeenCalled();
134+
});
135+
136+
137+
it('should set default headers for custom HTTP method', function(){
138+
var callback = jasmine.createSpy('callback');
139+
140+
$browserXhr.expect('FOO', 'URL', '', {'Accept': 'application/json, text/plain, */*',
141+
'X-Requested-With': 'XMLHttpRequest'}).
142+
respond(200, 'OK');
143+
144+
$xhr('FOO', 'URL', callback);
145+
$browserXhr.flush();
146+
expect(callback).toHaveBeenCalled();
147+
});
148+
149+
150+
describe('custom headers', function() {
151+
152+
it('should allow appending a new header to the common defaults', function() {
153+
var callback = jasmine.createSpy('callback');
154+
155+
$browserXhr.expectGET('URL', '', {'Accept': 'application/json, text/plain, */*',
156+
'X-Requested-With': 'XMLHttpRequest',
157+
'Custom-Header': 'value'}).
158+
respond(200, 'OK');
159+
160+
$xhr.defaults.headers.common['Custom-Header'] = 'value';
161+
$xhr('GET', 'URL', callback);
162+
$browserXhr.flush();
163+
expect(callback).toHaveBeenCalled();
164+
callback.reset();
165+
166+
$browserXhr.expectPOST('URL', 'xx', {'Accept': 'application/json, text/plain, */*',
167+
'X-Requested-With': 'XMLHttpRequest',
168+
'Content-Type': 'application/x-www-form-urlencoded',
169+
'Custom-Header': 'value'}).
170+
respond(200, 'OK');
171+
172+
$xhr('POST', 'URL', 'xx', callback);
173+
$browserXhr.flush();
174+
expect(callback).toHaveBeenCalled();
175+
});
176+
177+
178+
it('should allow appending a new header to a method specific defaults', function() {
179+
var callback = jasmine.createSpy('callback');
180+
181+
$browserXhr.expectGET('URL', '', {'Accept': 'application/json, text/plain, */*',
182+
'X-Requested-With': 'XMLHttpRequest',
183+
'Content-Type': 'application/json'}).
184+
respond(200, 'OK');
185+
186+
$xhr.defaults.headers.get['Content-Type'] = 'application/json';
187+
$xhr('GET', 'URL', callback);
188+
$browserXhr.flush();
189+
expect(callback).toHaveBeenCalled();
190+
callback.reset();
191+
192+
$browserXhr.expectPOST('URL', 'x', {'Accept': 'application/json, text/plain, */*',
193+
'X-Requested-With': 'XMLHttpRequest',
194+
'Content-Type': 'application/x-www-form-urlencoded'}).
195+
respond(200, 'OK');
196+
197+
$xhr('POST', 'URL', 'x', callback);
198+
$browserXhr.flush();
199+
expect(callback).toHaveBeenCalled();
200+
});
201+
202+
203+
it('should support overwriting and deleting default headers', function() {
204+
var callback = jasmine.createSpy('callback');
205+
206+
$browserXhr.expectGET('URL', '', {'Accept': 'application/json, text/plain, */*'}).
207+
respond(200, 'OK');
208+
209+
//delete a default header
210+
delete $xhr.defaults.headers.common['X-Requested-With'];
211+
$xhr('GET', 'URL', callback);
212+
$browserXhr.flush();
213+
expect(callback).toHaveBeenCalled();
214+
callback.reset();
215+
216+
$browserXhr.expectPOST('URL', 'xx', {'Accept': 'application/json, text/plain, */*',
217+
'Content-Type': 'application/json'}).
218+
respond(200, 'OK');
219+
220+
//overwrite a default header
221+
$xhr.defaults.headers.post['Content-Type'] = 'application/json';
222+
$xhr('POST', 'URL', 'xx', callback);
223+
$browserXhr.flush();
224+
expect(callback).toHaveBeenCalled();
225+
});
226+
});
227+
});
228+
});
229+
105230
describe('xsrf', function(){
106231
it('should copy the XSRF cookie into a XSRF Header', function(){
107232
var code, response;

0 commit comments

Comments
 (0)