【问题标题】:Default and specific request timeout默认和特定请求超时
【发布时间】:2017-08-29 12:15:51
【问题描述】:

通常希望有默认超时(例如 30 秒),它将应用于所有请求,并且可以为特定的更长请求(例如 600 秒)覆盖。

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

HttpClient 服务中解决这个问题的方法是什么?如何为所有传出请求定义默认超时,可以为特定请求覆盖?

【问题讨论】:

  • @neuhaus 它不是 angularjs 它是 angular,不是重复的
  • 这里可以使用超时算子吗?
  • @RahulSingh 这是在 Http 中完成的方式,这种方式需要为每个请求指定.timeout(...),而不是默认情况下。

标签: angular angular4-httpclient


【解决方案1】:

看来,如果不扩展 HttpClientModule 类,拦截器与相应请求进行通信的唯一预期方式是 paramsheaders 对象。

由于超时值是标量,它可以安全地作为自定义标头提供给拦截器,可以通过 RxJS timeout 运算符决定是默认超时还是特定超时:

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));
  }
}

这可以在您的应用模块中进行配置,例如:

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

然后使用自定义 timeout 标头完成请求

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

由于标头应该是字符串,所以应该先将超时值转换为字符串。

这里是a demo

感谢@RahulSingh 和@Jota.Toledo 提出了将拦截器与timeout 一起使用的想法。

【讨论】:

  • @Jota.Toledo 我不认为单独的 observables 可以提供帮助。一旦一个 observable 在拦截器中与.timeout(defaultTimeout) 操作符链接,就不可能“取消”它并与.timeout(customTimeout) 链接。这可能可以通过子类化 Observable 来实现,但它也很麻烦和脆弱。希望有一天这会在 HttpClient 本身中得到解决。 AngularJS $http 有超时选项,效果很好。
  • 是的,我知道这一点。我尝试使用基于 SO 答案的 mergeMap 和其他运算符来实现某些东西,但是在其中一种情况下(我认为默认值被更大的时间覆盖)我的方法不起作用。尽管我不喜欢使用 HttpHeader 与拦截器进行通信,但我会选择你的方法,但在 API 的当前状态下,我认为没有更好的方法。
  • 是的,因为没有其他选项可以与拦截器交互,所以标题看起来是唯一的,而且还不错。至少语义是正确的,标头应该携带有关请求的信息。希望这将在未来的版本中得到解决。
  • 此代码无法将超时请求增加到大于 30 秒。如果将其设置为 60 秒,则将应用 Angular 的默认 30 秒
  • @user1034912 Afaik 是的,Angular 没有提供更具体的实现方式
【解决方案2】:

作为对其他答案的补充,请注意,如果您在开发机器上使用 proxy config,代理的默认超时为 120 秒(2 分钟)。对于更长的请求,您需要在配置中定义更高的值,否则这些答案都不起作用。

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

【讨论】:

  • 感谢您添加此内容,当我阅读此答案时,我才意识到我正在使用代理!
  • 感谢您的精彩回答。我可以知道默认超时值记录在哪里吗?我在 Angular 和 webpack 文档中都找不到它。
  • 感谢这个精彩的回答,只有我看到这个时我才明白这一点
  • 这应该在开发服务器上完成吗?
  • 这在生产服务器上不起作用。有什么解决办法吗?
【解决方案3】:

您可以创建一个全局拦截器,其基本超时值如下:

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
  }
}

之后你需要在你的根模块的 providers 数组中注册这个可注入对象。

棘手的部分是覆盖特定请求的默认时间(增加/减少)。暂时不知道怎么解决。

【讨论】:

    【解决方案4】:

    使用新的 HttpClient 你可以尝试这样的事情

    @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);
            }
        });
      }
    }
    

    为传递的next.handle(req) 添加超时。

    在 AppModule 中像这样注册

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

    【讨论】:

    • 谢谢。但是,正如另一个答案中提到的那样,以这种方式增加特定请求的超时似乎是不可能的。
    • @estus 是的,到目前为止,这是最困难的部分,我认为没有任何直接的解决方案可以解决它。会注意的。另一个答案并没有太大的不同。超时后刚刚添加了一次投掷
    猜你喜欢
    • 2014-07-18
    • 2013-02-17
    • 1970-01-01
    • 2011-02-04
    • 1970-01-01
    • 1970-01-01
    • 2022-01-14
    • 1970-01-01
    相关资源
    最近更新 更多