【问题标题】:Request is sent twice when intercepting http response拦截http响应时请求发送两次
【发布时间】:2018-03-28 23:40:49
【问题描述】:

注意到在我们拦截HTTP响应并使用订阅获取Observable响应中的值时,请求被触发了两次。

代码如下:

通过扩展拦截 Http 请求和响应 (http.service.ts)

import { Injectable } from '@angular/core';
import { Http, XHRBackend, RequestOptions, Request, RequestOptionsArgs, Response, Headers, ConnectionBackend } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import { LoggedInUserApi } from './loggedInUser.service';

@Injectable()
export class HttpService extends Http {

    constructor(private loggedInUserApi: LoggedInUserApi, backend: XHRBackend, options: RequestOptions) {
        super(backend, options);
    }

    request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
        return this.intercept(super.request(url, options));
    }

    get(url: string, options?: RequestOptionsArgs): Observable<Response> {
        return this.intercept(super.get(url, options));
    }

    post(url: string, body: string, options?: RequestOptionsArgs): Observable<Response> {
        return this.intercept(super.post(url, body, options));
    }

    put(url: string, body: string, options?: RequestOptionsArgs): Observable<Response> {
        return this.intercept(super.put(url, body, options));
    }

    delete(url: string, options?: RequestOptionsArgs): Observable<Response> {
        return this.intercept(super.delete(url, options));
    }
    handleResponseHeader(header) {
        console.log(header);
    }
    intercept(observableResponse: Observable<Response>): Observable<Response> {
        observableResponse.subscribe(response => this.handleResponseHeader(response.headers));
        return observableResponse;
    }
}

我认为订阅可观察响应会导致问题。如果我们使用 .map 而不是 .subscribe,则不会出现可重现的问题,但无法获得所需的结果,例如响应中未返回标头值

在 app.module.ts 我们指定使用 HttpService 而不是 Http (app.module.ts)

.....
 providers: [
  ......
    {
      provide: Http,
      useFactory: (loggedInUserApi: service.LoggedInUserApi, xhrBackend: XHRBackend, requestOptions: RequestOptions) =>
        new service.HttpService(loggedInUserApi, xhrBackend, requestOptions),
      deps: [service.LoggedInUserApi, XHRBackend, RequestOptions]
    }
  ],

....

在服务中,我们使用 post 方法调用服务器 API 来添加用户。此 API 调用进行了两次,这就是问题所在。它应该只触发一次。 (用户操作.service.ts)

  public addUser(body: models.User, extraHttpRequestParams?: any): Observable<models.User> {
        // verify required parameter 'body' is not null or undefined
        if (body === null || body === undefined) {
            throw new Error('Required parameter body was null or undefined when calling addUser.');
        }

        const path = this.basePath + '/user';

        let queryParameters = new URLSearchParams();
        let headerParams = new Headers({ 'Content-Type': 'application/json' });

        let requestOptions: RequestOptionsArgs = {
            method: 'POST',
            headers: headerParams,
            search: queryParameters
        };
        requestOptions.body = JSON.stringify(body);

        return this.http.request(path, requestOptions)
            .map((response: Response) => {
                if (response.status === 204) {
                    return undefined;
                } else {
                    return response.json();
                }
            }).share();
    }

在用户组件中,我们使用按钮点击事件调用服务并传递用户模型。 (User.component.ts)

addUser(event) {
    // To add user using api
    this.busy = this.api.addUser(this.user)
      .subscribe(
      () => {
        DialogService.displayStatusMessage({ message: 'User configurations saved successfully.', type: 'success' });
        this.router.navigate(['users']);
      },
      (error: any) => {
        throw ({ message: error.json().message });
      }
      );
  }

我已经阅读过类似的问题,这些问题解释了冷和热可观察对象,我们应该使用 .share 使可观察对象变热并避免该问题。我试过了,但我没有运气。

【问题讨论】:

    标签: angular typescript rxjs


    【解决方案1】:

    您的intercept 方法订阅了一个可观察对象,并将其返回。消费代码正在订阅同样的 observable。

    当涉及到 Http 相关的 observable 时,两个订阅意味着两个 API 调用。

    intercept(observableResponse: Observable<Response>): Observable<Response> {
        observableResponse
          .subscribe(response =>                           // <-- pretty bad!
            this.handleResponseHeader(response.headers)
          );
        return observableResponse;
    }
    

    您想要做的是使用用于副作用的.do() 运算符。该操作符不会修改 Observable 类型或事件值,只是“解包”它,对值执行一些工作,然后将事件传递到流中。

    intercept(observableResponse: Observable<Response>): Observable<Response> {
        return observableResponse
          .do(response => this.handleResponseHeader(response.headers));
    }
    

    【讨论】:

    • 感谢 Igor Soloydenko 的回复。 'do' 解决了问题,但 handleResponseHeader(response.headers) 函数从未触发。 .map 也注意到了相同的行为,其中多个 api 调用没有被注意到但 handleResponseHeader 函数从未触发..
    • 你有一个 plunker 作为 repro 吗? do & map 都应该工作。您应该不惜一切代价避免重复订阅。
    • 对不起,我的错!!有效。十分感谢。问题是我在返回之前应用了 do。
    • 非常感谢..!!我为此苦苦挣扎。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-09-09
    • 1970-01-01
    • 2018-12-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-18
    相关资源
    最近更新 更多