I have written (copied from SO!) the following interceptor code. I want to modify the outgoing request and also intercept the response. However, I have noticed that the response never gets intercepted. Why? Is it because I am using Rxjs?
Interceptor code
import {HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse} from "#angular/common/http";
import {Injectable} from "#angular/core";
import {Observable} from "rxjs/Observable";
import 'rxjs/add/operator/do';
#Injectable()
export class CustomInterceptor implements HttpInterceptor {
constructor() {
}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
console.log("outgoing request",request);
request = request.clone({
withCredentials: true
});
console.log("new outgoing request",request);
return next
.handle(request)
.do((ev: HttpEvent<any>) => {
if (ev instanceof HttpResponse) {
console.log('processing response', ev); //I DON'T SEE THIS PRINT
}
});
}
}
I am sending the request to the server as follows
return this.http.post(/*this.API_URL+*/this.SIGNIN_USER_URL,body,httpOptions)
.map(response=>{
console.log('response from backend service',response); //I SEE THIS PRINT
let result= <ServerResponse>response;
console.log("result is "+result.result+' with additional information '+result.additionalInformation)
return result;
})
.catch(this.handleError);
Why is the interceptor code not getting hit?
Update - the provider code snippet in app.module.ts
providers: [WebToBackendInterfaceService, //some other provider
{
provide: HTTP_INTERCEPTORS,
useClass: CustomInterceptor , //interceptor provider
multi: true
}],
I added a print between do and if (console.log("got an event",ev). I can see the following message on console `got an event
{…}
body: Object { result: "success", "additional-info": "found user" }
headers: {…}
lazyInit: function lazyInit()
lazyUpdate: null
normalizedNames: Map
size: 0
<entries>
__proto__: Object { … }
__proto__: Object { has: has(), get: get(), keys: keys(), … }
ok: true
status: 200
statusText: "OK"
type: 4
url: "http://localhost:9000/ws/users/signin"
__proto__: Object { constructor: HttpResponse(), clone: clone() }`
I dont know why the if statement isn't getting executed as it seems the event is of type HttpResponse (referring to __proto__: Object { constructor: HttpResponse(), clone: clone() })
Related
I successfully implemented a jwt strategy for authentication using nestJs.
Below is the code for the jwt strategy
import { ServerResponse } from './../helpers/serverResponse.helper';
import { Injectable, UnauthorizedException, HttpStatus } from '#nestjs/common';
import { PassportStrategy } from '#nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { config as env } from 'dotenv';
import { Bugsnag } from '../helpers/bugsnag.helper';
env();
#Injectable()
export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
constructor(
private readonly logger: Bugsnag,
) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: process.env.JWT_SECRET_KEY,
passReqToCallback: true,
});
}
async validate(payload, done: Function) {
try {
const validClaims = await this.authService.verifyTokenClaims(payload);
if (!validClaims)
return done(new UnauthorizedException('invalid token claims'), false);
done(null, payload);
} catch (err) {
this.logger.notify(err);
return ServerResponse.throwError({
success: false,
status: HttpStatus.INTERNAL_SERVER_ERROR,
message: 'JwtStrategy class, validate function',
errors: [err],
});
}
}
}
I saw here that the validate function will be called only when a valid token was provided in the request headers and I'm okay with that. However, I would like to know if it is possible to customize the response object which is sent in that case (invalid token provided).
If yes, how do I do that ?
You can use a exception filter to catch UnauthorizedExceptions and modify the response there if you'd like. The other option would be extending the AuthGuard('jwt') mixin class and adding in some logic around a try/catch for the super.canActivate(context), then in the error read what the reason is and throw a specific UnauthorizedException with your custom message
You can use the AuthGuard('jwt')'s handleRequest method to throw any exception on JWT Validation failure.
#Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
handleRequest(err: any, user: any, info: any, context: any, status: any) {
if (info instanceof JsonWebTokenError) {
throw new UnauthorizedException('Invalid Token!');
}
return super.handleRequest(err, user, info, context, status);
}
}
JsonWebTokenError comes from jsonwebtoken library, which is used internally by passport.
My http method returns results when it is contained in my component, but does not return any results when called from a service located one directory up.
I've checked the console and there are no errors. I have tried printing to the console, which works from within the service (returns the desired data), but does not when run from within the child component.
This is the service that I'm trying to build:
import { Injectable } from '#angular/core';
import { Resturant } from '../../models/resturant.model'
import { HttpClient } from '#angular/common/http';
import { map } from 'rxjs/operators';
#Injectable({
providedIn: 'root'
})
export class GetResturantsService {
fullListresturants: Resturant[];
constructor(private http:HttpClient) { }
fetchList(){
this.http.get('https://lunchlads.firebaseio.com/posts.json')
.pipe(map(responseData =>{
const postsArray: Resturant[] = [];
for (const key in responseData) {
if (responseData.hasOwnProperty(key)){
postsArray.push({ ...responseData[key], id:key })
}
}
return postsArray;
}))
.subscribe(posts => {
// this.fullListresturants = posts;
});
}
}
This is the component which is one file down in the directory:
import { Component, OnInit } from '#angular/core';
import { Resturant } from '../../../models/resturant.model'
import { GetResturantsService } from '../get-resturants.service'
import { HttpClient } from '#angular/common/http';
//import { map } from 'rxjs/operators';
#Component({
selector: 'app-list-all',
templateUrl: './list-all.component.html',
styleUrls: ['./list-all.component.css']
})
export class ListAllComponent implements OnInit {
fullListresturants: Resturant;
constructor(private http:HttpClient, private listAllResturants:GetResturantsService) { }
ngOnInit() {
this.onfullList();
}
onfullList(){
this.fullList();
}
private fullList(){
// this.http.get('https://lunchlads.firebaseio.com/posts.json')
// .pipe(map(responseData =>{
// const postsArray: Resturant[] = [];
// for (const key in responseData) {
// if (responseData.hasOwnProperty(key)){
// postsArray.push({ ...responseData[key], id:key })
// }
// }
// return postsArray;
// }))
// .subscribe(posts => {
// // this.fullListresturants = posts;
// });
this.listAllResturants.fetchList();
}
}
The firebase backend contains roughly 10 records with a name:string, votes:number, and selected:number fields. When run from the component, the html file simply returns the name values with an *ngFor loop.
When run from the service, nothing is returned and no errors are reported in the console.
I suspect the problem lies somewhere in how I am calling the fetchList method from the component, but google and me have not been able to suss out what I'm doing wrong.
Your service should return an observable to make it work. As per your current code, you are not returning anything from GetResturantsService.fetchList(). To make it work let change the service like this:
export class GetResturantsService {
fullListresturants: Resturant[];
constructor(private http:HttpClient) { }
fetchList(){
return this.http.get('https://lunchlads.firebaseio.com/posts.json')
.pipe(map(responseData =>{
const postsArray: Resturant[] = [];
for (const key in responseData) {
if (responseData.hasOwnProperty(key)){
postsArray.push({ ...responseData[key], id:key })
}
}
return postsArray;
}));
}
}
Now in component subscribe to the observable returned from fetchList method like this:
export class ListAllComponent implements OnInit {
fullListresturants: Resturant;
constructor(private http:HttpClient, private listAllResturants:GetResturantsService) { }
ngOnInit() {
this.onfullList();
}
onfullList(){
this.fullList();
}
private fullList(){
this.listAllResturants.fetchList()
.subscribe(posts => {
//DO whatever you want to do with posts
this.fullListresturants = posts;
});
}
}
Hope it helps.
I want the user to be automatically logged out if any api returns a 401 error response.And to do that I am intercepting every request and as soon as the error code comes 401 I am clearing the jwt token in my local storage and auth guard prevents the user from jumping to that route.But after implementing the interceptor(examples are very less for this and no mention in the docs as well) I am unable to hit any HTTP request.Below is my code.Thanks in advance.
import { Injectable, Injector } from '#angular/core';
import {HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpErrorResponse} from '#angular/common/http';
import { Observable } from 'rxjs/Rx';
import 'rxjs/add/observable/throw'
import 'rxjs/add/operator/catch';
#Injectable()
export class ResponseInterceptor implements HttpInterceptor {
constructor() {
}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(request).do((event: HttpEvent<any>) => {
}, (err: any) => {
if (err instanceof HttpErrorResponse) {
// do error handling here
console.log('and the error is ');
console.log(err)
}
});
}
}
If it goes with error why you need to track every request if you could only catch needed?
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(req).pipe(
catchError((err:string, caught:Observable<any>)=>this.handleHttpClientError(err, caught))
);
}
handleHttpClientError(error: any, caught: Observable<any>)
{
if(error.status == 401){
... your logic here ...
}
return new EmptyObservable<Response>();
// or return Observable.throw('Auth error');
}
Currently I want to implement canActivate function, everything I want is to send a request to server each time page requested and get true/false in a json response in order to understand is user authenticated and permitted to review current page.
And it seems that I completely stuck with observable and promise objects, which is new for me, what is what I have so far.
import { Injectable } from '#angular/core';
import {CanActivate, Router} from '#angular/router';
import { Http, Response } from '#angular/http';
import {Observable, Observer, Subject} from "rxjs/Rx";
#Injectable()
export class AuthGuard implements CanActivate {
constructor(private router: Router, private http: Http) {}
canActivate() {
if (this.isAuthenticated()) {
return true;
} {
this.router.navigate(['404']);
return false;
}
}
isAuthenticated() : Observable<boolean> {
var subject = new Subject<boolean>();
this.http.get("/index.php?module=Api&action=IsAuthenticated")
.map((res : Response) => res.json())
.subscribe(res => {
console.log("next: returning true");
subject.next(true);
}, (res) => {
console.log("next: returning false");
subject.next(false);
});
return subject.asObservable().first();
}
}
A few changes
#Injectable()
export class AuthGuard implements CanActivate {
constructor(private router: Router, private http: Http) {}
canActivate() {
return this.isAuthenticated().first(); // not sure if `.first() is still necessary
}
isAuthenticated() : Observable<boolean> {
return this.http.get("/index.php?module=Api&action=IsAuthenticated")
.map((res : Response) => res.json())
.catch(err => return Observable.of(false))
.map(res => {
return true
});
}
}
If isAuthenticated() does some async execution, we don't get true or false back, we get an Observable that emits a true or false value eventually
What we do is to return the observable we get from isAuthenticated()
In isAuthenticated with return the observable we get fromthis.http.get()and transform the emitted event. It seems the response from the server (res.json()) is not used. Therefore we usecatch()to returnfalsein case of an error andtrue` otherwise.
Because the response from the server is not used .map((res : Response) => res.json()) could be omitted, except this is where you expect an error from that should case false to be returned.
Also your production code might look different and require the response to be processed.
We don't subscribe anywhere, because this is what the router is doing when an Observable is returned from canActivate and if we call subscribe() we get a Subscription instead of an Observable.
canActivate can return either Observable<boolean>, Promise<boolean> or boolean.
As you are depending on asynchronous checking you cannot return a boolean.
It however looks like you could just simply do
canActivate(): Observable<boolean> {
return this.isAuthenticated();
}
I'm no expert on Observable yet, but it would also be easy for you to chain on a call to redirect if you where not authorised.
Here is the solution that works for me:
canActivate() {
return this.http.get("/index.php?module=Api&action=IsAuthenticated")
.toPromise()
.then(this.extractData)
.catch(this.handleError);
}
I am currently writing tests for my Angular2 (with Typescript) application and all has been fine and dandy so far, that is until I have attempted to start testing one of my services.
This service has the Angular2 Http module injected on instantiation as shown below:
import { Injectable, EventEmitter } from 'angular2/core';
import { Http } from 'angular2/http';
import 'rxjs/add/operator/map';
import { ConfigObject } from '../ConfigObject';
import { HTTPHelper } from '../helpers/HTTPHelper';
import { Category } from '../classes/Category';
#Injectable()
export class CategoryService {
public emitter: EventEmitter<Category>;
constructor(private _http: Http) {
this.emitter = new EventEmitter();
}
private APIUrl = ConfigObject.productBox + ConfigObject.apiVersion + 'category';
getCategories(filters) {
return this._http.get(this.APIUrl + HTTPHelper.convertVarsToString(filters))
.map(res => res.json());
}
public emitCat(category): void {
this.emitter.emit(category);
}
}
This is then used to make GET requests to an API box I have created.
Here is my Jasmine test spec file for the service:
import { CategoryService } from '../../services/category.service';
import { Http } from 'angular2/http';
describe('Category service', () => {
let testCategoryService: CategoryService;
let _http: Http;
beforeEach(function() {
testCategoryService = new CategoryService(Http);
});
it('should have emitter name set', () => {
expect(testCategoryService.emitter._isScalar).toBeDefined();
});
it('should return categories', () => {
testCategoryService.getCategories({
order : 'asc',
order_by : 'name',
parent_id : 0,
});
});
});
As you can see, am including the Http object here too and injecting it into the test instantiation of my service class before each test on this line:
beforeEach(function() {
testCategoryService = new CategoryService(Http);
});
When I try and test the 'getCategories' function on my service class I get the following error:
TypeError: this._http.get is not a function
Which is odd as as far as I am concerned I am injecting the Http service into my test instantiation on the line above so this should be set in the class constructor?
Can anyone see why the Http object in my class is not being set?
Thanks