Http interceptors are not working angular 7 - javascript

I have upgraded my angular app from 4 to latest version 7. After so many errors I finally got it to work but now there is one problem: services are not working, like it is not giving any error in the console but not working as well.
I think the problem is with my Http interceptor and the factory in which I am missing something.
Can someone tell me what the issue is, exactly?
Http interceptor
export class InterceptedHttp extends HttpClient
constructor(
backend: HttpBackend,
private store: Store<any>,
private spinnerService: SpinnerService
) {
super(backend);
}
request( url: string | HttpRequest<any>, options?: any): Observable<any> {
this.showLoader();
return this.tryCatch(super.request(this.getRequestOptionArgs(options)))
}
get(url: string, options?: any): Observable<any> {
url = this.updateUrl(url);
return this.tryCatch(super.get(url));
}
post(url: string, body: string, options?: any): Observable<any> {
url = this.updateUrl(url);
return this.tryCatch(
super.post(url, body)
);
}
put(url: string, body: string, options?: any): Observable<any> {
url = this.updateUrl(url);
return this.tryCatch(
super.put(url, body)
);
}
delete(url: string, options?: any): Observable<any> {
url = this.updateUrl(url);
return this.tryCatch(super.delete(url));
}
patch(url: string, body: any, options?: any): Observable<any> {
url = this.updateUrl(url);
return this.tryCatch(
super.patch(url, body)
);
}
private updateUrl(req: string) {
return environment.origin + req;
}
private getRequestOptionArgs(options?: any): any {
if (options.headers == null) {
options.headers = new HttpHeaders();
}
options.headers.append('Content-Type', 'application/json');
options.headers.append(
'Authorization',
` Bearer ${sessionStorage.AccessToken}`
);
return options;
}
private tryCatch(obs: Observable<any>) {
return obs.pipe(catchError((error: HttpResponse<any>, caught) => {
if (error.status === 401 && sessionStorage.AccessToken) {
sessionStorage.clear();
this.store.dispatch({type: 'LOGOUT'});
}
this.hideLoader();
return observableThrowError(error);
}));
}
Http factory
export function httpFactory(xhrBackend: HttpXhrBackend,
store: Store<any>, spinnerService: SpinnerService): HttpClient {
return new InterceptedHttp(xhrBackend, store, spinnerService);
}
provider in app module
{
provide: HttpClient,
useFactory: httpFactory,
deps: [HttpXhrBackend, Store, SpinnerService]
},
Whenever I login it just starts loading, nothing else, no error or anything and when I comment out the provider in the app module it says "404 not found error".
Any help?
Thanks

Can't comment on how you did interceptors in Angular 4. But since 4.3 HttpInterceptor was introduced so here is an example on how you do http interceptors in Angular 7:
#Injectable()
export class ApiInterceptor implements HttpInterceptor {
constructor(private someService: SomeService) {}
intercept(
request: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
return this.someService.getBaseUrl().pipe(
mergeMap(baseUrl => {
const apiRequest = data
? request.clone({ url: `${baseUrl}${request.url}` })
: request;
return next.handle(apiRequest);
})
);
}
}
Here is an example of a intercept that does nothing but returning the request unchanged:
intercept(
request: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
return next.handle(request);
});
How to provide it:
import { HTTP_INTERCEPTORS } from '#angular/common/http';
// ...
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: ApiInterceptor, multi: true }
]
You can inspect the request object to check the request type (POST, GET) and do what ever you want with it.
This might be obvious (or not) but HttpInterceptor only works if your requests are made with the HttpClient introduced in Angular 4.3. If your requests are made by other http client (the older client from HttpModule, native code or other library) it will not work, AFAIK.
Also if you try to lazy load the interceptor it will not be straightforward (not even sure if its possible), so first try to get it to work like this.

Related

Angular 9: Uncaught DOMException: Blocked a frame with origin

I just developed sign in authentication for an Angular app and I went to test it. Sign up works just fine. Then I sign out and attempt to sign in and the header does not update as expected, instead I get this error in console:
Uncaught DOMException: Blocked a frame with origin
"chrome-extension://hdokiejnpimakedhajhdlcegeplioahd" from accessing a
cross-origin frame.
at e [as constructor] (chrome-extension://hdokiejnpimakedhajhdlcegeplioahd/lpfulllib.js:1:1441712)
at new e (chrome-extension://hdokiejnpimakedhajhdlcegeplioahd/lpfulllib.js:1:1444920)
at chrome-extension://hdokiejnpimakedhajhdlcegeplioahd/lpfulllib.js:1:1461728
But I am not authenticated because I get:
{authenticated: false, username: null}
authenticated: false
username: null
Even though the GET request itself went through successfully, but there is a problem there, because it's not supposed to be a GET but a POST request. Why does it think it's a GET request?
My signin() method inside my auth service clearly shows it's a post request:
signin(credentials: SigninCredentials) {
return this.http.post(this.rootUrl + "/auth/signin", credentials).pipe(
tap(() => {
this.signedin$.next(true);
})
);
}
Here is my auth http interceptor code:
import { Injectable } from "#angular/core";
import {
HttpEvent,
HttpInterceptor,
HttpHandler,
HttpRequest,
HttpEventType,
} from "#angular/common/http";
import { Observable } from "rxjs";
#Injectable()
export class AuthHttpInterceptor implements HttpInterceptor {
intercept(
req: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
// Modify or log the outgoing request
const modifiedReq = req.clone({
withCredentials: true,
});
return next.handle(modifiedReq);
}
}
Now I do not think the issue is in my AuthHttpInterceptor, I believe the problem is in AuthService:
import { Injectable } from "#angular/core";
import { HttpClient } from "#angular/common/http";
import { BehaviorSubject } from "rxjs";
import { tap } from "rxjs/operators";
interface UsernameAvailableResponse {
available: boolean;
}
interface SignupCredentials {
username: string;
password: string;
passwordConfirmation: string;
}
interface SignupResponse {
username: string;
}
interface SignedinResponse {
authenticated: boolean;
username: string;
}
interface SigninCredentials {
username: string;
password: string;
}
#Injectable({
providedIn: "root",
})
export class AuthService {
rootUrl = "https://api.my-email.com";
signedin$ = new BehaviorSubject(false);
constructor(private http: HttpClient) {}
usernameAvailable(username: string) {
return this.http.post<UsernameAvailableResponse>(
this.rootUrl + "/auth/username",
{
username,
}
);
}
signup(credentials: SignupCredentials) {
return this.http
.post<SignupResponse>(this.rootUrl + "/auth/signup", credentials)
.pipe(
tap(() => {
this.signedin$.next(true);
})
);
}
checkAuth() {
return this.http
.get<SignedinResponse>(this.rootUrl + "/auth/signedin")
.pipe(
tap(({ authenticated }) => {
this.signedin$.next(authenticated);
})
);
}
signout() {
return this.http.post(this.rootUrl + "/auth/signout", {}).pipe(
tap(() => {
this.signedin$.next(false);
})
);
}
signin(credentials: SigninCredentials) {
return this.http.post(this.rootUrl + "/auth/signin", credentials).pipe(
tap(() => {
this.signedin$.next(true);
})
);
}
}
I see you have no HttpHeaders. While I do not see your backend configuration, I suspect the mis configuration causes your exception.
You can update your Angular interpector to something like this:
import {
HttpEvent,
HttpHandler,
HttpHeaders,
HttpInterceptor,
HttpRequest,
} from '#angular/common/http'
import { Injectable } from '#angular/core'
import { Observable } from 'rxjs'
#Injectable()
export class AuthHttpInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// const token = localStorage.getItem('access_token')
// const userToken = localStorage.getItem('id_token')
if (token) {
const authRequest = req.clone({
setHeaders: {
'Content-Type': 'application/json',
// Authorization: `Bearer ${token}`,
// userID: `${userToken}`,
},
})
return next.handle(authRequest)
} else {
const headers = new HttpHeaders({
'Content-Type': 'application/json',
})
const cloned = req.clone({
headers,
})
return next.handle(cloned)
}
}
}
Also make sure that your backend is aligned and headers such as Access-Control-Allow-Origin configured to true
That is caused by LastPass extension. Deactivate it and the error will disappear.

Angular HTTP interceptor for Authentication header

I have to put a token inside the 'Authorization' header for every HTTP request.
So I have developed and registered an HttpInterceptor :
#Injectable()
export class TokenInterceptor implements HttpInterceptor {
constructor(public authService: AuthService) {
}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
let modifiedReq;
const token = this.authService.getToken();
// we need the heck clone because the HttpRequest is immutable
// https://angular.io/guide/http#immutability
if (token) {
modifiedReq = request.clone();
modifiedReq.headers.set('Authorization', `Bearer ${token}`);
}
return next.handle(modifiedReq ? modifiedReq : request).pipe(tap(() => {
// do nothing
},
(err: any) => {
if (err instanceof HttpErrorResponse) {
if (err.status === 0) {
alert('what the heck, 0 HTTP code?');
}
if (err.status !== 401) {
return;
}
this.authService.goToLogin();
}
}));
}
}
But the header seems never to be put on the request sent. What am I doing wrong?
Also, sometimes an errorcode '0' gets caught by the interceptor; what does it mean?
Angular 8.2.11
EDIT 1: ------------------------
I've also tried like this:
request = request.clone({
setHeaders: {
authorization: `Bearer ${token}`
}
});
but still no header has been set.
Also, the module is correctly registered in app.module
providers: [{
provide: HTTP_INTERCEPTORS,
useClass: TokenInterceptor ,
multi: true,
}..
EDIT 2 : ------------------------
Check this image... I'm going crazy.
It's working for me like this:
const headersConfig = {
'Accept': 'application/json', //default headers
};
...
if (token) {
headersConfig['Authorization'] = `Bearer ${token}`;
}
...
return next
.handle(request.clone({
setHeaders: headersConfig
}))
maybe you forget to put in app.module this:
providers: [{
provide: HTTP_INTERCEPTORS,
useClass: TokenInterceptor ,
multi: true,
}..
the final part write in this way:
return next.handle(modifiedReq);
I was wrong. When doing update of the clone request, angular will put the new headers in fields called "lazyUpdate" and not direcly inside the headers.
The requests were failing because of other reasons.

How to retrieve data from an Observable and use it in an Interceptor?

I'm building the front-end of an application in Angular 8. This application uses an OAuth 2 implementation to manage authentication (password grant) so any HTTP request (with the exception of ones to the token endpoint) needs to have on its header a valid access_token.
To provide said token I've made an Angular interceptor that retrieve the token from another service and then attach it to the intercepted HTTP request. The token retrieval method doesn't give directly the token but an observable which eventually resolves to a valid token, I made this choice because the access token may not be instantly available, if the token is expired the application needs to refresh it with an HTTP call and then the refreshed token can be passed to the HTTP interceptor.
The problem which I encounter is that despite my many attempts the interceptor doesn't wait for the token to be retrieved so at the end the interceptor is skipped and the HTTP request is made without any token attached.
This is the code of my interceptor, retrieveValidToken is the Observable which returns the token.
import { Injectable } from '#angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '#angular/common/http';
import { FacadeService } from './facade.service';
import { Observable } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class HttpInterceptorService implements HttpInterceptor {
constructor(private facadeService: FacadeService) { }
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (req.url.includes('localhost:3000') && !req.url.endsWith('token')) {
this.facadeService.retrieveValidToken()
.subscribe(
(res) => {
const clone = req.clone({ setHeaders: { Authorization: `Bearer ${res}` } });
return next.handle(clone);
},
(err) => {
const clone = req.clone({ setHeaders: { Authorization: `Bearer ` } });
return next.handle(clone);
}
);
} else {
return next.handle(req);
}
}
}
Observables are asynchronous. The code outside the subscribe method will not wait for the code inside.
You should return observable by itself, not only result inside its subscription:
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (req.url.includes('localhost:3000') && !req.url.endsWith('token')) {
return this.facadeService.retrieveValidToken()
.subscribe(
res => {
const clone = req.clone({ setHeaders: { Authorization: `Bearer ${res}` } });
return next.handle(clone);
}
);
} else {
return next.handle(req);
}
}
Something similar:
How use async service into angular httpClient interceptor
The problem is that 'intercept' method should return observable immediately, so instead of subscribing to 'this.facadeService.retrieveValidToken()' use the following code:
return this.facadeService.retrieveValidToken().pipe(
mergeMap(token =>
next.handle(req.clone({ setHeaders: { Authorization: 'Bearer ${token}' }))
)
)

Type Promise<void> is not assignable to type Promise<customType[]>

I am new to angularJs2. I have created following service:
import { Injectable, OnInit } from '#angular/core';
import { customType } from '../models/currentJobs';
import { Headers, Http } from '#angular/http';
import 'rxjs/add/operator/toPromise';
#Injectable()
export class JobService implements OnInit {
constructor(private http: Http) { }
ngOnInit(): void {
this.getCurrentJobs();
}
private headers: Headers = new Headers({ 'Content-Type': 'application/json' });
private ordersUrl: string = 'http://localhost:35032/api/order/';
public orders: customType[];
getCurrentJobs(): Promise<customType[]> {
var jobs = this.http.get(this.ordersUrl)
.toPromise()
.then(response => {
this.orders = response.json() as customType[];
})
.catch(this.handleError);
return jobs;//this line throws error
}
private handleError(error: any): Promise<any> {
console.error('An error occurred', error);
return Promise.reject(error.message || error);
}
}
Following are my Typescript compile configuration of Vs2017
When I compile the code using visual studio 2017 I get following error
**TS2322 Build:Type 'Promise<void>' is not assignable to type 'Promise<customType[]>'.**
Help me to fix this error.
You are not returning anything inside your then which makes jobs be of type Promise<void>. Return the array inside then:
getCurrentJobs(): Promise<customType[]> {
var jobs = this.http.get(this.ordersUrl)
.toPromise()
.then(response => {
this.orders = response.json() as customType[];
return this.orders;
})
.catch(this.handleError);
return jobs;
}
See the chaining behaviour of promises: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then#Chaining
I've added the 'catch' operator, and swapped your interface import for an interface definition in the code (as I don't obviously have access to yours). I can't really test this without the rest of your project code, but it looks right to me and doesn't throw any errors in VSC.
import { Injectable, OnInit } from '#angular/core';
import { Headers, Http } from '#angular/http';
import 'rxjs/add/operator/toPromise';
import 'rxjs/add/operator/catch';
export interface customType{
}
#Injectable()
export class JobService implements OnInit {
constructor(private http: Http) { }
private jobs: Promise<customType[]>;
ngOnInit(): void {
this.jobs = this.getCurrentJobs();
}
private headers: Headers = new Headers({ 'Content-Type': 'application/json' });
private ordersUrl: string = 'http://localhost:35032/api/order/';
public orders: customType[];
getCurrentJobs(): Promise<customType[]> {
return this.http.get(this.ordersUrl)
.map(response => response.json())
.catch(this.handleError)
.toPromise();
}
private handleError(error: any): Promise<any> {
console.error('An error occurred', error);
return Promise.reject(error.message || error);
}
}

Handle all http errors in the same way

All calls to my api are made through a service I created with the "GET, POST, PUT, DELETE, PATCH" methods. If any of these calls fail I would like to show the error in an alert and if the error status is 401 redirect the user to the login. How can I make a generic error handler?
api.service.ts
import { Injectable } from '#angular/core';
import { Http, RequestOptions, RequestOptionsArgs, Response, URLSearchParams } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import lodash from 'lodash';
#Injectable()
export class ApiService {
url: string = 'https://leetags-api.herokuapp.com';
options: RequestOptions = new RequestOptions({
withCredentials: true
});
constructor(private http: Http) {}
get(endpoint: string, params?: any, options: RequestOptionsArgs = {}): Observable<Response> {
if (params) {
const urlSearchParams: URLSearchParams = new URLSearchParams();
lodash.forEach(params, (value: any, key: string): void => urlSearchParams.set(key, value));
options.search = !options.search ? urlSearchParams : options.search;
}
return this.http.get(`${this.url}/${endpoint}`, this.options.merge(options));
}
post(endpoint: string, body: any, options?: RequestOptionsArgs): Observable<Response> {
return this.http.post(`${this.url}/${endpoint}`, body, this.options.merge(options));
}
put(endpoint: string, body: any, options?: RequestOptionsArgs): Observable<Response> {
return this.http.put(`${this.url}/${endpoint}`, body, this.options.merge(options));
}
delete(endpoint: string, options?: RequestOptionsArgs): Observable<Response> {
return this.http.delete(`${this.url}/${endpoint}`, this.options.merge(options));
}
patch(endpoint: string, body: any, options?: RequestOptionsArgs): Observable<Response> {
return this.http.put(`${this.url}/${endpoint}`, body, this.options.merge(options));
}
}
You can also make a catch() method in your service and assign it to the catch handler of your http call. The .catch block is automatically called whenever a api call fails.
Your above http statement should then look like following (for GET):
get() {
return this._http.get(this.getUserUrl)
.map((response: Response) => response.json())
.catch(this.myCatchFunction);
}
myCatchFunction(error: Response){
//look here for specific codes present in response error and handle accordingly
}
Hope this helps

Categories