默认和特定请求超时

通常需要有默认的超时(例如30秒) ,这将应用于所有的请求,并且可以覆盖特定的 更久请求(例如600秒)。

据我所知,在 Http服务中没有指定默认超时的好方法。

HttpClient服务中如何处理这个问题?如何定义所有传出请求的默认超时,可以为特定请求重写?

140440 次浏览

Using the new HttpClient you can try some thing like this

@Injectable()
export class AngularInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(req).timeout(5000).do(event => {}, err => { // timeout of 5000 ms
if(err instanceof HttpErrorResponse){
console.log("Error Caught By Interceptor");
//Observable.throw(err);
}
});
}
}

Adding a timeout to the next.handle(req) which is passed on.

Registering it in AppModule like

@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
HttpClientModule
],
providers: [
[ { provide: HTTP_INTERCEPTORS, useClass:
AngularInterceptor, multi: true } ]
],
bootstrap: [AppComponent]
})
export class AppModule {
}

You could create a global interceptor with the base timeout value as follows:

import { Injectable } from '@angular/core';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest} from '@angular/common/http';


@Injectable()
export class AngularInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(req).timeout(30000, Observable.throw("Request timed out"));
// 30000 (30s) would be the global default for example
}
}

Afterwards you need to register this injectable in the providers array of you root module.

The tricky part would be to override the default time (increase/decrease) for specific requests. For the moment I dont know how to solve this.

It appears that without extending HttpClientModule classes, the only expected ways for interceptors to communicate with respective requests are params and headers objects.

Since timeout value is scalar, it can be safely provided as a custom header to the interceptor, where it can be decided if it's default or specific timeout that should be applied via RxJS timeout operator:

import { Inject, Injectable, InjectionToken } from '@angular/core';
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Observable } from 'rxjs';
import { timeout } from 'rxjs/operators';


export const DEFAULT_TIMEOUT = new InjectionToken<number>('defaultTimeout');


@Injectable()
export class TimeoutInterceptor implements HttpInterceptor {
constructor(@Inject(DEFAULT_TIMEOUT) protected defaultTimeout: number) {
}


intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const timeoutValue = req.headers.get('timeout') || this.defaultTimeout;
const timeoutValueNumeric = Number(timeoutValue);


return next.handle(req).pipe(timeout(timeoutValueNumeric));
}
}

This can be configured in your app module like:

providers: [
[{ provide: HTTP_INTERCEPTORS, useClass: TimeoutInterceptor, multi: true }],
[{ provide: DEFAULT_TIMEOUT, useValue: 30000 }]
],

The request is then done with a custom timeout header

http.get('/your/url/here', { headers: new HttpHeaders({ timeout: `${20000}` }) });

Since headers are supposed to be strings, the timeout value should be converted to a string first.

Here is a demo.

Credits go to @RahulSingh and @Jota.Toledo for suggesting the idea of using interceptors with timeout.

In complement to the other answers, just beware that if you use the proxy config on the dev machine, the proxy's default timeout is 120 seconds (2 minutes). For longer requests, you'll need to define a higher value in the configuration, or else none of these answers will work.

{
"/api": {
"target": "http://localhost:3000",
"secure": false,
"timeout": 360000
}
}