Angular2 Http Call not firing - javascript

Context :
Following several tutorials, I am testing authentication with Angular2 and JWT. I come with a component and a service :
app.component.ts
user.service.ts
App component (and template) contains the subscription to an observable that shows the user logged in status. The Observable item is kept in the user service, and changes (fine) when user logs in and out.
The authentication token is written in "localStorage" as "auth_token". It contains a validity value (time) that should force the user to login again after a time.
What I'd like to do is to CHECK the token validity on app init. First, I tried to do it from the user.service CONSTRUCTOR, then (fail), I tried to do it from ngOnInit in the app.component, then (fail again), I tried to do it on event call (click on a button) from the app component, but fails again!
Some shortened code :
//app.component.html
//...
<a md-button class="app-icon-button" aria-label="checklogin" (click)="checkLogin()">
<md-icon svgIcon="check"></md-icon>
</a>
//...
//app.component.ts
//...
checkLogin(){
console.log('CHECK LOGIN FUNCTION');
let token = localStorage.getItem('auth_token');
if(token){
console.log('TOKEN FOUND');
this.userService.checkToken(token);
}else{
console.log('NO TOKEN FOUND');
}
}
//...
//user.service.ts
//...
checkToken(token){
console.log('CHECK TOKEN FUNCTION');
console.log('TOKEN : '+token);
let headers = new Headers();
headers.append('Content-Type','application/json');
return this.http
.post(
'/url/script.php',
JSON.stringify(token),
{ headers }
)
.map(res => res.json())
.map((res) => {
console.log('SCRIPT RESULT : ');
if(res.valid){
console.log('TOKEN IS VALID');
return true;
}else{
console.log('TOKEN NOT VALID');
return false;
}
});
}
//...
I did skip the observable part, and subscription.
Problem :
The problem actually is that the app NEVER CALLS the script!
When I do click on the "checkLogin" button (when token exists),
console shows 'CHECK LOGIN FUNCTION',
console shows 'TOKEN FOUND',
console shows 'CHECK TOKEN FUNCTION',
console shows 'TOKEN : '****************************** (token),
But it never shows 'SCRIPT RESULT',
and when using firebug to check if the http call is done, there is NO CALL to the script.php. Looks like the this.http part is just ignored...
Thanks for reading/help

Service starts working when subscription used only when consumer subscribe to output result, using .subscribe method.
You need: this.userService.checkToken(token).subscribe()

Your checkToken() method is returning an Observable that you need to subsrcibe to. An observable will never to execute unless it's subscribed to.
checkLogin(){
console.log('CHECK LOGIN FUNCTION');
let token = localStorage.getItem('auth_token');
if(token){
console.log('TOKEN FOUND');
this.userService.checkToken(token).subscribe(result => {
console.log(result);
}),
error => {
console.log(error);
});
} else {
console.log('NO TOKEN FOUND');
}
}

Ajax call's which use Observables will work only if you have an subscriber.
So you need to subscribe to that Observable. It is an Angular 2 feature. When you don't subscribe the Observable, it will never make that call.
And also you don't need to return anything from the subscriber, because you actually can't return anything.
this.userService.checkToken(token).subscribe((res) => {
console.log('SCRIPT RESULT : ');
if(res.valid) {
console.log('TOKEN IS VALID');
} else {
console.log('TOKEN NOT VALID');
}
});
checkToken(token){
console.log('CHECK TOKEN FUNCTION');
console.log('TOKEN : '+token);
let headers = new Headers();
headers.append('Content-Type','application/json');
return this.http
.post(
'/url/script.php',
JSON.stringify(token),
{ headers }
)
.map(res => res.json());
}

Have You tried using Postman and try to call function you need?
Also, why do You need to validate a token if angular2-jwt can do this for You?
You can do just like this:
install angular2-jwt with npm.
Include in app.module.ts:
import { AUTH_PROVIDERS } from 'angular2-jwt';
add to providers:
providers: [
AUTH_PROVIDERS,
],
and for example auth.service.ts looks like this:
import { Injectable, Inject } from '#angular/core';
import { Http, Response, Headers, RequestOptions, RequestMethod } from '#angular/http';
import { Router } from '#angular/router';
import { Observable } from 'rxjs/Observable';
import { Configuration } from '../../app.config';
import { RegisterViewModel } from '../../model/viewModel/registerViewModel';
import { LoginViewModel } from '../../model/viewModel/loginViewModel';
import { tokenNotExpired, AuthHttp } from 'angular2-jwt';
#Injectable()
export class AuthService {
private actionUrl: string;
constructor(private _http: Http, private _config: Configuration, private _router: Router, private _authHttp: AuthHttp){
this.actionUrl = _config.apiUrl;
}
register(user: RegisterViewModel){
let headers = new Headers({ 'Content-Type': 'application/json' });
//Admin in this system can only register users. that is why auth
return this._authHttp.post(this.actionUrl + '/Account/Register', JSON.stringify(user), { headers : headers })
.do(response => {
console.log(response.toString());
});
}
login(user: LoginViewModel) {
let headers = new Headers({ 'Content-Type': 'application/x-www-form-urlencoded' });
return this._http.post('http://localhost:56181/api/token', "username=" + user.userName + "&password=" + user.password + "&userId=" + user.userId, { headers : headers })
.do(response => {
if(response){
let authResult = response.json();
this.setUser(authResult);
this._router.navigate(['']);
}
});
}
public isAuthenticated(): boolean {
//angular2-jwt has this function to check if token is valid
return tokenNotExpired();
}
private setUser(authResult: any): void {
localStorage.setItem('id_token', authResult.id_token);
}
public logout(): void {
localStorage.removeItem('id_token');
this._router.navigate(['']);
}
}
also remember that angular2-jwt has default name for token in localstorage as id_token or else you will have to use angular2-jwt help class to specify other token name.
You can check if it is working by simply doing this:
in app.component.ts:
export class AppComponent {
constructor(private _auth: AuthService){
}
}
and in app.component.html:
<li>
<a class="nav-link" [routerLink]="['/login']" *ngIf="!_auth.isAuthenticated()">Login</a>
</li>
<li>
<a class="nav-link" (click)="_auth.logout()" *ngIf="_auth.isAuthenticated()">Log Out</a>
</li>
also You can read a little bit documentation about it in:
https://auth0.com/blog/introducing-angular2-jwt-a-library-for-angular2-authentication/

Related

How to handle the Internal server error? Nestjs

How to handle the error so that if the user does not provide a token, then an UnauthorizedException is thrown.
At the moment I am getting this error:
{
"statusCode": 500,
"message": "Internal server error"
}
ts:
canActivate(context: ExecutionContext) {
const request = context.switchToHttp().getRequest();
try {
const jwt = request.headers.authorization.split(' ')[1];
if (!jwt) {
throw new UnauthorizedException('Token is not provided.');
}
return this.jwtService.verify(jwt);
} catch (e) {
return false;
}
}
You can try to recreate auth module from the documentation.
Or try to console.log() on each line.
By default, JWT internal module works well. It can encode and decode all that you need automatically.
https://docs.nestjs.com/security/authentication
I use a middleware for this purpose. I'll share a basic version of it.
auth-middleware.ts
import {HttpStatus,Injectable,Logger,LoggerService,NestMiddleware,} from '#nestjs/common';
import { NextFunction } from 'express';
import { Request, Response } from 'express';
#Injectable()
export class AuthMiddleware implements NestMiddleware {
constructor(
private readonly authenticationService: AuthService,
// (I use Firebase auth. You can inject JWT service here instead)
private readonly logger: LoggerService, // Good'ol logger
) {}
public async use(req: Request, res: Response, next: NextFunction) {
// Checks if req has authorization header
const header = req.headers['authorization'];
if (!header) {
// If no headers are present, returns a 401.
// I use problem+json
// Thats why you are seeing more fields in the response instead of just a
// code and message
return res
.status(HttpStatus.UNAUTHORIZED)
.json({
title: 'Unauthorized',
detail: 'Invalid Token',
type: 'https://app-site.com/login',
status: HttpStatus.UNAUTHORIZED,
instance: 'login/null',
})
.setHeader('Content-Type', 'application/problem+json');
}
// Splitting "Bearer token" to ["Bearer","token"]
const token = header.split(' ')[1];
// Validating token with auth service
// It returns a "tuple" for me...you can have it return whatever you want
const [
authClaims, // Custom claims that is extracted from the JWT
result, // JWT Validation result (boolean)
authProviderUid, // Firebase UID
] = await this.authenticationService.verifyToken(token);
if (
!result || // If JWT is invalid
authClaims.accountStatus === AccountStatusList.Banned ||
authClaims.accountStatus === AccountStatusList.Suspended
) {
// You shall not pass
return res
.status(HttpStatus.UNAUTHORIZED)
.json({
title: 'Unauthorized',
detail: 'Invalid Token',
type: 'https://app-site.com/login',
status: HttpStatus.UNAUTHORIZED,
instance: 'login/null',
})
.setHeader('Content-Type', 'application/problem+json');
}
// Attaches the claims, result and UID with req for the next middleware(s)/controller
req['authResult'] = { authClaims, result, authProviderUid };
//Reassuring
this.logger.log('Token verified', AuthMiddleware.name);
// next function from express
next();
}
}
Next, In the module(s) your controllers are declared,
api.module.ts
import { MiddlewareConsumer, Module, NestModule, RequestMethod, } from '#nestjs/common';
#Module({
imports: [
//...
],
controllers: [
AuthController,
ProfileController,
SubscriptionController
],
providers: [
//...
],
})
export class ApiModule implements NestModule {
public async configure(consumer: MiddlewareConsumer) {
consumer
.apply(AuthMiddleware)
// Exclude some paths
.exclude({ path: '/api/v1/auth/sign-up', method: RequestMethod.POST })
.forRoutes( // Your controller classes you want to run the middleware on
ProfileController,
SubscriptionController,
AuthController
);
}
}
How it works
Every request goes through the specified middleware (if path not excluded). If the request is unauthorized, throws an error before it reaches the controller.
If the request is at the controller, the request is authenticated. You have to take care of the authorization part with guards etc...
Authentication and Authorization are different.
I'd suggest to use middleware for authentication and guards for authorization.
Links :
NestJS Middleware Documentation
Problem Details

HttpClient & Rxjs

I am working on a case where during a network connection we sometimes might have a limited internet connectivity where we unable to get response from the server or failed response as HttpError.
I hereby trying to ping the URL every second to check whether we are getting response or not, for this
I am trying this code, this is working fine in online method but when i am turning my internet of is doesn't return me false value.
fetch-data.service.ts
import { Injectable } from '#angular/core';
import { HttpClient, HttpResponse, HttpErrorResponse } from '#angular/common/http';
import { Posts } from './posts';
import { Observable, interval, throwError, of } from 'rxjs';
import { take, exhaustMap, map, retryWhen, retry, catchError, tap, mapTo, } from 'rxjs/operators';
#Injectable({
providedIn: 'root'
})
export class FetchDataService {
public url = 'https://jsonplaceholder.typicode.com/posts';
constructor(private _httpClient: HttpClient) { }
getData() {
const ob = interval(1000);
return ob.pipe(
exhaustMap(_ => {
return this._httpClient.get<Posts[]>(this.url, { observe: 'response' });
}),
map(val => {
if (val.status === 200)
return true;
throw val;
}),
retryWhen(errors => {
return errors.pipe(map(val => {
if (val.status === 0)
return false;
}))
})
);
}
// private handleError(error: HttpErrorResponse) {
// if (error.error instanceof ErrorEvent) {
// // A client-side or network error occurred. Handle it accordingly.
// console.error('An error occurred:', error.error.message);
// } else {
// // The backend returned an unsuccessful response code.
// // The response body may contain clues as to what went wrong,
// console.error(
// `Backend returned code ${error.status}, ` +
// `body was: ${error.error}`);
// if (error.status !== 200)
// return of(false);
// }
// // return an observable with a user-facing error message
// return throwError(
// 'Something bad happened; please try again later.');
// };
}
pulldata.component.html
import { Component, OnInit } from '#angular/core';
import { FetchDataService } from '../fetch-data.service';
import { Observable } from 'rxjs';
import { Posts } from '../posts';
#Component({
selector: 'app-pulldata',
templateUrl: './pulldata.component.html',
styleUrls: ['./pulldata.component.css']
})
export class PulldataComponent implements OnInit {
public data;
public error = '';
constructor(private _fecthDataServe: FetchDataService) { }
ngOnInit() {
this._fecthDataServe.getData().subscribe(val => {
this.data = val;
console.log(this.data);
});
}
}
what would be the best solution to check the internet connectivity in this manner?
My personal preference would be to not do this with HTTP because of data overhead. Every HTTP request will contain cookie data and other headers that are often useless in these kinds of scenarios.
Is it possible for you to use Web Sockets? With these, you can open up a connection to the server that, unlike HTTP, doesn't have to close. It can remain open forever. And you can subscribe to events to get notified about connection losses. Web Sockets also have the added benefit that it's a new protocol based on TCP, it's not HTTP, resulting in a lot less network data will have to be send.
let socket = new WebSocket('wss://yourserver/socket...');
socket.addEventListener('open', () => console.log('connection has been opened'));
socket.addEventListener('close', () => console.log('connection has been closed'));
In your situation, you might also want to check out the Reconnecting WebSocket, which reconnects when the connection drops. You could also write this small wrapper yourself, of course.
Also, what might even be a simpler solution. You can subscribe to online/offline events on the window object: read more on MDN
function updateOnlineStatus(event) {
var condition = navigator.onLine ? "online" : "offline";
// ...do something with the new status
}
window.addEventListener('online', updateOnlineStatus);
window.addEventListener('offline', updateOnlineStatus);
Both of these solutions should be easily wrappable in an Angular service, but let me know if that works out and/or if these solutions are an option for you.

Duplicate http requests sent when using http interceptor (in Ionic 2)

TL;DR;
Why subscribing to an Observable in an http interceptor produces duplicate http requests to server?
Sample code:
doGetWithInterceptor() {
console.log("Http get with interceptor -> 2 http calls ?? Why?");
this.http_interceptor_get("http://ip.jsontest.com/").subscribe(data => {
console.log("But only one block of data received:", data);
this.result= data.ip;
});
}
http_interceptor_get(url : string) {
let req= this.http.get(url).map(res => res.json());
req.subscribe((data) => {
console.log("[HttpInterceptor]");
});
return req;
}
Full details:
I use an http interceptor service in my Ionic 2 project to globally detect errors, authentication, and more...
But doing so, I am seeing duplicate http requests to the server.
I have an small test App starting from a blank Ionic 2 template:
Which clearly shows the problem in Firebug:
First request (it's ok, single) if using the GET button.
Second request (which duplicates) is using the "Get with interceptor" button.
Meanwhile, the code in the subscription part is executed only once, as it should.
The home.ts code is as follows:
import { Component } from '#angular/core';
import { NavController } from 'ionic-angular';
import { Http, Response } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
#Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
result : string = "???";
constructor(public navCtrl: NavController, public http: Http) {
}
http_get(url : string) {
return this.http.get(url).map(res => res.json());
}
http_interceptor_get(url : string) {
let req= this.http.get(url).map(res => res.json());
req.subscribe((data) => {
console.log("[HttpInterceptor]");
});
return req;
}
doGet() {
console.log("Normal http get -> 1 http call");
this.http_get("http://ip.jsontest.com/").subscribe(data => {
console.log("One block of data received:", data);
this.result= data.ip;
});
}
doGetWithInterceptor() {
console.log("Http get with interceptor -> 2 http calls ?? Why?");
this.http_interceptor_get("http://ip.jsontest.com/").subscribe(data => {
console.log("But only one block of data received:", data);
this.result= data.ip;
});
}
doClearResult() {
this.result= "???";
}
}
Its because you are not really intercepting. You are simply subscirbing to the request twice.
http_interceptor_get(url : string) {
let req= this.http.get(url).map(res => res.json());
req.subscribe((data) => { //1st subscription - 1st call
console.log("[HttpInterceptor]");
});
return req; //return original request
}
Then you are subscribing again in doGetWithInterceptor() to your http req.
If you want to log details of call, you can use do().
http_interceptor_get(url : string) {
//return http call
return this.http.get(url).map(res => res.json())
.do(data=>{
//do checks.
return data; //be sure to return data so it is passed on to subscription.
});
}
Then call in your doGetWithInterceptor()

Using JWT in my angular 2 application and storing it in localStorage. However, how do I handle for when that item doesn't exist?

I've created a token-service.ts that calls my back end auth API which returns a JWT. I store this JWT in localstorage as shown here in my getToken():
getToken() {
this.http.post('myAuthEndpoint', { credentials })
.subscribe((res) => {
const token = res.headers.get('Authorization')
localStorage.setItem('id_token', token);
});
}
In my app.component.ts, I am calling the getToken() in my ngOnInit method.
However, here's what I have in my app.component.html:
<navigation-top></navigation-top>
<router-outlet></router-outlet>
And this is where I have an issue - In my NavigationTop component, I am calling my getNavigationTop() from my top-navigation.service.ts to populate the links and stuff. The API call I make in getNavigationTop() requires the auth token that I get in my getToken(), but its null on init.
How can I handle this case? Right now it works when I reload the page after the first load, because then I can get the value from localStorage:
getNavigationTop(): Observable<any> {
let headers = new Headers({ 'Authorization': localStorage.getItem('token') });
let options = new RequestOptions({ headers: headers });
let data = this.http
.get('my url', options)
.map((res: Response) => {
return res.json().navTop;
})
.catch(this.handleError);
return data;
}
Thanks
Move those to a service and add it into your main module as a provider.
Then do this below. You can then inject the service into any component and call them at will.
#Injectable()
export class TokenService {
getToken() {
this.http.post('myAuthEndpoint', { credentials })
.subscribe((res) => {
const token = res.headers.get('Authorization')
localStorage.setItem('id_token', token);
this.getNavigationTop(token);
});
}
getNavigationTop(token?): Observable<any> {
let headers = new Headers({ 'Authorization': token ? token: localStorage.getItem('token') });
and then in component:
import {TokenService} from '..../';
...
#Component
export class NavigationTop {
constructor(private tokenService: TokenService) {
}
ngOnInit() {
this.tokenService.getToken();
}
Now if you run getNavigationTop again later, it will check for token argument(optional) first but if none exist, try localstorage instead.

Angular 2 http post request is not being called out

I am using angular 2 to make a get request and a post request. My get request works fine, but for my post, I do not see the request is made when checking my Firebug Net panel.
The code methods look like follows. I also have subscribe methods invoking them from a component class.
import {Injectable} from "angular2/core";
import {Http, Response, Headers, RequestOptions, Jsonp, URLSearchParams} from "angular2/http";
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
#Injectable()
export class MyService{
constructor (private _http: Http){}
testPost(){
var json = JSON.stringify({"userId": 111, "givenName": "CZ"});
var body = "json="+json;
var headers = new Headers({ 'Content-Type': 'application/json' });
var options = new RequestOptions({ headers: headers });
return this._http.post("http://mylocal.post.url", body, options)
.map(this.extractData)
.catch(this.handleError);
}
private extractData(res: Response) {
alert("test whether this method is reached");
let body = res.json();
return body.data || { };
}
private handleError (error: any) {
alert("test whether this method is reached");
let errMsg = (error.message) ? error.message :
error.status ? `${error.status} - ${error.statusText}` : 'Server error';
console.error(errMsg); // log to console instead
return Observable.throw(errMsg);
}
//testGet method is running well
testGet(link:string){
return this._http.get(link)
.map(res => res);
}
}
my subscribing method onTestPost(), which is assigned to a button on the page.
onTestPost(){
this.myService.testPost()
.subscribe(
data => this.getData = JSON.stringify(data),
error => alert(error),
() => console.log("Finished")
);
}
I put an alert statement at the beginning of two helper methods. None of the alerts is reached. And I don't see any request called to the my local post url when debugging with Firebug.
While my testGet method works correctly, I just don't know what is missing for the testPost.
I think your subscribe methods are the issue here. Please make sure subscribe is called.
"This observable is cold which means the request won't go out until
something subscribes to the observable."
See https://angular.io/docs/ts/latest/guide/server-communication.html
testPost() : Observable <Response> //import{Response} from '#angular/http'
{
var json = JSON.stringify({"userId": 111, "givenName": "CZ"});
var headers = new Headers({ 'Content-Type': 'application/json' });
var options = new RequestOptions({ headers: headers });
return this._http.post("http://mylocal.post.url/api",JSON.stringify(json), options)
.map((res:Response)=>res.json())
.catch(this.handleError);
}

Categories

Resources