Reputation: 167
It's noticed that the request is triggered twice when we intercept HTTP response and use subscribe to get the value in the Observable response.
Here is the code :
Intercerpting Http Request and Response by extending it (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;
}
}
I believe subscribing to the observable response is causing the issue. If we use .map instad of .subscribe, no issue is reproducible but not getting the desired result like the header values are not returned from the response
In app.module.ts we specify to use HttpService instead of 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]
}
],
....
In the service, we call server API using post method to add a user. This API call is made twice and that is the issue. It should be trigger only once. (User-operation.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();
}
In the user component, we call the service using a button click event and pass the user model. (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 });
}
);
}
I have read about similar issues which explains cold and hot observables and we should use .share to make the obsevable hot and to avoid the issue. I tried that and I had no luck.
Upvotes: 3
Views: 5951
Reputation: 11815
Your intercept
method subscribes to an observable, and returns it. This same exact observable is being subscribed to by the consuming code.
Two subscriptions mean two API calls when it comes to Http-related observables.
intercept(observableResponse: Observable<Response>): Observable<Response> {
observableResponse
.subscribe(response => // <-- pretty bad!
this.handleResponseHeader(response.headers)
);
return observableResponse;
}
What you want to do is use the .do()
operator which is used for side effects. This operator does not modify the Observable type nor event value, just "unwraps" it, performs some work on the value, and passes the event down the stream.
intercept(observableResponse: Observable<Response>): Observable<Response> {
return observableResponse
.do(response => this.handleResponseHeader(response.headers));
}
Upvotes: 4