当前位置: 首页 > article >正文

021、深入解析前端请求拦截器

目录

深入解析前端请求拦截器:

1. 引言

2. 核心实现与基础概念

2.1 基础拦截器实现

2.2 响应拦截器配置

3. 实际应用场景

3.1 完整的用户认证系统

3.2 文件上传系统

3.3 API请求缓存系统

3.4 请求重试机制

3.5 国际化处理

4. 性能优化实践

4.1 请求合并(Request Batching)

4.2 预请求与预加载

5. 安全性实践

5.1 XSS防护

5.2 CSRF防护

6. 监控与日志

6.1 请求监控系统

6.2 性能追踪系统

7. 高级应用场景

7.1 GraphQL请求处理

7.2 WebSocket 集成

7.3 离线请求队列

8. 微服务架构支持

9. 参考文献

10. 总结


深入解析前端请求拦截器:

1. 引言

在现代Web应用开发中,请求拦截器已成为处理HTTP请求的核心组件。根据Stack Overflow 2023年开发者调查报告,超过70%的企业级应用采用了请求拦截器进行统一的请求处理。本文将从多个维度深入分析请求拦截器的实现原理和最佳实践。

2. 核心实现与基础概念

2.1 基础拦截器实现

request.interceptors.request.use(config => {
    config.headers['Content-Type'] = 'application/json;charset=utf-8';
    let user = JSON.parse(localStorage.getItem("xm-user") || '{}')
    config.headers['token'] = user.token || ''
    return config
}, error => {
    return Promise.reject(error)
});

2.2 响应拦截器配置

request.interceptors.response.use(
    response => {
        // 统一处理响应
        const { code, data, message } = response.data;
        if (code === 200) {
            return data;
        } else if (code === 401) {
            // 处理认证失败
            router.push('/login');
            return Promise.reject(new Error('认证失败'));
        } else {
            Message.error(message);
            return Promise.reject(new Error(message));
        }
    },
    error => {
        // 处理网络错误
        if (error.response) {
            switch (error.response.status) {
                case 404:
                    Message.error('请求的资源不存在');
                    break;
                case 500:
                    Message.error('服务器内部错误');
                    break;
                default:
                    Message.error('网络错误');
            }
        }
        return Promise.reject(error);
    }
);

3. 实际应用场景

3.1 完整的用户认证系统

// 认证服务
const authService = {
    async login(credentials) {
        try {
            const response = await request.post('/auth/login', credentials);
            this.storeUserData(response.data);
            return response;
        } catch (error) {
            this.handleAuthError(error);
        }
    },
​
    storeUserData(data) {
        const encryptedToken = this.encryptSensitiveData(data.token);
        localStorage.setItem('xm-user', JSON.stringify({
            token: encryptedToken,
            expires: new Date().getTime() + 3600000,
            refreshToken: data.refreshToken
        }));
    },
​
    encryptSensitiveData(data) {
        // 使用AES加密敏感数据
        return CryptoJS.AES.encrypt(data, SECRET_KEY).toString();
    },
​
    async refreshToken() {
        const userData = JSON.parse(localStorage.getItem('xm-user') || '{}');
        if (this.isTokenExpiring(userData)) {
            const response = await request.post('/auth/refresh-token', {
                refreshToken: userData.refreshToken
            });
            this.storeUserData(response.data);
        }
    },
​
    isTokenExpiring(userData) {
        const bufferTime = 5 * 60 * 1000; // 5分钟缓冲时间
        return userData.expires - new Date().getTime() < bufferTime;
    }
};

3.2 文件上传系统

// 文件上传服务
const uploadService = {
    // 文件上传拦截器配置
    setupInterceptor() {
        request.interceptors.request.use(config => {
            if (config.url.includes('/upload')) {
                config.headers['Content-Type'] = 'multipart/form-data';
                config.timeout = 30000;
                config.onUploadProgress = this.handleProgress;
            }
            return config;
        });
    },
​
    handleProgress(progressEvent) {
        const percentCompleted = Math.round(
            (progressEvent.loaded * 100) / progressEvent.total
        );
        // 更新上传进度UI
        this.updateProgressBar(percentCompleted);
    },
​
    async uploadFile(file, options = {}) {
        const formData = new FormData();
        formData.append('file', file);
        
        // 添加额外的元数据
        if (options.metadata) {
            formData.append('metadata', JSON.stringify(options.metadata));
        }
​
        try {
            const response = await request.post('/api/upload', formData, {
                headers: {
                    'X-File-Name': file.name,
                    'X-File-Size': file.size
                }
            });
            return response.data;
        } catch (error) {
            this.handleUploadError(error);
        }
    },
​
    async uploadChunked(file) {
        const chunkSize = 1024 * 1024; // 1MB chunks
        const chunks = Math.ceil(file.size / chunkSize);
        
        for (let i = 0; i < chunks; i++) {
            const chunk = file.slice(
                i * chunkSize,
                Math.min((i + 1) * chunkSize, file.size)
            );
            await this.uploadChunk(chunk, i, chunks);
        }
    }
};

3.3 API请求缓存系统

// 高级缓存管理
class CacheManager {
    constructor() {
        this.cache = new Map();
        this.setupInterceptor();
    }
​
    setupInterceptor() {
        request.interceptors.request.use(async config => {
            if (config.method === 'get' && config.cache !== false) {
                const cacheKey = this.generateCacheKey(config);
                const cachedResponse = this.getCache(cacheKey);
                
                if (cachedResponse) {
                    return Promise.resolve(cachedResponse);
                }
            }
            return config;
        });
​
        request.interceptors.response.use(response => {
            if (response.config.method === 'get' && response.config.cache !== false) {
                const cacheKey = this.generateCacheKey(response.config);
                this.setCache(cacheKey, response);
            }
            return response;
        });
    }
​
    generateCacheKey(config) {
        return `${config.url}-${JSON.stringify(config.params || {})}-${JSON.stringify(config.data || {})}`;
    }
​
    getCache(key) {
        const cached = this.cache.get(key);
        if (!cached) return null;
​
        const { data, timestamp, maxAge } = cached;
        if (new Date().getTime() - timestamp > maxAge) {
            this.cache.delete(key);
            return null;
        }
        return data;
    }
​
    setCache(key, data, maxAge = 5 * 60 * 1000) {
        this.cache.set(key, {
            data,
            timestamp: new Date().getTime(),
            maxAge
        });
    }
​
    clearCache() {
        this.cache.clear();
    }
}

3.4 请求重试机制

// 高级重试机制
class RetryManager {
    constructor(maxRetries = 3, retryDelay = 1000) {
        this.maxRetries = maxRetries;
        this.retryDelay = retryDelay;
        this.setupInterceptor();
    }
​
    setupInterceptor() {
        request.interceptors.response.use(
            response => response,
            async error => {
                const config = error.config;
                
                // 初始化重试计数
                config.__retryCount = config.__retryCount || 0;
                
                if (config.__retryCount >= this.maxRetries) {
                    return Promise.reject(error);
                }
                
                // 增加重试计数
                config.__retryCount += 1;
                
                // 创建新的Promise用于重试延迟
                const backoff = new Promise(resolve => {
                    setTimeout(() => {
                        resolve();
                    }, this.retryDelay * Math.pow(2, config.__retryCount - 1));
                });
                
                // 等待延迟后重试
                await backoff;
                
                // 返回重试请求
                return request(config);
            }
        );
    }
}

3.5 国际化处理

// 国际化请求处理
const i18nInterceptor = {
    setup() {
        request.interceptors.request.use(config => {
            // 添加语言标识
            config.headers['Accept-Language'] = localStorage.getItem('language') || 'zh-CN';
            
            // 针对特定API添加地区信息
            if (config.url.includes('/api/location')) {
                config.params = {
                    ...config.params,
                    region: localStorage.getItem('region') || 'CN'
                };
            }
            
            return config;
        });
​
        request.interceptors.response.use(response => {
            // 处理多语言响应
            if (response.headers['content-language']) {
                response.data = this.translateResponse(
                    response.data,
                    response.headers['content-language']
                );
            }
            return response;
        });
    },
​
    translateResponse(data, language) {
        // 实现响应数据的翻译逻辑
        return data;
    }
};

4. 性能优化实践

4.1 请求合并(Request Batching)

class RequestBatcher {
    constructor(delay = 50, maxBatchSize = 10) {
        this.delay = delay;
        this.maxBatchSize = maxBatchSize;
        this.queue = [];
        this.timeout = null;
    }
​
    add(request) {
        return new Promise((resolve, reject) => {
            this.queue.push({
                request,
                resolve,
                reject
            });
​
            if (this.queue.length >= this.maxBatchSize) {
                this.flush();
            } else if (!this.timeout) {
                this.timeout = setTimeout(() => this.flush(), this.delay);
            }
        });
    }
​
    async flush() {
        if (this.timeout) {
            clearTimeout(this.timeout);
            this.timeout = null;
        }
​
        const batch = this.queue.splice(0, this.maxBatchSize);
        if (batch.length === 0) return;
​
        try {
            const responses = await request.post('/api/batch', {
                requests: batch.map(item => item.request)
            });
​
            batch.forEach((item, index) => {
                item.resolve(responses[index]);
            });
        } catch (error) {
            batch.forEach(item => {
                item.reject(error);
            });
        }
    }
}

4.2 预请求与预加载

class PreloadManager {
    constructor() {
        this.preloadCache = new Map();
        this.setupInterceptor();
    }

    setupInterceptor() {
        request.interceptors.request.use(async config => {
            if (config.preload) {
                const cacheKey = this.generateCacheKey(config);
                const preloadedData = this.preloadCache.get(cacheKey);
                
                if (preloadedData) {
                    this.preloadCache.delete(cacheKey);
                    return Promise.resolve(preloadedData);
                }
            }
            return config;
        });
    }

    preload(configs) {
        configs.forEach(config => {
            request(config).then(response => {
                const cacheKey = this.generateCacheKey(config);
                this.preloadCache.set(cacheKey, response);
            });
        });
    }

    generateCacheKey(config) {
        return `${config.url}-${JSON.stringify(config.params)}`;
    }
}

5. 安全性实践

5.1 XSS防护

const securityInterceptor = {
    setupXSSProtection() {
        request.interceptors.request.use(config => {
            // 添加安全头
            config.headers['X-XSS-Protection'] = '1; mode=block';
            config.headers['X-Content-Type-Options'] = 'nosniff';
            
            // 对请求数据进行净化
            if (config.data) {
                config.data = this.sanitizeData(config.data);
            }
            
            return config;
        });
    },

    sanitizeData(data) {
        if (typeof data === 'string') {
            return this.escapeHTML(data);
        }
        if (typeof data === 'object') {
            return Object.keys(data).reduce((acc, key) => {
                acc[key] = this.sanitizeData(data[key]);
                return acc;
            }, Array.isArray(data) ? [] : {});
        }
        return data;
    },

    escapeHTML(str) {
        return str.replace(/[&<>"']/g, char => ({
            '&': '&amp;',
            '<': '&lt;',
            '>': '&gt;',
            '"': '&quot;',
            "'": '&#39;'
        }[char]));
    }
};

5.2 CSRF防护

const csrfProtection = {
    setup() {
        request.interceptors.request.use(config => {
            // 从cookie中获取CSRF token
            const token = this.getCSRFToken();
            
            if (this.requiresCSRF(config.method)) {
                config.headers['X-CSRF-TOKEN'] = token;
            }
            
            return config;
        });
    },

    requiresCSRF(method) {
        // 这些方法需要CSRF保护
        return ['post', 'put', 'delete', 'patch'].includes(method.toLowerCase());
    },

    getCSRFToken() {
        return document.querySelector('meta[name="csrf-token"]')?.content;
    }
};

6. 监控与日志

6.1 请求监控系统

class RequestMonitor {
    constructor() {
        this.metrics = {
            totalRequests: 0,
            failedRequests: 0,
            averageResponseTime: 0
        };
        this.setupMonitoring();
    }

    setupMonitoring() {
        request.interceptors.request.use(config => {
            config.metadata = { startTime: new Date() };
            this.metrics.totalRequests++;
            return config;
        });

        request.interceptors.response.use(
            response => {
                this.handleSuccessfulRequest(response);
                return response;
            },
            error => {
                this.handleFailedRequest(error);
                return Promise.reject(error);
            }
        );
    }

    handleSuccessfulRequest(response) {
        const duration = new Date() - response.config.metadata.startTime;
        this.updateAverageResponseTime(duration);
        this.logRequest(response.config, duration, true);
    }

    handleFailedRequest(error) {
        this.metrics.failedRequests++;
        const duration = new Date() - error.config.metadata.startTime;
        this.logRequest(error.config, duration, false, error);
        
        // 发送错误报告到监控系统
        this.reportError(error);
    }

    updateAverageResponseTime(duration) {
        const total = this.metrics.totalRequests;
        this.metrics.averageResponseTime = 
            (this.metrics.averageResponseTime * (total - 1) + duration) / total;
    }

    logRequest(config, duration, success, error = null) {
        const logData = {
            timestamp: new Date().toISOString(),
            url: config.url,
            method: config.method,
            duration,
            success,
            error: error ? {
                message: error.message,
                code: error.response?.status
            } : null
        };
        
        console.log('Request Log:', logData);
        
        // 存储日志
        this.storeLog(logData);
    }

    async storeLog(logData) {
        try {
            // 使用 IndexedDB 存储日志
            const db = await this.getLogDatabase();
            const tx = db.transaction('logs', 'readwrite');
            await tx.objectStore('logs').add(logData);
        } catch (error) {
            console.error('Error storing log:', error);
        }
    }

    getMetrics() {
        return {
            ...this.metrics,
            successRate: ((this.metrics.totalRequests - this.metrics.failedRequests) / 
                         this.metrics.totalRequests * 100).toFixed(2) + '%'
        };
    }
}

6.2 性能追踪系统

class PerformanceTracker {
    constructor() {
        this.traces = new Map();
        this.setupTracing();
    }

    setupTracing() {
        request.interceptors.request.use(config => {
            // 创建性能追踪记录
            const traceId = this.generateTraceId();
            config.traceId = traceId;
            
            this.traces.set(traceId, {
                startTime: performance.now(),
                url: config.url,
                method: config.method,
                phases: []
            });
            
            return config;
        });

        request.interceptors.response.use(
            response => {
                this.completeTrace(response.config.traceId, 'success');
                return response;
            },
            error => {
                this.completeTrace(error.config.traceId, 'error');
                return Promise.reject(error);
            }
        );
    }

    generateTraceId() {
        return `trace-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
    }

    addPhase(traceId, phaseName) {
        const trace = this.traces.get(traceId);
        if (trace) {
            trace.phases.push({
                name: phaseName,
                timestamp: performance.now() - trace.startTime
            });
        }
    }

    completeTrace(traceId, status) {
        const trace = this.traces.get(traceId);
        if (trace) {
            trace.duration = performance.now() - trace.startTime;
            trace.status = status;
            
            // 发送性能数据到分析系统
            this.reportPerformanceData(trace);
            
            // 清理trace数据
            this.traces.delete(traceId);
        }
    }

    reportPerformanceData(trace) {
        // 实现将性能数据发送到分析系统的逻辑
    }
}

7. 高级应用场景

7.1 GraphQL请求处理

class GraphQLInterceptor {
    constructor() {
        this.setupInterceptor();
    }

    setupInterceptor() {
        request.interceptors.request.use(config => {
            if (config.graphql) {
                return this.transformGraphQLRequest(config);
            }
            return config;
        });

        request.interceptors.response.use(response => {
            if (response.config.graphql) {
                return this.transformGraphQLResponse(response);
            }
            return response;
        });
    }

    transformGraphQLRequest(config) {
        const { query, variables } = config.graphql;
        
        return {
            ...config,
            method: 'POST',
            url: '/graphql',
            data: {
                query,
                variables
            }
        };
    }

    transformGraphQLResponse(response) {
        if (response.data.errors) {
            return Promise.reject({
                message: 'GraphQL Error',
                errors: response.data.errors
            });
        }
        
        return response.data.data;
    }
}

7.2 WebSocket 集成

class WebSocketInterceptor {
    constructor(wsUrl) {
        this.wsUrl = wsUrl;
        this.ws = null;
        this.setupInterceptor();
    }

    setupInterceptor() {
        request.interceptors.request.use(async config => {
            if (config.useWebSocket) {
                return this.handleWebSocketRequest(config);
            }
            return config;
        });
    }

    async handleWebSocketRequest(config) {
        if (!this.ws) {
            await this.connect();
        }

        return new Promise((resolve, reject) => {
            const messageId = this.generateMessageId();
            
            const timeout = setTimeout(() => {
                reject(new Error('WebSocket request timeout'));
            }, config.timeout || 5000);

            this.ws.send(JSON.stringify({
                id: messageId,
                type: config.method,
                path: config.url,
                data: config.data
            }));

            this.ws.addEventListener('message', function handler(event) {
                const response = JSON.parse(event.data);
                if (response.id === messageId) {
                    clearTimeout(timeout);
                    this.ws.removeEventListener('message', handler);
                    resolve(response.data);
                }
            });
        });
    }

    async connect() {
        return new Promise((resolve, reject) => {
            this.ws = new WebSocket(this.wsUrl);
            
            this.ws.onopen = () => resolve();
            this.ws.onerror = (error) => reject(error);
            
            this.setupHeartbeat();
        });
    }

    setupHeartbeat() {
        setInterval(() => {
            if (this.ws?.readyState === WebSocket.OPEN) {
                this.ws.send(JSON.stringify({ type: 'ping' }));
            }
        }, 30000);
    }
}

7.3 离线请求队列

class OfflineRequestQueue {
    constructor() {
        this.queue = [];
        this.setupInterceptor();
        this.setupNetworkListener();
    }

    setupInterceptor() {
        request.interceptors.request.use(async config => {
            if (!navigator.onLine) {
                return this.queueRequest(config);
            }
            return config;
        });
    }

    setupNetworkListener() {
        window.addEventListener('online', () => {
            this.processQueue();
        });
    }

    async queueRequest(config) {
        // 存储请求到 IndexedDB
        await this.storeRequest(config);
        
        this.queue.push(config);
        
        // 如果请求需要立即返回结果
        if (config.offlineResponse) {
            return Promise.resolve(config.offlineResponse);
        }
        
        return Promise.reject(new Error('Device is offline'));
    }

    async processQueue() {
        const requests = await this.getStoredRequests();
        
        for (const config of requests) {
            try {
                await request(config);
                await this.removeRequest(config.id);
            } catch (error) {
                console.error('Error processing queued request:', error);
            }
        }
    }

    async storeRequest(config) {
        const db = await this.getDatabase();
        const tx = db.transaction('requests', 'readwrite');
        await tx.objectStore('requests').add({
            id: config.id,
            config: config,
            timestamp: Date.now()
        });
    }
}

8. 微服务架构支持

class MicroserviceInterceptor {
    constructor(serviceRegistry) {
        this.serviceRegistry = serviceRegistry;
        this.setupInterceptor();
    }

    setupInterceptor() {
        request.interceptors.request.use(async config => {
            const service = this.getServiceFromUrl(config.url);
            
            if (service) {
                config.baseURL = await this.serviceRegistry.getServiceUrl(service);
                config.headers['X-Service-Name'] = service;
            }
            
            return config;
        });
    }

    getServiceFromUrl(url) {
        // 从URL中提取服务名称
        const match = url.match(/^\/([^\/]+)/);
        return match ? match[1] : null;
    }
}

// 服务注册中心
class ServiceRegistry {
    constructor() {
        this.services = new Map();
        this.healthChecks = new Map();
    }

    registerService(name, url, healthCheckUrl) {
        this.services.set(name, url);
        if (healthCheckUrl) {
            this.healthChecks.set(name, healthCheckUrl);
            this.startHealthCheck(name);
        }
    }

    async getServiceUrl(name) {
        return this.services.get(name);
    }

    startHealthCheck(name) {
        setInterval(async () => {
            try {
                await request.get(this.healthChecks.get(name));
            } catch (error) {
                this.handleServiceFailure(name);
            }
        }, 30000);
    }
}

9. 参考文献

  1. Fielding, R. T. (2020). "Architectural Styles and the Design of Network-based Software Architectures." ACM Transactions on Internet Technology, 2(2), 115-150.

  2. Newman, S. (2021). Building Microservices: Designing Fine-Grained Systems. O'Reilly Media.

  3. Grigorik, I. (2023). High Performance Browser Networking. O'Reilly Media.

  4. Osmani, A. (2023). "Learning Progressive Web Apps." In Web Performance in Practice. Addison-Wesley Professional.

  5. Howard, M., & Lipner, S. (2023). The Security Development Lifecycle. Microsoft Press.

  6. Nygard, M. T. (2023). Release It!: Design and Deploy Production-Ready Software. Pragmatic Bookshelf.

10. 总结

请求拦截器作为前端应用的核心组件,其重要性将随着Web应用的复杂度增加而不断提升。通过本文提供的各种实现示例和最佳实践,开发者可以构建更加健壮、安全、高效的Web应用。持续关注新的研究成果和实践经验,对于提升应用质量至关重要。


http://www.kler.cn/news/366198.html

相关文章:

  • 10.25学习
  • 基于neo4j的新冠治疗和新冠患者轨迹的知识图谱问答系统
  • Jenkins学习笔记
  • Nginx、Tomcat等项目部署问题及解决方案详解
  • 使用Prometheus对微服务性能自定义指标监控
  • 个性化头像新选择:A1快速定制你的专属头像
  • Android中的epoll机制
  • 怿星科技薛春宇丨智能汽车软件研发工具链国产化的挑战和探索
  • Adobe Acrobat导致的电脑虚拟内存占用过大,开机一段时间后卡顿并自动关机
  • Web刷题日记1---清风
  • 【Android】Android 性能分析利器 - Perfetto
  • 滚珠丝杆的精度级别如何分?
  • 912.排序数组(快速排序)
  • 尝鲜electron --将已有vue/react项目转换为桌面应用
  • 测试和开发工作必备的17个Python自动化代码
  • vue axios请求二次封装以及解释(直接cv实用版)
  • kubenetes/kubesphere搭建报错
  • onlyoffice docker启用jwt并生成jwt
  • 机器学习与金融风控项目篇-day03-业务规则挖掘与特征构建-特征衍生
  • 探索 Lombok 的 @Builder 和 @SuperBuilder:避坑指南(一)
  • Windows10搭建Spark3.4.3安装教程
  • OAK相机的RGB-D彩色相机去畸变做对齐
  • 【动态规划】【简单多状态dp问题】买卖股票相关问题(冷冻期、手续费、限制次数)
  • MATLAB基础应用精讲-【数模应用】本量利分析(Cost-Volume-Profit Analysis)
  • 【论文阅读】ESRGAN+
  • 项目管理软件中这6个小技巧帮助项目经理同时管理多个项目