Having issues updating legacy Angular project use old HTTP to new HTTPClient for basic auth

Viewed 29

This is in an Angular 7 project and the code was written maybe 5-6 years ago, and I'm trying to update the application to latest Angular. Right now I'm just testing the login portion of the app which is basic http authentication. This is the last piece I need to refactor but im pretty lost. I don't want to break the entire system or do a full revamp just get something working so the whole ui can be upgraded from angular 7 to 14.

This is the old code below.

import { ConnectionBackend, Http, Request, RequestOptions, RequestOptionsArgs, Response, XHRBackend } from '@angular/http';
import { AuthService } from './../auth/auth.service';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
@Injectable()
export class AuthHttp extends Http {
    constructor(backend: XHRBackend, defaultOptions: RequestOptions, private auth : AuthService) {
        super(backend, defaultOptions);
        this.auth = auth;
    }
    request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
        // hack to deal with compatibility issue of rxjs versions
        // see https://stackoverflow.com/questions/38168581/observablet-is-not-a-class-derived-from-observablet
        let optionsAny = <any>options;
        let opts: RequestOptionsArgs = {
            method: optionsAny.method,
            body: optionsAny.body,
            withCredentials: true,
            headers: optionsAny.headers,
        }
        let query = '';
        for (let k of Array.from(optionsAny.params.paramsMap.keys())) {
            let v = optionsAny.params.paramsMap.get(k);
            query += '&' + k + '=' + encodeURIComponent(v);
        }
        if (query) {
            url = url + '?' + query.substring(1);
        }
        return Observable.create(sub => {
            super.request(url, opts).subscribe(
                res => { sub.next(res); },
                err => {
                    if (err.status === 401) {
                        this.auth.signout();
                    } else {
                        sub.error(err);
                    }
                } );
        });
    }
}

I tried some stuff but it all seems to lead to no where. I'm not really sure where to start. This is my first time using angular and it's a decently sized project. I feel like there's a really simple way to do this, but I'm not totally sure, I've been researching and haven't found anyone quite doing anything like this.

It compiles with no errors with an empty class. Like below, these imports are all the equivalent or roughly equivalent for the new angular httpclientmodule I believe .

import { AuthService } from './../auth/auth.service';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { HttpClient, HttpParams, HttpHeaders, HttpXhrBackend, HttpResponse, HttpBackend, HttpRequest, HttpEvent } from '@angular/common/http';
@Injectable()
export class AuthHttp extends HttpClient {}

Then I get 2 errors below in the console of my browser when i do ng serve with the blank class above.

ERROR NullInjectorError: R3InjectorError(AppModule)[ApiService -> ApiService -> ApiService]: 
  NullInjectorError: No provider for ApiService!
    Angular 9
    AppComponent_Factory app.component.ts:11
    Angular 26
    631 main.ts:11
    Webpack 7
core.mjs:6362:22
    Angular 16

Any help on where to start or helpful resource is appreciated thank you.

This is how ApiService is implemented

@Injectable()
export class ApiService {
  private apiUrl: string;

  constructor(private authHttp: AuthHttp, private auth: AuthService) {}

  getUrl() {
    return this.apiUrl;
  }

  setUrl(url: string) {
    this.apiUrl = url;
  }

  getConfiguration(): Configuration {
    const token = this.auth.getToken();
    return new Configuration({
      accessToken: token
    });
  }

  getUserApi(): UserService {
    return new UserService(this.authHttp, this.apiUrl, this.getConfiguration());
  }

  getProductionApi(): ProductionService {
    return new ProductionService(
      this.authHttp,
      this.apiUrl,
      this.getConfiguration()
    );
  }
2 Answers

Without seeing the implementation of ApiService and its difficult to identify the issue. Broadly speaking it seems to me like the ApiService isn't provided from any module.

A quick check would be to provide it to the root of the application. @Injectable({ providedIn: 'root', }) export class ApiService ....

Ok I got it working but I'm still not totally sure how it's working or what it's doing. Basically the old method was doing this in a complicated why because of the limitations of older angular 2.

@Injectable()
export class AuthHttp extends HttpClient implements HttpInterceptor {
    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        const authReq = req.clone({
            withCredentials: true
        });
        return next.handle(authReq);
    }
}
Related