Interceptor not intercepting in Angular 17

9.4k views Asked by At

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.

5

There are 5 answers

0
Mădălina Vârgă On

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

0
ela elavarasan On
bootstrapApplication(AppComponent, 
  {
    providers:[
      provideRouter(routes), 
      ResumeService, 
      importProvidersFrom(HttpClientModule), 
      provideHttpClient(withInterceptors([authenticationInterceptor])),
      provideAnimations()
    ]
  }
) 

add to main.ts

0
alejomaf On

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);
  }
};
1
Manohar On

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.

0
trickymind On

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