Angular 4中如何为XmlHttpRequest请求启用HttpInterceptor拦截器
嘿,这个问题我碰到过好几次——Angular的HttpInterceptor确实只对HttpClient发起的请求生效,原生XmlHttpRequest完全绕开了Angular的HTTP客户端层,所以拦截器自然不会触发。下面给你两种可行的解决方案,看哪种更适合你的场景:
方案1:改用HttpClient上传文件(推荐)
如果没有特殊必须用XHR的理由,优先用Angular的HttpClient来处理文件上传,这样就能直接复用你已有的HttpInterceptor,完全不用额外改动。示例代码如下:
// 组件中的文件上传方法 uploadFile(file: File): Observable<any> { const formData = new FormData(); formData.append('file', file); // 用HttpClient发起请求,拦截器会自动处理请求头 return this.http.post('/api/upload', formData, { reportProgress: true, // 可选:监听上传进度 observe: 'events' }); }
这样一来,你的HttpInterceptor会像处理普通请求一样,自动给这个上传请求添加所需的请求头。
方案2:封装XHR请求,复用拦截器逻辑
如果因为某些特殊需求必须使用原生XHR,那你可以封装一个XHR服务,手动把拦截器的逻辑应用到XHR请求上,避免重复代码。
步骤1:提取拦截器公共逻辑到共享服务
比如你的拦截器主要负责添加认证头,把这部分逻辑抽成独立服务:
@Injectable({ providedIn: 'root' }) export class HttpHeaderService { getAuthHeaders(): HttpHeaders { const token = localStorage.getItem('authToken'); return new HttpHeaders().set('Authorization', `Bearer ${token}`); // 这里可以添加其他你需要的请求头 } }
步骤2:封装XHR服务,应用头信息
@Injectable({ providedIn: 'root' }) export class XhrService { constructor(private headerService: HttpHeaderService) {} uploadFile(url: string, file: File): Observable<ProgressEvent> { return new Observable(observer => { const xhr = new XMLHttpRequest(); xhr.open('POST', url); // 从共享服务获取并设置请求头 const headers = this.headerService.getAuthHeaders(); headers.keys().forEach(key => { xhr.setRequestHeader(key, headers.get(key)!); }); // 处理上传进度和结果 xhr.upload.addEventListener('progress', (event) => { observer.next(event); }); xhr.addEventListener('load', () => { if (xhr.status >= 200 && xhr.status < 300) { observer.next(xhr.response); observer.complete(); } else { observer.error(new Error(xhr.statusText)); } }); xhr.addEventListener('error', () => { observer.error(new Error('Network error')); }); // 发送FormData格式的文件 const formData = new FormData(); formData.append('file', file); xhr.send(formData); // 取消订阅时终止请求 return () => xhr.abort(); }); } }
之后在组件里调用这个XhrService的uploadFile方法,就能自动带上你需要的请求头,而且逻辑和拦截器保持一致。
进阶:直接复用HttpInterceptor实例
如果你的拦截器逻辑比较复杂(比如包含重试、错误处理等),可以直接注入拦截器,把XHR请求转化为Angular的HttpRequest对象,经过拦截器处理后再应用到XHR上:
@Injectable({ providedIn: 'root' }) export class XhrService { constructor( private interceptors: HttpInterceptor[], private httpHandler: HttpBackend ) {} uploadFile(url: string, file: File): Observable<any> { const formData = new FormData(); formData.append('file', file); // 创建Angular HttpRequest对象 const req = new HttpRequest('POST', url, formData, { reportProgress: true }); // 手动执行拦截器链 let handler: HttpHandler = this.httpHandler; this.interceptors.forEach(interceptor => { handler = new HttpInterceptorHandler(handler, interceptor); }); // 处理拦截后的请求,应用到XHR return handler.handle(req).pipe( switchMap(interceptedReq => { return new Observable(observer => { const xhr = new XMLHttpRequest(); xhr.open(interceptedReq.method, interceptedReq.urlWithParams); // 应用拦截后的请求头 interceptedReq.headers.keys().forEach(key => { xhr.setRequestHeader(key, interceptedReq.headers.get(key)!); }); // 处理进度和结果(和前面的XHR逻辑一致) xhr.upload.addEventListener('progress', (event) => { observer.next(event); }); xhr.addEventListener('load', () => { observer.next(xhr.response); observer.complete(); }); xhr.send(interceptedReq.body); return () => xhr.abort(); }); }) ); } } // 实现Angular内部的HttpInterceptorHandler类 class HttpInterceptorHandler implements HttpHandler { constructor(private next: HttpHandler, private interceptor: HttpInterceptor) {} handle(req: HttpRequest<any>): Observable<HttpEvent<any>> { return this.interceptor.intercept(req, this.next); } }
这种方式能完全复用你已有的所有拦截器逻辑,但实现起来稍复杂,适合拦截器逻辑较多的场景。
内容的提问来源于stack exchange,提问作者vik
相关产品推荐
相关产品推荐

