24

I have created an interceptor which appends a token to the authorization header only needed for API calls made within a feature lazy loaded module.

However, I don't think the interceptor is being called as no console.logs are being displayed when within the reports module.

I have read on other questions that this may have something to do with the HTTPClientModule. This HttpClientModule is only ever once initialized in my main app.module.

How do I get an interceptor to work only for a lazy loaded feature module?

auth.interceptor.ts

import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpHandler, HttpRequest, HttpEvent, HttpResponse } from '@angular/common/http';
import { Observable } from 'rxjs/observable';
import 'rxjs/add/operator/do';

import { AuthService } from './../services/auth/auth.service';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {

    constructor(private auth: AuthService) {
        console.log('start interceptor');
     }

    intercept(req: HttpRequest<any>, next: HttpHandler) {

        console.log('intercepting');

        const authToken = this.auth.getProfileToken();

        console.log(authToken);

        const authReq = req.clone({
            headers: req.headers.set('Authorization', authToken)
        });

        return next.handle(authReq);

    }

}

reports.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { UpperCasePipe } from '@angular/common';
import { DatePipe } from '@angular/common';
import { HTTP_INTERCEPTORS } from '@angular/common/http';

import { ReportsRoutingModule } from './reports-routing.module';

...

import { SharedModule } from './../shared/shared.module';

..

import { AuthInterceptor } from './../core/interceptors/auth.interceptor';

@NgModule({
    imports: [
        CommonModule,
        SharedModule,
        ReportsRoutingModule,
    ],
    declarations: [
        ...
    ],
    entryComponents: [
        ...
    ],
    providers: [DatePipe, UpperCasePipe,
        { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }
    ]
})
export class ReportsModule { }

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';

import { SharedModule } from './shared/shared.module';
import { CoreModule } from './core/core.module';
import { HttpClientModule } from '@angular/common/http';
   
@NgModule({
    declarations: [
        AppComponent
    ],
    imports: [
        BrowserModule,
        AppRoutingModule,
        BrowserAnimationsModule,
        HttpClientModule,
        SharedModule,
        CoreModule.forRoot(),
    ],
    providers: [],
    bootstrap: [AppComponent]
})
export class AppModule { }

app-routing.module.ts

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { AuthGuard } from '../app/core/guard/auth/auth.guard';

const routes: Routes = [
    {
        path: 'reports',
        loadChildren: './reports/reports.module#ReportsModule',
        canActivate: [
            AuthGuard
        ]
    }
];

@NgModule({
    imports: [RouterModule.forRoot(routes)],
    exports: [RouterModule],
    providers: [AuthGuard]
})
export class AppRoutingModule { }

Mario Petrovic
  • 7,500
  • 14
  • 42
  • 62
Kay
  • 17,906
  • 63
  • 162
  • 270
  • I would suggest you move the interceptor to the main module, and code it to only intercept requests to the relevant domain. – jonrsharpe Apr 16 '18 at 10:45
  • reports is you lazy loaded module ?? – Pranay Rana Apr 16 '18 at 10:46
  • @jonrsharpe are you suggesting that this is not possible? And what do you mean by code it to only intercept requests to the relevant domain? – Kay Apr 16 '18 at 10:47
  • @PranayRana thats correct, you can see that from the app-routing.module. Reports is my lazy loaded feature module. – Kay Apr 16 '18 at 10:48
  • I don't think it is, no. And I mean in the interceptor you check the request and decide whether or not to do anything with it. – jonrsharpe Apr 16 '18 at 10:48
  • what are the other places you are registerging AuthInterceptor ? there is only one place right ? – Pranay Rana Apr 16 '18 at 10:48
  • @PranayRana Im not registering it anywhere else, i only need it for the reports module. – Kay Apr 16 '18 at 10:49
  • @jonrsharpe This would be a hack right? To add in globally and check every request to see if it needs modifying in a certian way. Like mentioned i only need this for the reports module. I wouldint even know where to begin to determine if the request came from the reports module or not. – Kay Apr 16 '18 at 10:50
  • No, that would be exactly how you're supposed to do it. Even if you only need it for some requests, interceptors are invoked for all requests. The other method for conditional interception is here: https://stackoverflow.com/q/46469349/3001761. – jonrsharpe Apr 16 '18 at 10:52
  • @jonrsharpe i think in this case its more difficult to determine what should be ignored. – Kay Apr 16 '18 at 10:53
  • Well you'll need to do it somewhere. How do *you* know which requests should be authenticated? There must be *some* logic you could encode. But regardless the point is that, as far as I'm aware, there's no way you could have an interceptor that only applies to requests made from a certain module, and all interceptors must be available for injection when the client is created so they can't be lazily loaded. – jonrsharpe Apr 16 '18 at 10:55
  • The method you mentioned in the stackoverflow thread would require me to add an ignoreintercepter in the headers of all the http requests i make in my app with only 1 request not ignoring the interceptor. I would like to avoid doing that if possible. Thats fine, i believe this is still a valid use case. I will wait to see if anyone else has any input on this. @jonrsharpe. – Kay Apr 16 '18 at 10:59
  • Well then do it the other way, have an apply header rather than a skip header. But the validity of your use case is irrelevant, because you *can't do it this way*. It seems pointless to discuss this further. – jonrsharpe Apr 16 '18 at 11:01
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/169073/discussion-between-kay-and-jonrsharpe). – Kay Apr 16 '18 at 11:13
  • @Kay Do you use `--aot` when using `ng serve`? I think I had some trouble with lazyloading modules without aot – Fussel Apr 16 '18 at 11:26
  • @Kay Did you get this working in a lazy loaded module? – Neil May 03 '18 at 06:37

3 Answers3

29

The HTTP_INTERCEPTORS provider token is reset when a lazy loaded module imports another module which imports the HttpClientModule by itself.

So HttpClientModule can be included in your application module, and is only necessary once.

https://angular.io/guide/http#setup-installing-the-module

Yuri
  • 4,254
  • 1
  • 29
  • 46
Sumit Zamadar
  • 310
  • 4
  • 9
6

Every time a module loads in lazy loaded module a new instance of the HttpClient service is injected in the module that has not been configured to use the interceptor configured in the AppModule. So add interceptors at module level instead of application level.

Yuri
  • 4,254
  • 1
  • 29
  • 46
Praveen
  • 447
  • 5
  • 9
-4

Import HttpClientModule into your lazy (reports.module.ts) loaded module, then the interceptor will trigger:

...
@NgModule({
    imports: [
        CommonModule,
        SharedModule,
        ReportsRoutingModule,
        HttpClientModule,
    ],
...
hohnzy
  • 63
  • 4