Interceptor not intercepting in Angular 17
Asked Answered
U

7

13

I'm learning angular through course and currently I'm learning about Interceptors. The angular version in the course isn't 17 but the one which I'm using in my local is. So, after 1st trying to implement interceptor via class didn't work as intercept didn't happen.

When I looked into net and saw that we can make an intercept constant and that we can provide it in app.config.ts, even after doing that it didn't work. So I'm literally stuck. Any help would be appreciated. Here are my files:

app.component.ts

import { Component, OnInit, OnDestroy } from '@angular/core';
import { HttpClient, HttpClientModule } from '@angular/common/http';
import { CommonModule } from '@angular/common';
import { RouterOutlet } from '@angular/router';
import { FormsModule } from '@angular/forms';
import { Post } from './Post';
import { PostService } from './post-service';
import { Subscription } from 'rxjs';

@Component({
    selector: 'app-root',
    standalone: true,
    imports: [CommonModule, RouterOutlet, HttpClientModule, FormsModule],
    templateUrl: './app.component.html',
    styleUrl: './app.component.css'
})
export class AppComponent implements OnInit, OnDestroy {
    title = 'http-request';

    postJsonUrl: string = 'https://ng-complete-guide-d77e5-default-rtdb.firebaseio.com/posts.json';

    loadedPosts: Post[] = [];

    isFetching = false;

    error = null;

    errorSubscription: Subscription;

    constructor(private http: HttpClient, private postService: PostService) { }

    ngOnInit() {
        this.isFetching = true;
        this.errorSubscription = this.postService.error.subscribe(errorMessage => {
            this.error = errorMessage;
        });
        //things metioned in ngOnInit will load once the application is loaded
        //subscribing here, heavy-lifiting in service class
        this.postService.fetchPosts().subscribe(posts => {
            this.isFetching = false;
            this.loadedPosts = posts;
            console.log(posts);
        }, error => {
            this.isFetching = false;
            this.error = error.message;
            console.log(error);
        });
    }

    onCreatePost(postData: Post) {
        this.postService.createAndStorePost(postData.title, postData.content);

    }

    onFetchPosts() {
        this.isFetching = true;
        // Send Http request
        //subscribing here, heavy-lifiting in service class
        this.postService.fetchPosts().subscribe(posts => {
            this.isFetching = false;
            this.loadedPosts = posts;

        }, error => {
            this.isFetching = false;
            this.error = error.message;
        });
    }

    onClearPosts() {
        this.postService.deletePosts().subscribe(() => {
            this.isFetching = false;
            this.loadedPosts = [];
        })

    }


    onErrorHandling() {
        this.error = false;
        this.isFetching = false;
    }


    ngOnDestroy(): void {
        this.errorSubscription.unsubscribe();
    }

}

auth-interceptor-service.interceptor.ts

import { HttpInterceptorFn } from '@angular/common/http';

export const authInterceptorServiceInterceptor: HttpInterceptorFn = (req, next) => {
  console.log("Request is on its way");
  return next(req);
};

app.config.ts

import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';

import { routes } from './app.routes';
import { provideHttpClient, withInterceptors, withInterceptorsFromDi } from '@angular/common/http';
import { authInterceptorServiceInterceptor } from './auth-interceptor-service.interceptor';

export const appConfig: ApplicationConfig = {
  providers: [provideRouter(routes), provideHttpClient(withInterceptors([
    authInterceptorServiceInterceptor
  ]))]
};

post-service.ts

import { HttpClient, HttpEventType, HttpHeaders, HttpParams } from "@angular/common/http";
import { Injectable } from "@angular/core";
import { Post } from "./Post";
import { Subject, map, tap } from "rxjs";

//either provide it this way or in app.module.ts if exists
@Injectable({ providedIn: 'root' })
export class PostService {

    postJsonUrl: string = 'https://ng-complete-guide-d77e5-default-rtdb.firebaseio.com/posts.json';

    error = new Subject<string>();

    constructor(private http: HttpClient) { }


    createAndStorePost(title: string, content: string) {
        const postData: Post = { title: title, content: content };

        // Send Http request
        console.log(postData);
        this.http
            .post(
                this.postJsonUrl, //.json for firebase
                postData,
                {
                    observe: 'response',
                    //response will show the whole body
                    responseType : 'json'
                }
            )
            .subscribe(responseData => {
                console.log(responseData);
            }, error => {
                this.error.next(error.message);
            });
        //angular uses observables for http requests. If not subscribed, it discards that particular
        //http as not important, hence no result. So do subscribe to http requests.

    }

    fetchPosts() {
        let searchParams = new HttpParams();
        searchParams = searchParams.append('print', 'pretty');
        searchParams = searchParams.append('custom', 'key');
        return this.http.get<{ [key: string]: Post }>(this.postJsonUrl,
            {
                //we can send header 
                headers: new HttpHeaders({'Custom-Header' : 'Hello'}),
                //and params too....... two paramters
                params : searchParams
            })
            //.pipe() does intermediate operation and returns a new obserable which can be subscribed
            .pipe(map(responseData => {
                const postsArray: Post[] = [];
                for (const key in responseData) {
                    if (responseData.hasOwnProperty(key)) {
                        postsArray.push({ ...responseData[key], id: key });
                        console.log(postsArray);
                    }
                }
                return postsArray;
            }));
        //here we funneled our object from the pbject and added them in array
        //we gonna sibscribe in component page

    }

    deletePosts(){
        return this.http.delete(this.postJsonUrl, {
            observe : 'events',
            responseType : 'json'
        }).pipe(tap(event => {
            if(event.type == HttpEventType.Sent){
                //...
            }
            if(event.type === HttpEventType.Response){
                console.log(event);
            }
        }));
    }
}

Post.ts

export interface Post {
    title: string;
    content: string;
    id?: string;
}

I tried all solutions like: Adding @SkipSelf, checking if HttpClientModule is imported only once. But nothing worked.

Urinary answered 8/12, 2023 at 6:59 Comment(4)
whats the error? can you create a stackblitz?Coacervate
Actually there were no errors. But I at last got it, diving even more, I got a solution where I need to add the interceptor in main.ts and it worked. And thanks Joy for taking your timeUrinary
I would love to know what you did! I am having the same problem. What did you do in main.ts? Please!!! I have your same app.config file and HttpInterceptorFn and my log never prints (unike in a couple of videos I watched)Rommel
@shivendrayadav, if you solved your own issue, don't forget to add your solution as an answer and mark it as the correct answer. Just check first if the current answers already do what you did to solve this to mark one of those instead as the answer.Jataka
P
20

In Previous versions of Angular, we should register the Interceptor in app.module but from Angular 17, we should specify this in app.config file.

Angular 17 uses standalone components by default so instead of creating functional interceptors, we should create a DI-based interceptor like this:

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

@Injectable()
export class SampleInterceptor implements HttpInterceptor {
    constructor() { }
    intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
        console.log("Intercepting Requests")
        return next.handle(request);
    }
}

and in app.config file it should be registered as follows:

export const appConfig: ApplicationConfig = {
    providers: [
        provideRouter(routes),
        provideHttpClient(withInterceptorsFromDi()),  
        {
            provide:HTTP_INTERCEPTORS,
            useClass:SampleInterceptor,
            multi:true
        }
    ]
};
Putamen answered 12/2 at 19:39 Comment(3)
Angular documentation recommends to use Functional Interceptors. We can register this functional interceptors in app.config.ts using withInterceptor() method inside providers.Marisolmarissa
@HelsinkiRed you might be correct, It seems like they have updated the docs now, I almost wasted hours trying to set up a simple interceptor while v18 was on betaPutamen
I register Interceptor using this method, now it throwing run time Error says: Error: NG0200: Circular dependency in DI detected for InjectionToken HTTP_INTERCEPTORSFuturism
V
15

In Angular 17, it's possible to use HttpInterceptorFn in the provideHttpClient section of the app.config.ts.

export const appConfig: ApplicationConfig = {
  providers: [provideRouter(routes), CookieService, provideHttpClient(withInterceptors(
      [authInterceptor]
  )), provideAnimations(),],
};

For the authInterceptor, you can implement it as follows. Note that I'm using CookieService here by choice, but you can adapt this to your needs:

export const authInterceptor: HttpInterceptorFn = (
    req: HttpRequest<any>,
    next: HttpHandlerFn
): Observable<HttpEvent<any>> => {
  const cookieService = inject(CookieService);
  const token = cookieService.get('your-token');
  if (token) {
    const cloned = req.clone({
      setHeaders: {
        authorization: token,
      },
    });
    return next(cloned);
  } else {
    return next(req);
  }
};
Voltmeter answered 6/1 at 16:20 Comment(0)
N
6

I'm writing this post regarding interceptors not working with Angular 17 as I looked up from some old tutorials.

The structure of Angular 17 was so different, the most annoying thing could be removing app.module.ts file.

ANGULAR CODE STRUCTURE

enter image description here

APP MODULE STRUCTURE

enter image description here

After a lots of surfing through StackOverflow and trail and errors, I found a way where interceptors were working.

In the app.config.ts file, I wrote like this:

export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(routes), 
    provideClientHydration(),
    provideHttpClient(withInterceptorsFromDi()),  
    {
      provide:HTTP_INTERCEPTORS,
      useClass:HttpInterceptorBasicAuthService,
      multi:true
    }
  ]
};

The key lines for the interceptors to work in the above file were these:

provideHttpClient(withInterceptorsFromDi()),  
{
  provide:HTTP_INTERCEPTORS,
  useClass:HttpInterceptorBasicAuthService,
  multi:true
}

I recommend you to explore about withInterceptorsFromDi(), to understand more about interceptors.

Navarre answered 20/1 at 6:24 Comment(2)
Thanks a lot I was struggling with same !!!Dominant
This worked for me too. Ty.Suffumigate
M
2

app.component.ts

remove HttpClientModule from imports

 imports: [CommonModule, RouterOutlet, HttpClientModule, FormsModule],

app.config.ts.

export const appConfig: ApplicationConfig = {
  providers: [provideRouter(routes), provideHttpClient(withInterceptors(
      [authInterceptor]
  )),CookieService]
};

authInterceptor.ts

export const authInterceptor: HttpInterceptorFn = (
    req: HttpRequest<any>,
    next: HttpHandlerFn
): Observable<HttpEvent<any>> => {
  const cookieService = inject(CookieService);
  const token = cookieService.get('your-token');
  if (token) {
    const cloned = req.clone({
      setHeaders: {
        authorization: token,
      },
    });
    return next(cloned);
  } else {
    return next(req);
  }
};
Maness answered 25/4 at 1:24 Comment(1)
app.component.ts --> remove HttpClientModule from imports This was what blocked my interceptor from firing! Thanks!Attested
M
1
bootstrapApplication(AppComponent, 
  {
    providers:[
      provideRouter(routes), 
      ResumeService, 
      importProvidersFrom(HttpClientModule), 
      provideHttpClient(withInterceptors([authenticationInterceptor])),
      provideAnimations()
    ]
  }
) 

add to main.ts

Megilp answered 17/12, 2023 at 16:17 Comment(0)
H
0

Before Angular 15, there were class interceptors in Angular that needed to be registered in the app.module under providers. However, starting from version 15, functional interceptors are used, which, in Angular 17, should be registered in the app.config.ts

export const appConfig: ApplicationConfig = { providers: [ provideRouter(routes), provideHttpClient(withInterceptors([YourInterceptor])), ]};

A really nice articole about: https://medium.com/@santosant/angular-functional-interceptors-3a2a2e71cdef

Hamford answered 9/12, 2023 at 12:22 Comment(0)
L
0

Thanks to the previous answers, I got it right. And to me, withInterceptorsFromDi() was the key:

In app.module.ts, the following works:

enter image description here

But the following doesn't work:

enter image description here

I've just started to use Angular 18. I need to get familiar with it.

Latour answered 15/8 at 12:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.