Sending duplicate requests occurs when intercepting HTTP responses

Upon observation, it has been noted that there is a duplicate triggering of the request when intercepting HTTP response and using subscribe to retrieve the value in the Observable response.

Below is the code snippet:

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

The issue seems to arise from subscribing to the observable response. If .map is used instead of .subscribe, the issue does not occur, but the desired result - like returning header values from the response - is not achieved.

In app.module.ts, we specify the use of 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]
    }
  ],

....

Within the service, the server API call to add a user using the post method appears to be called twice causing the issue of duplication. It should ideally 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, the service is invoked upon button click event to 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 });
      }
      );
  }

References to similar issues mention cold and hot observables, suggesting to use .share to make the observable hot and prevent the issue. Attempts were made in this direction but the solution did not yield successful results.

Answer №1

In your code, the `intercept` method subscribes to an observable and then returns it. However, this results in two subscriptions when the consuming code also subscribes to the same observable.

Having multiple subscriptions for Http-related observables can lead to unnecessary API calls.

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

A better approach is to utilize the `.do()` operator for side effects. This operator allows you to perform work on the value without modifying the Observable type or event value, passing the event down the stream seamlessly.

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

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Using Angular to incorporate HighCharts for a gauge chart

I'm currently working on an Angular project where I need to display a statistic using a gauge chart. The thing is, I'm utilizing the HighCharts library and it's worth mentioning that I've successfully used other types of charts from Hig ...

Transform the date format in react.js using information provided by an API endpoint

I'm currently working on a project using React and TypeScript where I need to format the date retrieved from an API. I am able to map over the data and display it, but I'm struggling to convert it into a format like '22 June 2021'. The ...

Troubleshooting the error message: "Uncaught TypeError: this.schedulerActionCtor is not a constructor" that occurs while executing the rootEpic in redux-

As I delve into learning how redux-observables work with typescript, I've been following a project example and referencing various guides like those found here and here. However, no matter what I try in setting up the epics/middleware, I keep encounte ...

Navigating a text input field in a NextJS application

Having trouble with handling input from a textarea component in a NextJS app. This is the structure of the component: <textarea placeholder={pcHld} value={fldNm} onChange={onChangeVar} className="bg-cyan-300" ...

Guide on checking the presence of an error message post button click using react testing library

Hey there, I'm currently in the process of testing my react app's behavior using @testing-library/react version: 11.2.3 @testing-library/jest-dom version 5.11.9 I'm aiming to observe an error message that appears after a button is clicked. ...

What are the best practices for implementing Alertify in a Typescript project?

I'm relatively new to Angular2 so please bear with me. I attempted to implement Alertify.js in my Angular2 project for a custom dialog box, but I am encountering difficulties getting Alertify to work properly. Since I lack deep knowledge of JavaScrip ...

Improving the App() function in React and Typescipt

When working on my React/Typescript app, I encountered a challenge with the length of the code in one of the sections. Despite watching tutorials and searching for solutions, I couldn't find a clear way to refactor it. The specific issue is with refa ...

Concatenate all sub-items within a JSON object

I have 2 Objects like this : [ { _id: ObjectId("62990f96345ef9001d9f2dfe"), deletedAt: null, expiredAt: ISODate("2022-06-05T19:29:26.746Z"), dataBarang: [ { vendor: ObjectId("6215dd91139c99003fe4c7cd ...

Issue detected in the ngx-joyride package: Error found in ./node_modules/ngx-joyride/assets/images/close.svg

During my build process, I encountered an error with ngx-joyride: ERROR in ./node_modules/ngx-joyride/assets/images/close.svg Module parse failed: Unexpected token (1:0) You may need an appropriate loader to handle this file type." <line x1="1" y1=" ...

Display the information in real-time as it is being transmitted to the backend

I'm facing a challenge with the user experience. Currently, I am developing a chat application using Ionic, Angular, and Firebase. The issue at hand is that messages are only displayed once they are successfully sent to the server. This means that o ...

Is it feasible to retrieve the component's class name by using the selector name in Angular 2?

Is there a way in Angular 2 to retrieve the class name or reference of a component based on its selector name? @Component({ selector: 'selector-1', template: '<h1>Hello</h1>', }) export class Component1 {} @Component({ ...

The functionality to subscribe in ts(6385) has been marked as

I am encountering an error message regarding the deprecation of the subscribe function in my code. The issue seems to be with the second part of the code for the getStarwarsHeroes function, where the .subscribe method is being deprecated. import { Injectab ...

Using Snap SVG in a React application with Next.js and TypeScript

Query I have been attempting to incorporate SnapSVG into my React project, but I am encountering difficulties getting it to function properly from the outset. Can someone provide assistance with the correct configurations? I do not have much experience wi ...

Unexpected behavior in Typescript: variable type remains "unknown" after validation

Here is a code snippet I'm working with: You can view and interact with the code on the Typescript Playground. // this class is imported by the validator.ts module class EWC extends Error { constructor(public message: str... When working with th ...

Using `this` within an object declaration

I am encountering an issue with the following code snippet: const myObj = { reply(text: string, options?: Bot.SendMessageOptions) { return bot.sendMessage(msg.chat.id, text, { reply_to_message_id: msg.message_id, ...options }) ...

Using jscodeshift, transform all named import statements to default import statements for MUI V5

I'm in need of assistance with a jscodeshift script to convert all named imports to default imports for Material-UI version 5 using React and Typescript. import { Button, TextField } from '@mui/material'; The desired result should be: impor ...

What is the best way to convert one array of types to another array of types in Typescript?

Imagine you have the following: type AwesomeTuple = [string, number, boolean] Now, you're looking to transform that type using a generic approach: type AmazingGeneric<T extends any[]> = ... In this scenario: AmazingGeneric<AwesomeType> w ...

Angular 2 rc4's HTTP requests are generating uncaught exceptions (in promise)

Is there a change in Angular 2 HTTP service after upgrading to Angular 2 rc4? I have spent the entire day going through Angular HTTP client documentation and searching on Stack Overflow, but I can't seem to find the solution to my problem. Here are ...

Instance property value driven class property type guard

Is it possible to create a class example that can determine the config type based on the value of animalType instance: enum Animal { BIRD = 'bird', DOG = 'dog', } type Base = { id: number } // Object example type Smth = Base & ...

Having trouble making axios a global instance in my Vue project

Previously, I used to import axios in each Vue component separately when making HTTP requests. An example of this can be seen below: <script lang="ts"> import axios from 'axios'; @Component export default class ExamplePage extend ...