0
# Interceptors
1
2
Request and response transformation pipeline for modifying HTTP requests before they are sent and responses before they are processed.
3
4
## Capabilities
5
6
### Interceptor Interface
7
8
Define custom interceptors to transform requests and responses.
9
10
```javascript { .api }
11
/**
12
* HTTP interceptor interface for request/response transformation
13
*/
14
interface HttpInterceptor {
15
/** Transform request before sending */
16
request?(request: HttpOptions): HttpOptions;
17
/** Transform response before processing */
18
response?(response: HttpResponse): HttpResponse;
19
}
20
21
/**
22
* Functional interceptor that receives request and next callback
23
* @param request - HTTP request options
24
* @param next - Function to call to continue the chain
25
*/
26
type FunctionalInterceptor = (request: HttpOptions, next: NextCallback) => void;
27
28
interface NextCallback {
29
/** Continue to next interceptor or send request */
30
(response?: ResponseCallback): void;
31
}
32
33
interface ResponseCallback {
34
/** Transform response before resolving promise */
35
(response: HttpResponse): HttpResponse;
36
}
37
```
38
39
**Usage Examples:**
40
41
```javascript
42
// Object-style interceptor
43
const authInterceptor = {
44
request(request) {
45
if (!request.headers) request.headers = {};
46
request.headers['Authorization'] = 'Bearer ' + getToken();
47
return request;
48
},
49
50
response(response) {
51
if (response.status === 401) {
52
// Handle unauthorized
53
redirectToLogin();
54
}
55
return response;
56
}
57
};
58
59
// Functional interceptor
60
const loggingInterceptor = function(request, next) {
61
console.log('Sending request to:', request.url);
62
63
next(function(response) {
64
console.log('Received response:', response.status);
65
return response;
66
});
67
};
68
69
// Register interceptors
70
Vue.http.interceptors.push(authInterceptor);
71
Vue.http.interceptors.push(loggingInterceptor);
72
```
73
74
### Built-in Interceptors
75
76
Vue Resource includes several built-in interceptors that are automatically applied in order. Understanding their functionality helps when creating custom interceptors or troubleshooting request/response transformations.
77
78
```javascript { .api }
79
// Built-in interceptor execution order
80
Vue.http.interceptors = ['before', 'method', 'jsonp', 'json', 'form', 'header', 'cors'];
81
82
// Built-in interceptor implementations
83
Vue.http.interceptor = {
84
before: BeforeInterceptor;
85
method: MethodInterceptor;
86
jsonp: JsonpInterceptor;
87
json: JsonInterceptor;
88
form: FormInterceptor;
89
header: HeaderInterceptor;
90
cors: CorsInterceptor;
91
};
92
```
93
94
#### Before Interceptor
95
96
Executes the `before` callback function if provided in request options, allowing custom pre-request logic.
97
98
**Implementation:**
99
- Calls `request.before(request)` if `request.before` is a function
100
- Executes in the context of the Vue component (`this` binding)
101
- Useful for request logging, authentication tokens, or custom headers
102
103
**Usage Examples:**
104
105
```javascript
106
this.$http.get('/api/users', {
107
before(request) {
108
console.log('About to send request to:', request.url);
109
// 'this' refers to the Vue component
110
this.loading = true;
111
}
112
}).then(response => {
113
this.loading = false;
114
this.users = response.body;
115
});
116
```
117
118
#### Method Interceptor
119
120
Handles HTTP method emulation for servers that don't support all HTTP methods.
121
122
**Implementation:**
123
- When `emulateHTTP: true` is set and method is PUT, PATCH, or DELETE
124
- Changes method to POST and adds `X-HTTP-Method-Override` header with original method
125
- Allows RESTful operations on servers that only support GET/POST
126
127
**Usage Examples:**
128
129
```javascript
130
// Server only supports GET/POST, but you need DELETE
131
this.$http.delete('/api/users/1', {
132
emulateHTTP: true
133
});
134
// Sends: POST /api/users/1 with header X-HTTP-Method-Override: DELETE
135
```
136
137
#### JSON Interceptor
138
139
Handles JSON serialization for requests and deserialization for responses.
140
141
**Request Processing:**
142
- Serializes object bodies to JSON strings when Content-Type is `application/json`
143
- Automatically applies when sending objects as request bodies
144
145
**Response Processing:**
146
- Parses JSON responses based on Content-Type header or content detection
147
- Falls back to content analysis for JSON-like strings (starts with `{` or `[`)
148
- Sets `response.body` to parsed object or original text
149
150
**Usage Examples:**
151
152
```javascript
153
// Request: automatically stringifies object
154
this.$http.post('/api/users', {
155
name: 'John',
156
email: 'john@example.com'
157
});
158
// Body becomes: '{"name":"John","email":"john@example.com"}'
159
160
// Response: automatically parses JSON
161
this.$http.get('/api/users').then(response => {
162
// response.body is already parsed from JSON string to object
163
console.log(response.body.length); // Works directly with array/object
164
});
165
```
166
167
#### Form Interceptor
168
169
Handles form data encoding and multipart uploads.
170
171
**Implementation:**
172
- Removes Content-Type header for FormData bodies (browser sets multipart boundary)
173
- When `emulateJSON: true`, converts objects to URL-encoded form data
174
- Sets appropriate Content-Type for form encoding
175
176
**Usage Examples:**
177
178
```javascript
179
// FormData upload (multipart)
180
const formData = new FormData();
181
formData.append('file', fileInput.files[0]);
182
formData.append('name', 'document.pdf');
183
184
this.$http.post('/api/upload', formData);
185
// Content-Type: multipart/form-data; boundary=...
186
187
// URL-encoded form data
188
this.$http.post('/api/contact', {
189
name: 'John',
190
email: 'john@example.com'
191
}, {
192
emulateJSON: true
193
});
194
// Content-Type: application/x-www-form-urlencoded
195
// Body: name=John&email=john%40example.com
196
```
197
198
#### Header Interceptor
199
200
Applies default headers based on HTTP method and cross-origin status.
201
202
**Implementation:**
203
- Applies method-specific headers from `Vue.http.headers[method]`
204
- Applies common headers from `Vue.http.headers.common`
205
- Adds `X-Requested-With: XMLHttpRequest` for same-origin requests
206
207
**Usage Examples:**
208
209
```javascript
210
// Configure default headers
211
Vue.http.headers.common['Authorization'] = 'Bearer token123';
212
Vue.http.headers.post['Content-Type'] = 'application/json';
213
214
// All requests get Authorization header
215
// POST requests get additional Content-Type header
216
this.$http.post('/api/data', {key: 'value'});
217
```
218
219
#### CORS Interceptor
220
221
Handles Cross-Origin Resource Sharing (CORS) request configuration.
222
223
**Implementation:**
224
- Configures credentials and cross-origin behavior
225
- Works with `credentials` and `crossOrigin` options
226
- Manages browser CORS policy compliance
227
228
**Usage Examples:**
229
230
```javascript
231
// CORS request with credentials
232
this.$http.get('https://api.external.com/data', {
233
credentials: true,
234
crossOrigin: true
235
});
236
```
237
238
#### JSONP Interceptor
239
240
Handles JSONP requests for cross-domain API calls.
241
242
**Implementation:**
243
- Converts requests to JSONP when method is 'JSONP'
244
- Creates script tags for cross-domain requests
245
- Manages callback parameter and cleanup
246
247
**Usage Examples:**
248
249
```javascript
250
// JSONP request (bypasses CORS)
251
this.$http.jsonp('https://api.external.com/data?callback=JSONP_CALLBACK')
252
.then(response => {
253
console.log('JSONP data:', response.body);
254
});
255
```
256
257
### Request Interceptors
258
259
Transform requests before they are sent to the server.
260
261
**Usage Examples:**
262
263
```javascript
264
// Add authentication token
265
Vue.http.interceptors.push(function(request, next) {
266
const token = localStorage.getItem('auth_token');
267
if (token) {
268
if (!request.headers) request.headers = {};
269
request.headers['Authorization'] = `Bearer ${token}`;
270
}
271
next();
272
});
273
274
// Add API version header
275
Vue.http.interceptors.push(function(request, next) {
276
if (!request.headers) request.headers = {};
277
request.headers['Accept'] = 'application/vnd.api+json;version=1';
278
next();
279
});
280
281
// Transform request data
282
Vue.http.interceptors.push(function(request, next) {
283
if (request.method === 'POST' && request.body) {
284
// Wrap data in envelope
285
request.body = {
286
data: request.body,
287
timestamp: Date.now()
288
};
289
}
290
next();
291
});
292
293
// Modify URL based on environment
294
Vue.http.interceptors.push(function(request, next) {
295
if (process.env.NODE_ENV === 'development') {
296
request.url = request.url.replace('/api/', '/dev-api/');
297
}
298
next();
299
});
300
```
301
302
### Response Interceptors
303
304
Transform responses before they are processed by the application.
305
306
**Usage Examples:**
307
308
```javascript
309
// Handle global error responses
310
Vue.http.interceptors.push(function(request, next) {
311
next(function(response) {
312
if (response.status === 401) {
313
// Redirect to login on unauthorized
314
window.location.href = '/login';
315
} else if (response.status === 403) {
316
// Show access denied message
317
alert('Access denied');
318
}
319
return response;
320
});
321
});
322
323
// Unwrap API response envelope
324
Vue.http.interceptors.push(function(request, next) {
325
next(function(response) {
326
if (response.body && response.body.data) {
327
// Extract data from envelope
328
response.body = response.body.data;
329
}
330
return response;
331
});
332
});
333
334
// Add response metadata
335
Vue.http.interceptors.push(function(request, next) {
336
next(function(response) {
337
response.metadata = {
338
requestTime: Date.now(),
339
url: request.url,
340
method: request.method
341
};
342
return response;
343
});
344
});
345
346
// Transform error responses
347
Vue.http.interceptors.push(function(request, next) {
348
next(function(response) {
349
if (!response.ok && response.body && response.body.error) {
350
// Standardize error format
351
response.body = {
352
message: response.body.error.message,
353
code: response.body.error.code,
354
details: response.body.error.details
355
};
356
}
357
return response;
358
});
359
});
360
```
361
362
### Custom Interceptors
363
364
Create reusable interceptors for specific functionality.
365
366
**Usage Examples:**
367
368
```javascript
369
// Retry interceptor
370
function createRetryInterceptor(maxRetries = 3) {
371
return function(request, next) {
372
let attempts = 0;
373
374
function attempt() {
375
attempts++;
376
next(function(response) {
377
if (!response.ok && attempts < maxRetries) {
378
setTimeout(attempt, 1000 * attempts); // Exponential backoff
379
return response;
380
}
381
return response;
382
});
383
}
384
385
attempt();
386
};
387
}
388
389
// Cache interceptor
390
function createCacheInterceptor() {
391
const cache = new Map();
392
393
return function(request, next) {
394
const key = `${request.method}:${request.url}`;
395
396
if (request.method === 'GET' && cache.has(key)) {
397
const cachedResponse = cache.get(key);
398
if (Date.now() - cachedResponse.timestamp < 60000) { // 1 minute cache
399
return Promise.resolve(cachedResponse.response);
400
}
401
}
402
403
next(function(response) {
404
if (request.method === 'GET' && response.ok) {
405
cache.set(key, {
406
response: response,
407
timestamp: Date.now()
408
});
409
}
410
return response;
411
});
412
};
413
}
414
415
// Register custom interceptors
416
Vue.http.interceptors.push(createRetryInterceptor(3));
417
Vue.http.interceptors.push(createCacheInterceptor());
418
```
419
420
### Interceptor Management
421
422
Manage the interceptor chain dynamically.
423
424
```javascript { .api }
425
// Interceptor array (can be modified)
426
Vue.http.interceptors: (HttpInterceptor | string | Function)[];
427
428
// Built-in interceptor registry
429
Vue.http.interceptor: { [name: string]: HttpInterceptor };
430
```
431
432
**Usage Examples:**
433
434
```javascript
435
// Add interceptor by name (uses built-in)
436
Vue.http.interceptors.push('cors');
437
438
// Add custom interceptor
439
Vue.http.interceptors.push(function(request, next) {
440
console.log('Custom interceptor');
441
next();
442
});
443
444
// Remove interceptor
445
const index = Vue.http.interceptors.indexOf('cors');
446
if (index > -1) {
447
Vue.http.interceptors.splice(index, 1);
448
}
449
450
// Clear all interceptors
451
Vue.http.interceptors = [];
452
453
// Reset to defaults
454
Vue.http.interceptors = ['before', 'method', 'jsonp', 'json', 'form', 'header', 'cors'];
455
456
// Add custom built-in interceptor
457
Vue.http.interceptor.myInterceptor = function(request, next) {
458
// Custom logic
459
next();
460
};
461
462
Vue.http.interceptors.push('myInterceptor');
463
```
464
465
### Error Handling in Interceptors
466
467
Handle errors within interceptors.
468
469
**Usage Examples:**
470
471
```javascript
472
// Catch and transform errors
473
Vue.http.interceptors.push(function(request, next) {
474
next(function(response) {
475
try {
476
if (response.body && typeof response.body === 'string') {
477
response.body = JSON.parse(response.body);
478
}
479
} catch (e) {
480
console.error('Failed to parse JSON response:', e);
481
response.body = {error: 'Invalid JSON response'};
482
}
483
return response;
484
});
485
});
486
487
// Handle network errors
488
Vue.http.interceptors.push(function(request, next) {
489
next(function(response) {
490
if (response instanceof Error) {
491
console.error('Network error:', response.message);
492
return {
493
ok: false,
494
status: 0,
495
statusText: 'Network Error',
496
body: {error: 'Network request failed'}
497
};
498
}
499
return response;
500
});
501
});
502
```
503
504
## Types
505
506
```javascript { .api }
507
interface HttpInterceptor {
508
/** Transform request before sending (optional) */
509
request?(request: HttpOptions): HttpOptions;
510
/** Transform response before processing (optional) */
511
response?(response: HttpResponse): HttpResponse;
512
}
513
514
type FunctionalInterceptor = (request: HttpOptions, next: NextCallback) => void;
515
516
interface NextCallback {
517
/** Continue interceptor chain without response transformation */
518
(): void;
519
/** Continue interceptor chain with response transformation */
520
(responseCallback: ResponseCallback): void;
521
}
522
523
interface ResponseCallback {
524
/** Transform response */
525
(response: HttpResponse): HttpResponse;
526
}
527
528
interface InterceptorRegistry {
529
/** Pre-request processing interceptor */
530
before: HttpInterceptor;
531
/** HTTP method transformation interceptor */
532
method: HttpInterceptor;
533
/** JSONP request handling interceptor */
534
jsonp: HttpInterceptor;
535
/** JSON processing interceptor */
536
json: HttpInterceptor;
537
/** Form data processing interceptor */
538
form: HttpInterceptor;
539
/** Header management interceptor */
540
header: HttpInterceptor;
541
/** CORS handling interceptor */
542
cors: HttpInterceptor;
543
/** Custom interceptors */
544
[name: string]: HttpInterceptor;
545
}
546
```