UNPKG

36.2 kBMarkdownView Raw
1# Interceptors
2
3- [Incerceptor Principals](#interceptor-principals)
4- [Provided Interceptors](#interceptor-provided)
5 - [Common Interceptors](#interceptor-provided-common)
6 - [Default Request Interceptor](#module-rest/interceptor/defaultRequest)
7 - [Hypermedia As The Engine Of Application State (HATEOAS) Interceptor](#module-rest/interceptor/hateoas)
8 - [Location Interceptor](#module-rest/interceptor/location)
9 - [MIME Interceptor](#module-rest/interceptor/mime)
10 - [Path Prefix Interceptor](#module-rest/interceptor/pathPrefix)
11 - [Authentication Interceptors](#interceptor-provided-auth)
12 - [Basic Auth Interceptor](#module-rest/interceptor/basicAuth)
13 - [OAuth Interceptor](#module-rest/interceptor/oAuth)
14 - [CSRF Interceptor](#module-rest/interceptor/csrf)
15 - [Error Detection and Recovery Interceptors](#interceptor-provided-error)
16 - [Error Code Interceptor](#module-rest/interceptor/errorCode)
17 - [Retry Interceptor](#module-rest/interceptor/retry)
18 - [Timeout Interceptor](#module-rest/interceptor/timeout)
19 - [Fallback Interceptors](#interceptor-provided-fallback)
20 - [JSONP Interceptor](#module-rest/interceptor/jsonp)
21 - [Cross Domain Request for IE Interceptor](#module-rest/interceptor/ie/xdomain)
22 - [ActiveX XHR for IE Interceptor](#module-rest/interceptor/ie/xhr)
23- [Custom Interceptors](#interceptor-custom)
24 - [Interceptor Best Practices](#interceptor-custom-practices)
25 - [Example Interceptors by Concept](#interceptor-custom-concepts)
26 - [Augmented Request/Response](#interceptor-custom-concepts-augment)
27 - [Config Initialization](#interceptor-custom-concepts-config)
28 - [Replaced Request/Response](#interceptor-custom-concepts-replace)
29 - [Reentrent Clients](#interceptor-custom-concepts-reentrent)
30 - [Error Creators](#interceptor-custom-concepts-error)
31 - [Error Recovery](#interceptor-custom-concepts-recovery)
32 - [Cancellation](#interceptor-custom-concepts-cancellation)
33 - [Sharred Request/Response Context](#interceptor-custom-concepts-context)
34 - [Async Request/Response](#interceptor-custom-concepts-async)
35 - [Override Parent Client (ComplexRequest)](#interceptor-custom-concepts-parent)
36 - [Abort Request (ComplexRequest)](#interceptor-custom-concepts-abort)
37
38
39<a name="interceptor-principals"></a>
40## Incerceptor Principals
41
42rest.js distinguishes itself from other HTTP client libraries by providing a minimal core that can be wrapped by more advanced behavior. These configured clients can then be consumed by our application. If a portion of our application needs more advanced behavior, it can continue to wrap the client without impacting other portions of the application. Functional programming FTW.
43
44Each [interceptor](interfaces.md#interface-interceptor) is a function that optionally accepts a parent [client](interfaces.md#interface-client) and some configuration returning a new [client](interfaces.md#interface-client).
45
46```javascript
47// don't do this, there's a better way
48pathPrefix = require('rest/interceptor/pathPrefix');
49errorCode = require('rest/interceptor/errorCode');
50mime = require('rest/interceptor/mime');
51
52client = pathPrefix(errorCode(mime(), { code: 500 }), { prefix: 'http://example.com' });
53```
54
55That works, but it's a mess, don't do it. The configuration is visually separated from the interceptor it belongs to. Glancing at the code, it's hard to know what's going on, never mind the fun of debugging when you give an interceptor the wrong config.
56
57```javascript
58// better, but can still be improved
59pathPrefix = require('rest/interceptor/pathPrefix');
60errorCode = require('rest/interceptor/errorCode');
61mime = require('rest/interceptor/mime');
62
63client = mime();
64client = errorCode(client, { code: 500 });
65client = pathPrefix(client, { prefix: 'http://example.com' });
66```
67
68This example is much more clear. The configuration is now related to it's interceptor. However, it's a bit difficult to follow the `client` var. If we forget to provide the parent client to the next interceptor in the chain, the chain is broken and reset with the default client.
69
70```javascript
71// here we go
72rest = require('rest');
73pathPrefix = require('rest/interceptor/pathPrefix');
74errorCode = require('rest/interceptor/errorCode');
75mime = require('rest/interceptor/mime');
76
77client = rest.wrap(mime)
78 .wrap(errorCode, { code: 500 })
79 .wrap(pathPrefix, { prefix: 'http://example.com' });
80```
81
82In this last example, we're no longer redefining the `client` var, there's no confusion about what the `client` does and we can't forget to pass it along. It's clearly the combination of the default client, and the mime, errorCode and pathPrefix interceptors. The configuration for each interceptor is still directly linked with the interceptor.
83
84It's important to consider the order that interceptors are applied, as some interceptors are more ideal near the root of the chain, while others are better being last. The request phase of the interceptors is applied from the last chained to the root, while the response phase flows in the opposite direction.
85
86Clients may be [declaratively configured using wire.js](wire.md).
87
88
89<a name="interceptor-provided"></a>
90## Provided Interceptors
91
92
93<a name="interceptor-provided-common"></a>
94### Common Interceptors
95
96
97<a name="module-rest/interceptor/defaultRequest"></a>
98#### Default Request Interceptor
99
100`rest/interceptor/defaultRequest` ([src](../interceptor/defaultRequest.js))
101
102Provide default values for the request object. Default values can be provided for the `method`, `path`, `params`, `headers`, `entity`, and/or `mixin`. If the value does not exist in the request already, then the default value is utilized. The `method`, `path` and `entity` values are direct copies, while the `params`, `headers`, and `mixin` values are mixed into the request. In no case will the interceptor overwrite a value in the request.
103
104**Phases**
105
106- request
107
108**Configuration**
109
110<table>
111<tr>
112 <th>Property</th>
113 <th>Required?</th>
114 <th>Default</th>
115 <th>Description</th>
116</tr>
117<tr>
118 <td>method</td>
119 <td>optional</td>
120 <td><em>none</em></td>
121 <td>default HTTP method</td>
122</tr>
123<tr>
124 <td>path</td>
125 <td>optional</td>
126 <td><em>none</em></td>
127 <td>default path</td>
128</tr>
129<tr>
130 <td>params</td>
131 <td>optional</td>
132 <td><em>none</em></td>
133 <td>default params, mixed into request</td>
134</tr>
135<tr>
136 <td>headers</td>
137 <td>optional</td>
138 <td><em>none</em></td>
139 <td>default headers, mixed into request</td>
140</tr>
141<tr>
142 <td>entity</td>
143 <td>optional</td>
144 <td><em>none</em></td>
145 <td>default entity</td>
146</tr>
147<tr>
148 <td>mixin</td>
149 <td>optional</td>
150 <td><em>none</em></td>
151 <td>default extra parameters for the <a href="clients.md#module-rest/client/xhr">XHR object</a> or <a href="clients.md#module-rest/client/node">Node.js</a>.
152</tr>
153</table>
154
155**Example**
156
157```javascript
158client = rest.wrap(defaultRequest, { method: 'PUT', entity: 'defaulted' });
159
160client({});
161// resulting request { method: 'PUT', entity: 'defaulted' }
162
163client({ entity: 'custom' });
164// resulting request { method: 'PUT', entity: 'custom' }
165```
166
167```javascript
168client = rest.wrap(defaultRequest, { headers: { 'X-Requested-With': 'rest.js' } });
169
170client({});
171// resulting request { headers: { 'X-Requested-With': 'rest.js' } }
172
173client({ headers: { 'Some-Other-Header': 'still here' } });
174// resulting request { headers: { 'Some-Other-Header': 'still here', 'X-Requested-With': 'rest.js' } }
175
176client({ headers: { 'X-Requested-With': 'it a secret' } });
177// resulting request { headers: { 'X-Requested-With': 'it a secret' } }
178```
179
180
181<a name="module-rest/interceptor/hateoas"></a>
182#### Hypermedia As The Engine Of Application State (HATEOAS) Interceptor
183
184`rest/interceptor/hateoas` ([src](../interceptor/hateoas.js))
185
186Indexes `links` properties inside an entity to make accessing the related resources easier to access.
187
188Links are index in two ways:
189
1901. as link's `rel` which when accessed issues a request for the linked resource. A promise for the related resource is expected to be returned.
1912. as link's `rel` with 'Link' appended, as a reference to the link object.
192
193The 'Link' response header is also parsed for related resources following rfc5988. The values parsed from the headers are indexed into the response.links object.
194
195Also defines a `clientFor` factory function that creates a new client configured to communicate with a related resource.
196
197The client for the resource reference and the `clientFor` function can be provided by the `client` config property. This method is also useful if the request for the resource
198
199Index links are exposed by default on the entity. A child object may be configed by the 'target' config property.
200
201The entire response object graph will be inspected looking for an Array property names `links`; object cycles are detected and not reindexed.
202
203**TIP:** The MIME interceptor should be installed before the HATEOAS interceptor to convert the response entity from a string into proper JS Objects.
204
205**NOTE:** Native EcmaScript 5 support is required to access related resources implicitly. Polyfills and shims are insufficient. Non-native environment can be supported by using the `clientFor(rel)` method, invoking the return client as normal.
206
207**WARNING:** This interceptor is considered experimental, the behavior may change at any time
208
209**Phases**
210
211- response
212
213**Configuration**
214
215<table>
216<tr>
217 <th>Property</th>
218 <th>Required?</th>
219 <th>Default</th>
220 <th>Description</th>
221</tr>
222<tr>
223 <td>target</td>
224 <td>optional</td>
225 <td>''</td>
226 <td>property to create on the entity and parse links into. If empty, the response entity is used directly.</td>
227</tr>
228<tr>
229 <td>client</td>
230 <td>optional</td>
231 <td><em>this client</em></td>
232 <td>client to use for subsequent requests</td>
233</tr>
234</table>
235
236**Example**
237
238```javascript
239// assuming a native ES5 environment
240client = rest.warp(mime).wrap(hateoas);
241client({ path: '/people/scott' }).then(function (response) {
242 // assuming response for /people/scott: { entity: '{ "name": "Scott", "links": [ { "rel": "father", "href": "/peopele/ron" } ], ... }', ... }
243 // assuming response for /people/ron: { entity: '{ "name": "Ron", ... }', ... }
244
245 assert.same('Scott', response.entity.name);
246 return response.entity.father;
247}).then(function (response) {
248 assert.same('Ron', response.entity.name);
249});
250```
251
252```javascript
253// fallback for non-native ES5 environments
254client = rest.wrap(mime).wrap(hateoas);
255client({ path: '/people/scott' }).then(function (response) {
256 // assuming response for /people/scott: { entity: '{ "name": "Scott", "links": [ { "rel": "father", "href": "/peopele/ron" } ], ... }', ... }
257 // assuming response for /people/ron: { entity: '{ "name": "Ron", ... }', ... }
258
259 assert.same('Scott', response.entity.name);
260 response.entity.clientFor('father')({}).then(function (father) {
261 assert.same('Ron', father.entity.name);
262 });
263});
264```
265
266
267<a name="module-rest/interceptor/location"></a>
268#### Location Interceptor
269
270`rest/interceptor/location` ([src](../interceptor/location.js))
271
272Follows the `Location` header, returning the response of the subsequent request. Browsers will typically automatically follow the location header for redirect in the 300s range, however, they will not follow the Location for a response in the 200s range. Other clients may not follow 300s redirects. This interceptor will always follow a redirect for the original request by default. If configured with `code` the response status code has to be equal or greater than `code` the be treated as a redirect.
273
274Subsequent redirects can be automatically followed by including this interceptor twice in the client chain. However, in this situation, redirect loops will not be detected.
275
276**Phases**
277
278- success
279
280**Configuration**
281
282<table>
283<tr>
284 <th>Property</th>
285 <th>Required?</th>
286 <th>Default</th>
287 <th>Description</th>
288</tr>
289<tr>
290 <td>client</td>
291 <td>optional</td>
292 <td><em>parent client</em></td>
293 <td>client to use for subsequent requests</td>
294</tr>
295<tr>
296 <td>code</td>
297 <td>optional</td>
298 <td>0</td>
299 <td>status code if equal or greater indicates a redirect</td>
300</tr>
301</table>
302
303**Example**
304
305```javascript
306client = rest.wrap(location);
307client({ method: 'POST', path: 'http://example.com/messages', entity: 'hello world' }).then(function (response) {
308 // assuming response for POST: { status: { code: 201 }, headers: { Location: 'http://example.com/messages/1' } }
309 // assuming response for GET: { status: { code: 200 }, entity: 'hello world', ... }
310
311 assert.same('hello wold', response.entity);
312 assert.same('GET', response.request.method);
313 assert.same('http://example.com/messages/1', response.request.path);
314});
315```
316
317
318<a name="module-rest/interceptor/mime"></a>
319#### MIME Interceptor
320
321`rest/interceptor/mime` ([src](../interceptor/mime.js))
322
323Converts request and response entities using the MIME converter registry. Converters are looked up by the `Content-Type` header value. Content types without a converter default to plain text.
324
325See the docs for the MIME registry for more information on available converters and how to register custom converters.
326
327**Phases**
328
329- request
330- response
331
332**Configuration**
333
334<table>
335<tr>
336 <th>Property</th>
337 <th>Required?</th>
338 <th>Default</th>
339 <th>Description</th>
340</tr>
341<tr>
342 <td>mime</td>
343 <td>optional</td>
344 <td><code>Content-Type</code> request header, or 'text/plain'</td>
345 <td>MIME type for request entities</td>
346</tr>
347<tr>
348 <td>accept</td>
349 <td>optional</td>
350 <td>mime + ', application/json;q=0.8, text/plain;q=0.5, */*;q=0.2'</td>
351 <td><code>Accept</code> header to use for the request</td>
352</tr>
353<tr>
354 <td>registry</td>
355 <td>optional</td>
356 <td><em>default registry</em></td>
357 <td>custom MIME registry</td>
358</tr>
359</table>
360
361**Example**
362
363```javascript
364client = rest.wrap(mime);
365client({ path: 'data.json' }).then(function (response) {
366 // for the response: { entity: '{ "key": "value" }', headers: { 'Content-Type': 'application/json', ... } }
367 assert.same('value', response.entity.key);
368});
369```
370
371```javascript
372client = rest.wrap(mime, { mime: 'application/json' );
373client({ method: 'POST', entity: { key: 'value' } }).then(function (response) {
374 assert.same('{ "key": "value" }', response.request.entity);
375 assert.same('application/json, application/json;q=0.8, text/plain;q=0.5, */*;q=0.2', response.request.headers['Content-Type']);
376});
377```
378
379
380<a name="module-rest/interceptor/pathPrefix"></a>
381#### Path Prefix Interceptor
382
383`rest/interceptor/pathPrefix` ([src](../interceptor/pathPrefix.js))
384
385The path prefix interceptor prepends its value to the path provided in the request. The prefix can be used as a base path that the request path is then made relative to. A slash will be inserted between the prefix and path values if needed.
386
387**Phases**
388
389- request
390
391**Configuration**
392
393<table>
394<tr>
395 <th>Property</th>
396 <th>Required?</th>
397 <th>Default</th>
398 <th>Description</th>
399</tr>
400<tr>
401 <td>prefix</td>
402 <td>optional</td>
403 <td><em>empty string</em></td>
404 <td>value to prepend to the request path</td>
405</tr>
406</table>
407
408**Example**
409
410```javascript
411client = rest.wrap(pathPrefix, { prefix: 'http://example.com/messages' });
412client({ path: '1' }).then(function (response) {
413 assert.same('http://example.com/messages/1', response.request.path);
414});
415```
416
417
418<a name="interceptor-provided-auth"></a>
419### Authentication Interceptors
420
421
422<a name="module-rest/interceptor/basicAuth"></a>
423#### Basic Auth Interceptor
424
425`rest/interceptor/basicAuth` ([src](../interceptor/basicAuth.js))
426
427Apply HTTP Basic Authentication to the request. The username and password can either be provided by the interceptor configuration, or the request.
428
429**Phases**
430
431- request
432
433**Configuration**
434
435<table>
436<tr>
437 <th>Property</th>
438 <th>Required?</th>
439 <th>Default</th>
440 <th>Description</th>
441</tr>
442<tr>
443 <td>username</td>
444 <td>optional</td>
445 <td><em>none</em></td>
446 <td>username for the authentication</td>
447</tr>
448<tr>
449 <td>password</td>
450 <td>optional</td>
451 <td><em>empty string</em></td>
452 <td>password for the authentication</td>
453</tr>
454</table>
455
456**Example**
457
458```javascript
459client = rest.wrap(basicAuth, { username: 'admin', password: 'letmein' });
460// interceptor config
461client({}).then(function (response) {
462 assert.same('Basic YWRtaW46bGV0bWVpbg==', response.request.headers.Authorization);
463});
464```
465
466```javascript
467client = rest.wrap(basicAuth);
468// request config
469client({ username: 'admin', password: 'letmein' }).then(function (reponse) {
470 assert.same('Basic YWRtaW46bGV0bWVpbg==', response.request.headers.Authorization);
471});
472```
473
474
475<a name="module-rest/interceptor/oAuth"></a>
476#### OAuth Interceptor
477
478`rest/interceptor/oAuth` ([src](../interceptor/oAuth.js))
479
480Support for the OAuth implicit flow. In a separate window users are redirected to the authentication server when a new access token is required. That authentication server may prompt the user to authenticate and/or grant access to the application requesting an access token. The authentication server then redirects the user back to the application which then needs to parse the access token from the URL and pass it back to the intercept via a callback function placed on the window.
481
482**TIP:** A client request may take a very long time to respond while the user is being prompted to authenticate. Once the user returns to the app, the original request is made with the new access token. If an access token expires, the next request may take a similarly long time to respond as a new token is obtained from the authorization server. The oAuth interceptor should typically be after time sensitive interceptors such as timeout.
483
484**IMPORTANT:** rest.js is only able to provide part of the client flow. When the user is redirected back from the authentication server, the application server must handle the initial request and provide an HTML page with the scripts to parse the URL fragment containing the access token and provide the token to the callback function. As rest.js is not a server side web framework, it is unable to provide support for this part of the oAuth flow.
485
486**Phases**
487
488- request
489- response
490
491**Configuration**
492
493<table>
494<tr>
495 <th>Property</th>
496 <th>Required?</th>
497 <th>Default</th>
498 <th>Description</th>
499</tr>
500<tr>
501 <td>token</td>
502 <td>optional</td>
503 <td><em>none</em></td>
504 <td>pre-configured authorization token obtained by some other means, using this property is uncommon</td>
505</tr>
506<tr>
507 <td>clientId</td>
508 <td>required</td>
509 <td><em>none</em></td>
510 <td>the authentication server clientId, this is given to you by the auth server owner</td>
511</tr>
512<tr>
513 <td>scope</td>
514 <td>required</td>
515 <td><em>none</em></td>
516 <td>comma separated list of resource server scopes to request an access token for,</td>
517</tr>
518<tr>
519 <td>authorizationUrl</td>
520 <td>required</td>
521 <td><em>none</em></td>
522 <td>base URL for the authorization server</td>
523</tr>
524<tr>
525 <td>redirectUrl</td>
526 <td>requried</td>
527 <td><em>none</em></td>
528 <td>URL within this page's origin that the authorization server should redirect back to providing the access token</td>
529</tr>
530<tr>
531 <td>windowStrategy</td>
532 <td>optional</td>
533 <td>window.open</td>
534 <td>strategy for opening the browser window to the authorization server</td>
535</tr>
536<tr>
537 <td>oAuthCallback</td>
538 <td>optional</td>
539 <td><em>none</em></td>
540 <td>callback function to receive the access token, typically used with a custom windowStrategy</td>
541</tr>
542<tr>
543 <td>oAuthCallbackName</td>
544 <td>optional</td>
545 <td>'oAuthCallback'</td>
546 <td>name to register the callback function as on the window</td>
547</tr>
548</table>
549
550**Example**
551
552```javascript
553client = rest.wrap(oAuth, {
554 clientId: 'assignedByAuthServer',
555 scope: 'read, write, openid',
556 authorizationUrl: 'http://authserver.example.com/oauth',
557 redirectUrl: 'http://myapp.example.com/oauthhandler'
558});
559client({ path: 'http://resourceserver.example.com' }).then(function (response) {
560 // authenticated response from the resource server
561});
562```
563
564
565<a name="module-rest/interceptor/csrf"></a>
566#### CSRF Interceptor
567
568`rest/interceptor/csrf` ([src](../interceptor/csrf.js))
569
570Applies a Cross-Site Request Forgery protection header to a request
571
572CSRF protection helps a server verify that a request came from a trusted client and not another client that was able to masquerade as an authorized client. Sites that use cookie based authentication are particularly vulnerable to request forgeries without extra protection.
573
574**Phases**
575
576- request
577
578**Configuration**
579
580<table>
581<tr>
582 <th>Property</th>
583 <th>Required?</th>
584 <th>Default</th>
585 <th>Description</th>
586</tr>
587<tr>
588 <td>name</td>
589 <td>optional</td>
590 <td>'X-Csrf-Token'</td>
591 <td>name of the request header, may be overridden by `request.csrfTokenName`</td>
592</tr>
593<tr>
594 <td>token</td>
595 <td>optional</td>
596 <td><em>none</em></td>
597 <td>CSRF token, may be overridden by `request.csrfToken`</td>
598</tr>
599</table>
600
601**Example**
602
603```javascript
604client = rest.wrap(csrf, { token: 'abc123xyz789' });
605// interceptor config
606client({}).then(function (response) {
607 assert.same('abc123xyz789', response.request.headers['X-Csrf-Token']);
608});
609```
610
611```javascript
612client = rest.wrap(csrf);
613// request config
614client({ csrfToken: 'abc123xyz789' }).then(function (reponse) {
615 assert.same('abc123xyz789', response.request.headers['X-Csrf-Token']);
616});
617```
618
619
620<a name="interceptor-provided-error"></a>
621### Error Detection and Recovery Interceptors
622
623
624<a name="module-rest/interceptor/errorCode"></a>
625#### Error Code Interceptor
626
627`rest/interceptor/errorCode` ([src](../interceptor/errorCode.js))
628
629Marks a response as an error based on the status code. According to the HTTP spec, 500s status codes are server errors, 400s codes are client errors. rest.js by default will treat any response from a server as successful, this allows interceptors to define what constitutes an error. The errorCode interceptor will mark a request in error if the status code is equal or greater than the configured value.
630
631**Phases**
632
633- response
634
635**Configuration**
636
637<table>
638<tr>
639 <th>Property</th>
640 <th>Required?</th>
641 <th>Default</th>
642 <th>Description</th>
643</tr>
644<tr>
645 <td>code</td>
646 <td>optional</td>
647 <td>400</td>
648 <td>status code if equal or greater indicates an error</td>
649</tr>
650</table>
651
652**Example**
653
654```javascript
655client = rest.wrap(errorCode);
656client({}).then(
657 function (response) {
658 // not called
659 },
660 function (response) {
661 assert.same(500, response.status.code);
662 }
663);
664```
665
666
667<a name="module-rest/interceptor/retry"></a>
668#### Retry Interceptor
669
670`rest/interceptor/retry` ([src](../interceptor/retry.js))
671
672Reattempts an errored request after a delay. Attempts are scheduled after a failed response is received, the period between requests is the duration of request plus the delay.
673
674**Phases**
675
676- error
677
678**Configuration**
679
680<table>
681<tr>
682 <th>Property</th>
683 <th>Required?</th>
684 <th>Default</th>
685 <th>Description</th>
686</tr>
687<tr>
688 <td>initial</td>
689 <td>optional</td>
690 <td>100</td>
691 <td>initial delay in milliseconds after the first error response</td>
692</tr>
693<tr>
694 <td>multiplier</td>
695 <td>optional</td>
696 <td>2</td>
697 <td>multiplier for the delay on each subsequent failure used for exponential back offs</td>
698</tr>
699<tr>
700 <td>max</td>
701 <td>optional</td>
702 <td>Infinity</td>
703 <td>max delay in milliseconds</td>
704</tr>
705</table>
706
707**Example**
708
709```javascript
710client = rest.wrap(retry, { initial: 1e3, max: 10e3 });
711client({}).then(function (response) {
712 // assuming it takes a minute from the first request to a successful response
713 // requests occur at 0s, 1s, 3s, 7s, 15s, 25s, 35s, 45s, 55s, 65s
714});
715```
716
717Commonly combined with the timeout interceptor to define a max period to wait
718
719```javascript
720client = rest.wrap(retry, { initial: 1e3, max: 10e3 }).wrap(timeout, { timeout 120e3 });
721client({}).then(
722 function (response) {
723 // called once a request succeeds
724 },
725 function (response) {
726 // called after two minutes waiting, no further retry attempts are made
727 }
728);
729```
730
731
732<a name="module-rest/interceptor/timeout"></a>
733#### Timeout Interceptor
734
735`rest/interceptor/timeout` ([src](../interceptor/timeout.js))
736
737Rejects a request that takes longer than the timeout. If a request is in-flight, it is canceled. The timeout value may be specified in the request or the interceptor config.
738
739**Phases**
740
741- request
742- response
743
744**Configuration**
745
746<table>
747<tr>
748 <th>Property</th>
749 <th>Required?</th>
750 <th>Default</th>
751 <th>Description</th>
752</tr>
753<tr>
754 <td>timeout</td>
755 <td>optional</td>
756 <td><em>disabled</em></td>
757 <td>duration in milliseconds before canceling the request. Non-positive values disable the timeout.</td>
758</tr>
759</table>
760
761**Example**
762
763```javascript
764client = rest.wrap(timeout, { timeout: 10e3 });
765client({}).then(
766 function (response) {
767 // called if the response took less then 10 seconds
768 },
769 function (response) {
770 // called if the response took greater then 10 seconds
771 }
772);
773```
774
775
776<a name="interceptor-provided-fallback"></a>
777### Fallback Interceptors
778
779
780<a name="module-rest/interceptor/jsonp"></a>
781#### JSONP Interceptor
782
783`rest/interceptor/jsonp` ([src](../interceptor/jsonp.js))
784
785Configures a request to use the [JSONP client](clients.md#module-rest/client/jsonp). For most JSONP services, the interceptor defaults are adequate. The script tag and callback function used to load the response, is automatically cleaned up after a response. The callback function may remain after a cancellation in order to avoid script errors in the response if the server responds.
786
787**Phases**
788
789- request
790
791**Configuration**
792
793<table>
794<tr>
795 <th>Property</th>
796 <th>Required?</th>
797 <th>Default</th>
798 <th>Description</th>
799</tr>
800<tr>
801 <td>callback.param</td>
802 <td>optional</td>
803 <td>'callback'</td>
804 <td>request param containing the jsonp callback function name</td>
805</tr>
806<tr>
807 <td>callback.prefix</td>
808 <td>optional</td>
809 <td>'jsonp'</td>
810 <td>prefix for the jsonp callback function name</td>
811</tr>
812<tr>
813 <td>callback.name</td>
814 <td>optional</td>
815 <td><em>generated</em></td>
816 <td>pins the name of the callback function, useful for cases where the server doesn't allow custom callback names. Generally not recommended.</td>
817</tr>
818</table>
819
820**Example**
821
822```javascript
823client = rest.wrap(jsonp);
824client({ path: 'http://ajax.googleapis.com/ajax/services/search/web?v=1.0', params: { q: 'javascript' } }).then(function (response) {
825 // results from google
826});
827```
828
829
830<a name="module-rest/interceptor/ie/xdomain"></a>
831#### Cross Domain Request for IE Interceptor
832
833`rest/interceptor/ie/xdomain` ([src](../interceptor/ie/xdomain.js))
834
835Utilizes IE's XDomainRequest support via the [XDomainRequest client](clients.md#module-rest/client/xdr) for making cross origin requests if needed, available and a CORS enabled XMLHttpRequest is not available. XDR request have a number of limitations, see the [XDR client](clients.md#module-rest/client/xdr) for limitation details. Will not interfere if installed in other environments.
836
837This interceptor should be installed as close to the root of the interceptor chain as possible. When a XDomainRequest client is needed, the normal parent client will not be invoked.
838
839**Phases**
840
841- request
842
843**Configuration**
844
845*none*
846
847**Example**
848
849```javascript
850client = rest.wrap(xdomain)
851 .wrap(defaultRequest, { params: { api_key: '95f41bfa4faa0f43bf7c24795eabbed4', format: 'rest' } });
852client({ params: { method: 'flickr.test.echo' } }).then(function (response) {
853 // response from flickr
854});
855```
856
857
858<a name="module-rest/interceptor/ie/xhr"></a>
859#### ActiveX XHR for IE Interceptor
860
861`rest/interceptor/ie/xhr` ([src](../interceptor/ie/xhr.js))
862
863Attempts to use an ActiveX XHR replacement if a native XMLHttpRequest object is not available. Useful for IE < 9, which does not natively support XMLHttpRequest. Will not interfere if installed in other environments.
864
865**Phases**
866- request
867
868**Configuration**
869
870*none*
871
872**Example**
873
874```javascript
875client = rest.wrap(xhr);
876client({}).then(function (response) {
877 // normal XHR response, even in IE without XHR
878});
879```
880
881
882<a name="interceptor-custom"></a>
883## Custom Interceptors
884
885Creating a custom interceptor is easy. Fundamentally, an interceptor is a function that accepts a client and a configuration object and returns a new client. While not required, it's highly recommended that interceptor authors use the interceptor factory available in `rest/interceptor`.
886
887The interceptor factory allows for interception of the request and/or response. Once the interceptor has a handle on the request or response, it can pass through, manipulate or replace the request/response.
888
889There are five phases that may be intercepted
890
891<table>
892<tr>
893 <th>Phase</th>
894 <th>Description</th>
895</tr>
896<tr>
897 <td>init</td>
898 <td>one time setup of the interceptor config</td>
899</tr>
900<tr>
901 <td>request</td>
902 <td>as the request is initiated, before the socket is opened</td>
903</tr>
904<tr>
905 <td>response</td>
906 <td>after the response is fully received, success or error</td>
907</tr>
908<tr>
909 <td>success</td>
910 <td>a response in a successful state (all responses from servers are successful until an interceptor puts them into an error state)</td>
911</tr>
912<tr>
913 <td>error</td>
914 <td>a response in an error state (either a socket/api level error, or a server response handled as an error)</td>
915</tr>
916</table>
917
918The `response` phase is a catchall for the `success` and `error` phases; if both `response` and `success` handlers are defined, and the request responds normally, then only the `success` phase fires.
919
920Request handlers are functions that accept the request object and interceptor config. Response handlers are provided with the same arguments as request handlers, in addition to the client for the handler. The value returned by a handler becomes the request/response for the next handler in the interceptor chain.
921
922```javascript
923interceptor = require('rest/interceptor');
924noopInterceptor = interceptor({
925 init: function (config) {
926 // do studd with the config
927 return config;
928 },
929 request: function (request, config, meta) {
930 // do stuff with the request
931 return request;
932 },
933 response: function (response, config, meta) {
934 // do stuff with the response
935 return response;
936 },
937 success: function (response, config, meta) {
938 // do stuff with the response
939 return response;
940 },
941 error: function (response, config, meta) {
942 // do stuff with the response
943 return response;
944 }
945});
946```
947
948Promisses representing the request/response may be returned.
949
950```javascript
951interceptor = require('rest/interceptor');
952when = require('when');
953delayRequestInterceptor = interceptor({
954 request: function (request, config) {
955 return when(request).delay(config.delay || 0);
956 }
957});
958```
959
960The `meta` argument contains additional information about the context of the request. It contains the `client`, which can be used to make subsequent requests, and the raw `arguments` provided to the client.
961
962For interceptors that need to track state between request and response handlers, the context of each handler is shared and unique to each invocation.
963
964```javascript
965interceptor = require('rest/interceptor');
966counter = 0;
967countLoggingInterceptor = interceptor({
968 request: function (request) {
969 this.count = counter++;
970 return request;
971 },
972 response: function (response) {
973 console.log('invocation count: ', this.count);
974 return response;
975 }
976});
977```
978
979Success responses can be converted into errors by returning a rejected promise for the response.
980
981```javascript
982interceptor = require('rest/interceptor');
983when = require('when');
984alwaysErrorInterceptor = interceptor({
985 success: function (response) {
986 return when.reject(response);
987 }
988});
989```
990
991Error responses can be converted into successes by returning a resolved promise for the response. This is a special ability of the `error` handler and is not applicable to the `response` handler.
992
993```javascript
994interceptor = require('rest/interceptor');
995when = require('when');
996alwaysErrorInterceptor = interceptor({
997 error: function (response) {
998 return when(response);
999 }
1000});
1001```
1002
1003Interceptors may also override the default client if a parent client is not provided when instantiating the interceptor.
1004
1005```javascript
1006interceptor = require('rest/interceptor');
1007customDefaultClient = require(...);
1008customDefaultClientInterceptor = interceptor({
1009 client: customDefaultClient
1010});
1011```
1012
1013Default configuration values can be provided in the `init` phase. The config object provided is begotten from the config object provided to the interceptor when created. This means that all the properties of the configuration are available, but updates are protected from causing side effects in other interceptors configured with the same config object.
1014
1015```javascript
1016interceptor = require('rest/interceptor');
1017defaultedConfigInterceptor = interceptor({
1018 init: function (config) {
1019 config.prop = config.prop || 'default-value';
1020 return config;
1021 }
1022});
1023```
1024
1025
1026<a name="interceptor-custom-practices"></a>
1027### Interceptor Best Practices
1028
1029- keep interceptors simple, focus on one thing
1030- avoid replacing the request object, augment it instead
1031- make properties configurable
1032- provide sane defaults for configuration properties, avoid required config
1033- allow a request to override configured values
1034- provide default configuration values in the 'init' handler
1035
1036
1037<a name="interceptor-custom-concepts"></a>
1038### Example Interceptors by Concept
1039
1040The interceptors provided with rest.js provide are also good examples. Here are a few interceptors that demonstrate a particular capability. The order of examples within a topic is simple to complex.
1041
1042<a name="interceptor-custom-concepts-augment"></a>
1043**Augmented Request/Response**
1044
1045- [rest/interceptor/basicAuth](#module-rest/interceptor/basicAuth)
1046- [rest/interceptor/pathPrefix](#module-rest/interceptor/pathPrefix)
1047- [rest/interceptor/defaultRequest](#module-rest/interceptor/defaultRequest)
1048- [rest/interceptor/mime](#module-rest/interceptor/mime)
1049- [rest/interceptor/hateoas](#module-rest/interceptor/hateoas)
1050
1051<a name="interceptor-custom-concepts-config"></a>
1052**Config Initialization**
1053
1054- [rest/interceptor/errorCode](#module-rest/interceptor/errorCode)
1055- [rest/interceptor/hateoas](#module-rest/interceptor/hateoas)
1056- [rest/interceptor/oAuth](#module-rest/interceptor/oAuth)
1057
1058<a name="interceptor-custom-concepts-replace"></a>
1059**Replaced Request/Response**
1060
1061- [rest/interceptor/entity](#module-rest/interceptor/entity)
1062
1063<a name="interceptor-custom-concepts-reentrent"></a>
1064**Reentrent Clients**
1065
1066- [rest/interceptor/location](#module-rest/interceptor/location)
1067- [rest/interceptor/retry](#module-rest/interceptor/retry)
1068- [rest/interceptor/hateoas](#module-rest/interceptor/hateoas)
1069- [rest/interceptor/oAuth](#module-rest/interceptor/oAuth)
1070
1071<a name="interceptor-custom-concepts-error"></a>
1072**Error Creators**
1073
1074- [rest/interceptor/errorCode](#module-rest/interceptor/errorCode)
1075- [rest/interceptor/timeout](#module-rest/interceptor/timeout)
1076
1077<a name="interceptor-custom-concepts-recovery"></a>
1078**Error Recovery**
1079
1080- [rest/interceptor/retry](#module-rest/interceptor/retry)
1081
1082<a name="interceptor-custom-concepts-cancellation"></a>
1083**Cancellation**
1084
1085- [rest/interceptor/timeout](#module-rest/interceptor/timeout)
1086
1087<a name="interceptor-custom-concepts-context"></a>
1088**Sharred Request/Response Context**
1089
1090- [rest/interceptor/timeout](#module-rest/interceptor/timeout)
1091
1092<a name="interceptor-custom-concepts-async"></a>
1093**Async Request/Response**
1094
1095- [rest/interceptor/mime](#module-rest/interceptor/mime)
1096
1097<a name="interceptor-custom-concepts-parent"></a>
1098**Override Parent Client (ComplexRequest)**
1099
1100- [rest/interceptor/ie/xdomain](#module-rest/interceptor/ie/xdomain)
1101
1102<a name="interceptor-custom-concepts-abort"></a>
1103**Abort Request (ComplexRequest)**
1104
1105- [rest/interceptor/timeout](#module-rest/interceptor/timeout)