2

I'm using Angular 6 and the issue is when I try to use an interceptor as an app module provider together with the APP_INITIALIZER to get the json config file from local.

My app module fragment is:

../src/app/app.module.ts (the providers fragment)

providers: [
  AppConfigService,
  {
    provide: APP_INITIALIZER,
    useFactory: initializeApp,
    deps: [AppConfigService], multi:  true
  },
  { provide: HTTP_INTERCEPTORS, useClass: TokenInterceptor, multi: true },
  { provide: LOCALE_ID, useValue: 'ES' },
  Title,
]

It's interesting that if I remove the interceptors, the code works well bringing the config file. My code to get the config file is:

../src/app/_services/app-config.service.ts

import { Injectable } from '@angular/core';
import { Response } from '@angular/http';
import { HttpClient } from '@angular/common/http';
import { environment } from '../../environments/environment.dev';
import { IAppConfig } from '../_models/_interfaces/app-config.model';

@Injectable()
export class AppConfigService {

static settings: IAppConfig;

constructor(private http: HttpClient) {}

load() {
    const jsonFile = `../../assets/config/config.${environment.name}.json`;
    return new Promise<void>((resolve, reject) => {
        this.http.get(jsonFile).toPromise().then((response: Response) => {
           AppConfigService.settings = <IAppConfig>response;
           resolve();
        }).catch((response: any) => {
           reject(`Could not load file '${jsonFile}': ${JSON.stringify(response)}`);
        });
    });
  }
}

Finally, the interceptor code:

../src/app/_interceptors/token.interceptor.ts

import {
  HttpRequest,
  HttpHandler,
  HttpEvent,
  HttpInterceptor
} from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import { AuthService } from '../_services/auth.service';

@Injectable()
export class TokenInterceptor implements HttpInterceptor {

  constructor(public auth: AuthService) {}

  intercept(request: HttpRequest<any>, next: HttpHandler): 
Observable<HttpEvent<any>> {

request = request.clone({
  setHeaders: {
    Authorization: `Bearer ${this.auth.getToken()}`
  }
});

return next.handle(request);

  }
}

When Angular compile the app it returns:

Could not load file '../../assets/config/config.dev.json': {}

I've tried several approaches trying to avoid this issue, but can't solve it until now.

Any tip about it?

Thanks in advance.

Ploppy
  • 14,810
  • 6
  • 41
  • 58
katmel98
  • 81
  • 2
  • 9
  • Please show the request before and after the interceptor. Also, `jsonFile` looks wonky, why are you using `../..` like you are traversing a file system, should it not start with `/assets`? – Rafael Oct 20 '18 at 23:14

1 Answers1

3

When you are using HttpClient to get your configuration file, your interceptor is filtering that request as well. In your code the interceptor is replacing (not appending to existing) all the HTTP headers with Authorization header. When the default Content-Type application/json is lost, the request doesn't work anymore. Save and modify the old headers with request.headers.append(...) before setting them to cloned one.

OR

Define an exception for your file request in the interceptor. One simple way is to check which URL is used in request.url

if (request.url.indexOf('config') > -1) {
    requestModified = request.clone({
       url: request.url,
       headers: request.headers.set('Content-Type', 'application/json')
    });
}
micaro
  • 946
  • 6
  • 10
  • Hi micaro I used your suggestion in my code changing the headers assignment, but it still not work I changed the interceptor to an approach where I take the request headers create a new object copying all current headers keys and adding Autorization Token and Content Type application/json, now if I removed the config service injection all works fine with interceptor adding the jwt token. But if I add the config service it returns me the same error not loading the file from assets ... I don't understand the angular way in this case – katmel98 Oct 21 '18 at 22:28
  • Tricky case. Try to replace current headers with Content-Type only. Don't use Authorization token in request to local file. I'm not sure if that makes any difference. – micaro Oct 22 '18 at 06:40
  • Yeah, really tricky, I replace the interceptor code, change all for "console.log(request)" and if I put as providers both (config service and the interceptor) and it gives me the same error. If I use config service alone, it works bringing me the config file values, and if I use just the interceptor (alone) and it works fine adding jwt token to the request, but if I try both, fails. Maybe it's better If I upload any other code to check it better? – katmel98 Oct 22 '18 at 07:25
  • 1
    I found a viable solution here: https://stackoverflow.com/questions/46469349/how-to-make-an-angular-module-to-ignore-http-interceptor-added-in-a-core-module, I used the second suggestion, I'll check the first one later, I've lost a lot of time with this issue. Thanks micaro for your help. I still don't understand where the issue is following al your logic because it sounds pretty well. – katmel98 Oct 23 '18 at 20:02
  • Nice. HttpBackend injection is something new for me. I am used to define different functions for different types of requests in the interceptor. I have never faced problems in uploading or downloading files before. Live and learn :) – micaro Oct 24 '18 at 13:41