Chapter 3: Request and Response Configuration
9/1/25About 7 min
Chapter 3: Request and Response Configuration
Learning Objectives
- Understand the structure of the request configuration object
- Master setting and managing request headers
- Learn to configure timeouts and retry mechanisms
- Understand the structure and handling of response data
- Master judging response status codes
3.1 Request Configuration Object Explained
Complete Request Configuration Structure
// Axios's complete request configuration options
const fullRequestConfig = {
// Request URL
url: '/api/users',
// Request method
method: 'get', // default is get
// Base URL, will be prepended to `url`
baseURL: 'https://api.example.com',
// Request headers
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token',
'Accept': 'application/json'
},
// URL parameters (will be appended to the URL)
params: {
page: 1,
limit: 10
},
// Parameter serialization function
paramsSerializer: function(params) {
return Qs.stringify(params, { arrayFormat: 'brackets' });
},
// Request body data
data: {
name: 'John',
email: 'john@example.com'
},
// Request timeout in milliseconds
timeout: 5000,
// Whether cross-site requests should use credentials
withCredentials: false,
// Response data type
responseType: 'json', // 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream'
// Response encoding
responseEncoding: 'utf8',
// XSRF settings
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
// Upload progress callback
onUploadProgress: function(progressEvent) {
console.log('Upload progress:', (progressEvent.loaded / progressEvent.total) * 100 + '%');
},
// Download progress callback
onDownloadProgress: function(progressEvent) {
console.log('Download progress:', (progressEvent.loaded / progressEvent.total) * 100 + '%');
},
// Maximum response body size
maxContentLength: 2000,
maxBodyLength: 2000,
// Status code validation function
validateStatus: function(status) {
return status >= 200 && status < 300;
},
// Maximum number of redirects
maxRedirects: 5,
// Node.js specific configuration
httpAgent: new http.Agent({ keepAlive: true }),
httpsAgent: new https.Agent({ keepAlive: true }),
// Proxy configuration
proxy: {
host: '127.0.0.1',
port: 9000,
auth: {
username: 'mikeymike',
password: 'rapunz3l'
}
},
// Cancel token
cancelToken: new axios.CancelToken(function(cancel) {
// ...
})
};
Configuration Priority
// Configuration priority demonstration
// Priority from highest to lowest:
// 1. Request configuration
// 2. Instance configuration
// 3. Global configuration
// Global configuration
axios.defaults.timeout = 10000;
axios.defaults.baseURL = 'https://api.example.com';
// Instance configuration
const apiClient = axios.create({
timeout: 5000,
baseURL: 'https://api.myservice.com'
});
// Request configuration (highest priority)
apiClient.get('/users', {
timeout: 3000 // This will override the instance and global timeout configurations
});
3.2 Request Header Management
Common Request Header Settings
// Setting common request headers
const commonHeaders = {
// Content type
'Content-Type': 'application/json',
'Accept': 'application/json',
// Authorization related
'Authorization': 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
'X-API-Key': 'your-api-key',
// Client information
'User-Agent': 'MyApp/1.0.0',
'X-Client-Version': '1.0.0',
// Request tracing
'X-Request-ID': 'req-' + Date.now(),
'X-Correlation-ID': 'corr-' + Math.random().toString(36),
// Cache control
'Cache-Control': 'no-cache',
'Pragma': 'no-cache',
// Language and region
'Accept-Language': 'en-US,en;q=0.9,zh-CN;q=0.8',
'Accept-Encoding': 'gzip, deflate, br'
};
// Using request headers
async function requestWithHeaders() {
try {
const response = await axios.get('/api/users', {
headers: commonHeaders
});
console.log('Request successful:', response.data);
return response.data;
} catch (error) {
console.error('Request failed:', error);
throw error;
}
}
Dynamic Request Headers
// Dynamically setting request headers
class HeaderManager {
constructor() {
this.headers = {};
}
// Set authentication header
setAuthHeader(token, type = 'Bearer') {
this.headers['Authorization'] = `${type} ${token}`;
}
// Set API key
setApiKey(key, headerName = 'X-API-Key') {
this.headers[headerName] = key;
}
// Set content type
setContentType(type) {
this.headers['Content-Type'] = type;
}
// Set user agent
setUserAgent(userAgent) {
this.headers['User-Agent'] = userAgent;
}
// Add custom header
addHeader(name, value) {
this.headers[name] = value;
}
// Remove header
removeHeader(name) {
delete this.headers[name];
}
// Get all headers
getHeaders() {
return { ...this.headers };
}
// Clear all headers
clear() {
this.headers = {};
}
}
// Using the header manager
const headerManager = new HeaderManager();
headerManager.setAuthHeader('your-jwt-token');
headerManager.setApiKey('your-api-key');
headerManager.addHeader('X-Custom-Header', 'custom-value');
async function requestWithDynamicHeaders() {
const response = await axios.get('/api/protected', {
headers: headerManager.getHeaders()
});
return response.data;
}
Conditional Setting of Request Headers
// Dynamically setting request headers based on conditions
function buildHeaders(options = {}) {
const headers = {
'Accept': 'application/json'
};
// Set Content-Type based on request type
if (options.hasFormData) {
// FormData automatically sets Content-Type, no need to set it manually
// headers['Content-Type'] = 'multipart/form-data'; // Don't set this
} else if (options.isFormUrlEncoded) {
headers['Content-Type'] = 'application/x-www-form-urlencoded';
} else if (options.isXml) {
headers['Content-Type'] = 'application/xml';
} else {
headers['Content-Type'] = 'application/json';
}
// Authentication header
if (options.token) {
headers['Authorization'] = `Bearer ${options.token}`;
}
// API key
if (options.apiKey) {
headers['X-API-Key'] = options.apiKey;
}
// Request ID for tracing
if (options.includeRequestId) {
headers['X-Request-ID'] = `req-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
}
// Client info
if (options.clientInfo) {
headers['X-Client-Info'] = JSON.stringify(options.clientInfo);
}
return headers;
}
// Usage example
async function apiRequest(url, data, options = {}) {
const headers = buildHeaders(options);
const response = await axios.post(url, data, { headers });
return response.data;
}
3.3 Timeout Configuration
Basic Timeout Setting
// Global timeout setting
axios.defaults.timeout = 10000; // 10 seconds
// Individual request timeout
async function requestWithTimeout() {
try {
const response = await axios.get('/api/slow-endpoint', {
timeout: 5000 // 5-second timeout
});
console.log('Request successful:', response.data);
return response.data;
} catch (error) {
if (error.code === 'ECONNABORTED') {
console.error('Request timed out');
throw new Error('Request timed out, please try again later');
}
throw error;
}
}
Timeout Strategy for Different Request Types
// Timeout strategy for different API endpoints
class TimeoutManager {
constructor() {
this.timeouts = {
// Fast query interface
'fast': 3000,
// Normal CRUD operations
'normal': 10000,
// Data analysis interface
'analysis': 30000,
// File upload
'upload': 60000,
// Report generation
'report': 120000
};
}
getTimeout(type) {
return this.timeouts[type] || this.timeouts.normal;
}
setCustomTimeout(type, timeout) {
this.timeouts[type] = timeout;
}
}
const timeoutManager = new TimeoutManager();
// Set different timeouts based on interface type
async function smartRequest(url, options = {}) {
const { type = 'normal', ...otherOptions } = options;
const timeout = timeoutManager.getTimeout(type);
try {
const response = await axios({
url,
timeout,
...otherOptions
});
return response.data;
} catch (error) {
if (error.code === 'ECONNABORTED') {
console.error(`${type} type request timed out (${timeout}ms)`);
}
throw error;
}
}
// Usage example
smartRequest('/api/users', { type: 'fast' });
smartRequest('/api/upload', { type: 'upload', method: 'post', data: formData });
smartRequest('/api/generate-report', { type: 'report' });
Timeout Retry Mechanism
// Timeout handling with retry
async function requestWithTimeoutRetry(url, config = {}, maxRetries = 3) {
const baseTimeout = config.timeout || 5000;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
// Incrementing timeout
const timeout = baseTimeout * attempt;
console.log(`Attempting request ${attempt}, timeout: ${timeout}ms`);
const response = await axios({
url,
timeout,
...config
});
console.log(`Request attempt ${attempt} successful`);
return response.data;
} catch (error) {
if (error.code === 'ECONNABORTED') {
console.log(`Request attempt ${attempt} timed out`);
if (attempt === maxRetries) {
throw new Error(`Request failed: Timed out after ${maxRetries} retries`);
}
// Wait for a while before retrying
await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
} else {
// Non-timeout error, throw directly
throw error;
}
}
}
}
3.4 Response Configuration and Handling
Response Data Type
// Handling different response types
const responseTypeExamples = {
// JSON data (default)
json: async () => {
const response = await axios.get('/api/users', {
responseType: 'json'
});
console.log('JSON data:', response.data);
return response.data;
},
// Plain text
text: async () => {
const response = await axios.get('/api/readme', {
responseType: 'text'
});
console.log('Text content:', response.data);
return response.data;
},
// Binary data
arraybuffer: async () => {
const response = await axios.get('/api/file.pdf', {
responseType: 'arraybuffer'
});
console.log('Binary data length:', response.data.byteLength);
return response.data;
},
// Blob object (in browser)
blob: async () => {
const response = await axios.get('/api/image.jpg', {
responseType: 'blob'
});
console.log('Blob type:', response.data.type);
return response.data;
},
// Stream data (in Node.js)
stream: async () => {
const response = await axios.get('/api/large-file', {
responseType: 'stream'
});
// Process stream data
response.data.on('data', chunk => {
console.log('Received data chunk:', chunk.length);
});
response.data.on('end', () => {
console.log('Stream data reception complete');
});
return response.data;
}
};
Response Status Code Validation
// Custom status code validation
const statusValidators = {
// Default validation (200-299 is success)
default: (status) => status >= 200 && status < 300,
// Lenient validation (includes 304 Not Modified)
lenient: (status) => (status >= 200 && status < 300) || status === 304,
// Strict validation (only 200 is success)
strict: (status) => status === 200,
// Custom business validation
business: (status) => {
// 200: Success
// 201: Created successfully
// 204: Deleted successfully
// 400: Bad request but can be handled
return [200, 201, 204, 400].includes(status);
}
};
async function requestWithStatusValidation(url, validationType = 'default') {
try {
const response = await axios.get(url, {
validateStatus: statusValidators[validationType]
});
console.log('Status code:', response.status);
console.log('Response data:', response.data);
return response.data;
} catch (error) {
console.error('Request failed:', error.response?.status, error.response?.data);
throw error;
}
}
Response Data Transformation
// Custom response data transformers
const responseTransformers = {
// Add received timestamp
addTimestamp: (data) => {
if (typeof data === 'object' && data !== null) {
data._receivedAt = new Date().toISOString();
}
return data;
},
// Convert date strings to Date objects
parseDates: (data) => {
if (typeof data === 'object' && data !== null) {
const dateRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/;
function parseObject(obj) {
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
const value = obj[key];
if (typeof value === 'string' && dateRegex.test(value)) {
obj[key] = new Date(value);
} else if (typeof value === 'object' && value !== null) {
parseObject(value);
}
}
}
}
parseObject(data);
}
return data;
},
// Normalize API response format
normalizeResponse: (data) => {
// Assume backend returns format { code: 200, message: 'success', data: {...} }
if (data && typeof data === 'object' && 'code' in data) {
if (data.code === 200) {
return data.data;
} else {
throw new Error(data.message || 'Request failed');
}
}
return data;
}
};
// Using custom transformers
async function requestWithTransforms() {
const response = await axios.get('/api/users', {
transformResponse: [
...axios.defaults.transformResponse, // Keep default transformers
responseTransformers.addTimestamp,
responseTransformers.parseDates,
responseTransformers.normalizeResponse
]
});
console.log('Transformed data:', response.data);
return response.data;
}
3.5 Request Configuration Templates
Configuration Template Class
// Request configuration template manager
class RequestConfigManager {
constructor() {
this.templates = {
// API request template
api: {
baseURL: '/api/v1',
timeout: 10000,
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
validateStatus: (status) => status >= 200 && status < 300
},
// File upload template
upload: {
timeout: 60000,
headers: {
'Content-Type': 'multipart/form-data'
},
onUploadProgress: (progressEvent) => {
const percent = Math.round(
(progressEvent.loaded * 100) / progressEvent.total
);
console.log(`Upload progress: ${percent}%`);
}
},
// File download template
download: {
responseType: 'blob',
timeout: 120000,
onDownloadProgress: (progressEvent) => {
if (progressEvent.lengthComputable) {
const percent = Math.round(
(progressEvent.loaded * 100) / progressEvent.total
);
console.log(`Download progress: ${percent}%`);
}
}
},
// Long polling template
longPolling: {
timeout: 30000,
headers: {
'Cache-Control': 'no-cache',
'Pragma': 'no-cache'
}
}
};
}
// Get a template
getTemplate(name) {
return { ...this.templates[name] };
}
// Merge configurations
mergeConfig(templateName, customConfig = {}) {
const template = this.getTemplate(templateName);
return {
...template,
...customConfig,
headers: {
...template.headers,
...customConfig.headers
}
};
}
// Add a custom template
addTemplate(name, config) {
this.templates[name] = config;
}
}
// Using configuration templates
const configManager = new RequestConfigManager();
// API request
async function apiRequest(url, data, customConfig = {}) {
const config = configManager.mergeConfig('api', {
method: 'post',
url,
data,
...customConfig
});
const response = await axios(config);
return response.data;
}
// File upload
async function uploadFile(file, customConfig = {}) {
const formData = new FormData();
formData.append('file', file);
const config = configManager.mergeConfig('upload', {
method: 'post',
url: '/api/upload',
data: formData,
...customConfig
});
const response = await axios(config);
return response.data;
}
// File download
async function downloadFile(url, filename) {
const config = configManager.mergeConfig('download', {
method: 'get',
url
});
const response = await axios(config);
// Create download link (browser environment)
const blob = new Blob([response.data]);
const downloadUrl = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = downloadUrl;
link.download = filename;
link.click();
window.URL.revokeObjectURL(downloadUrl);
return true;
}
3.6 Practical Application Example
Complete API Client Configuration
// Enterprise-level API client configuration
class EnterpriseApiClient {
constructor(config = {}) {
this.config = {
baseURL: config.baseURL || '/api/v1',
timeout: config.timeout || 10000,
retries: config.retries || 3,
retryDelay: config.retryDelay || 1000,
...config
};
this.setupClient();
}
setupClient() {
// Create axios instance
this.client = axios.create({
baseURL: this.config.baseURL,
timeout: this.config.timeout,
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'X-Client-Version': '1.0.0'
}
});
// Set request interceptor
this.client.interceptors.request.use(
(config) => {
// Add request timestamp
config.metadata = { startTime: Date.now() };
// Add request ID
config.headers['X-Request-ID'] = this.generateRequestId();
console.log(`Sending request: ${config.method?.toUpperCase()} ${config.url}`);
return config;
},
(error) => {
console.error('Request configuration error:', error);
return Promise.reject(error);
}
);
// Set response interceptor
this.client.interceptors.response.use(
(response) => {
const duration = Date.now() - response.config.metadata.startTime;
console.log(`Request complete: ${response.status} (${duration}ms)`);
return response;
},
(error) => {
const duration = error.config?.metadata
? Date.now() - error.config.metadata.startTime
: 0;
console.error(`Request failed: ${error.response?.status} (${duration}ms)`);
// Retry logic
return this.handleRetry(error);
}
);
}
generateRequestId() {
return `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
async handleRetry(error) {
const config = error.config;
if (!config || !this.shouldRetry(error)) {
return Promise.reject(error);
}
config.__retryCount = config.__retryCount || 0;
if (config.__retryCount >= this.config.retries) {
return Promise.reject(error);
}
config.__retryCount++;
console.log(`Retrying request (${config.__retryCount}/${this.config.retries})`);
// Wait for retry delay
await new Promise(resolve =>
setTimeout(resolve, this.config.retryDelay * config.__retryCount)
);
return this.client(config);
}
shouldRetry(error) {
// Only retry on network errors or 5xx errors
return !error.response || (error.response.status >= 500);
}
// API methods
async get(url, config = {}) {
const response = await this.client.get(url, config);
return response.data;
}
async post(url, data, config = {}) {
const response = await this.client.post(url, data, config);
return response.data;
}
async put(url, data, config = {}) {
const response = await this.client.put(url, data, config);
return response.data;
}
async patch(url, data, config = {}) {
const response = await this.client.patch(url, data, config);
return response.data;
}
async delete(url, config = {}) {
const response = await this.client.delete(url, config);
return response.data;
}
}
// Using the enterprise-level client
const apiClient = new EnterpriseApiClient({
baseURL: 'https://api.myservice.com/v1',
timeout: 15000,
retries: 3,
retryDelay: 1000
});
// Business API wrapper
class UserService {
constructor(apiClient) {
this.api = apiClient;
}
async getUsers(params = {}) {
return this.api.get('/users', { params });
}
async getUser(id) {
return this.api.get(`/users/${id}`);
}
async createUser(userData) {
return this.api.post('/users', userData);
}
async updateUser(id, userData) {
return this.api.put(`/users/${id}`, userData);
}
async deleteUser(id) {
return this.api.delete(`/users/${id}`);
}
}
const userService = new UserService(apiClient);
Chapter Summary
In Chapter 3, we learned in-depth about:
- Request Configuration Object: Understood the complete configuration options and priority mechanism.
- Request Header Management: Mastered methods for setting static and dynamic request headers.
- Timeout Configuration: Learned to set timeout times and retry mechanisms.
- Response Handling: Understood different response types and data transformation.
- Configuration Templates: Learned to create reusable configuration templates.
- Practical Application: Mastered methods for building enterprise-level API clients.
This knowledge allows us to precisely control all aspects of HTTP requests, laying the foundation for building stable and reliable network applications.
Key Takeaways
- The configuration object provides comprehensive request control capabilities.
- Request header management is an important part of API interaction.
- Reasonable timeout and retry mechanisms improve application robustness.
- Correct handling of response data affects user experience.
- Configuration templates can improve development efficiency and consistency.
- Enterprise-level applications require complete error handling and monitoring.
Next Chapter Preview
The next chapter will cover the interceptor mechanism, one of Axios's most powerful features, which can implement unified request/response handling and middleware functionality.