Handle server error while using Observables - javascript

i'm working on an Angular 2 Apllication where i have in my login feature this service.
import { Http, Response } from '#angular/http';
import {Injectable} from '#angular/core';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/catch';
import { Observable } from 'rxjs/Observable';
import { contentHeaders, apiUrl} from '../shared/headers';
#Injectable()
export class LoginService extends BaseService{
constructor(private http: Http){
super();
}
/**
* send the user login data (email, password) and the token back to be stored on the client side.
* #param user
* #returns {any|Promise}
*/
login(user: any): Observable<any>{
let body = JSON.stringify(user);
return this.http.post(apiUrl + '/login', body, { headers: contentHeaders })
.map(this.extractData)
.catch(this.handleError);
}
/**
* extract response data and return it to the component
* #param res
* #returns {*}
*/
public extractData(res: Response) {
let body = res.json();
console.log(body);
return body;
}
/**
* handle service error
* #param error
* #returns {ErrorObservable}
*/
public handleError(res: Response) {
return Observable.throw(res);
}
}
and i use it in my LoginComponent in this way
this.loginService.login(userObj)
.subscribe(
(response: any) => {
// success call that is Ok
},
(errorRes: any)=> {
console.log('res in err is', error);
}
-the result of console.log in my component is
TypeError: Observable_1.Observable.throw is not a function
i tried to search stackoverflow or in github if an issue solves this but i couldn't find soething that helps me, so if someone can help me handle the error in LoginComponent as a response from handleError method of the service and get the error message of the server in my component it will be great.
Note: the success part is working fine the problem is in the case of error when i make
return Observable.throw(res);
thanks in advance

You need to import Observable.throw().
Add this import statement:
import 'rxjs/add/observable/throw';

When ever throw is raised you should handle using catch operator as below
getTasks(): Observable<any[]> {
return this._http.get(this._url)
.map((response: Response) => <any[]>response.json())
.do(data => console.log("data we got is " + JSON.stringify(data)))
.catch(this.handleError);
}
private handleError(error: Response) {
console.log(error);
}
Also you are throwing again in your error Handler method which should not be as such
this.loginService.login(userObj)
.subscribe(
(response: any) => {
// success call that is Ok
},
(error)=> {
////////////////////////error message is available in this object
console.log('res in err is', error);
})

Related

You provided 'undefined' where a stream was expected. in token interceptor

I am trying to make an interceptor to refresh the token, but it throws me this error and I don't know why
ERROR TypeError: You provided 'undefined' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.
token-interceptor.service.ts
import { Injectable } from '#angular/core';
import { AuthService } from './auth.service';
import { HttpClient, HttpErrorResponse, HttpHandler, HttpInterceptor, HttpRequest } from '#angular/common/http';
import { environment } from 'src/environments/environment';
import { catchError, map} from 'rxjs/operators';
import { throwError } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class TokenInterceptorService implements HttpInterceptor {
constructor(
private auth: AuthService,
private http: HttpClient
) { }
intercept(req: HttpRequest<any>, next: HttpHandler) {
return next.handle(req).pipe(
catchError((err: any) => {
if (err instanceof HttpErrorResponse) {
if (err.url.includes('signin') || err.url.includes('refreshToken')) {
return next.handle(req)
}
//if error is not about authorization
if (err.status !== 401) {
return next.handle(req)
}
this.renewToken(req).subscribe(request => {
return next.handle(request)
})
} else {
return throwError(err)
}
})
)
}
renewToken(req: HttpRequest<any>) {
return this.http.get(`${environment.API_URL}/refreshToken`, { withCredentials: true }).pipe(
map((res: any) => {
//update access token
this.auth.setToken(res.token)
return req.clone({
setHeaders: {
authorization: `Bearer ${res.token}`
}
})
})
)
}
}
Ignore this: It looks like your post is mostly code; please add some more details. It looks like your post is mostly code; please add some more details.
this piece of code is wrong:
this.renewToken(req).subscribe(request => {
return next.handle(request)
})
istead it should be:
return this.renewToken(req).pipe(switchMap(request => next.handle(request)));
you are just returning nothing in your variant, that is why it doesn't work.
also the whole logic of token interpceptor seems weird to me. I believe you should rethink about how you want it to work. for now as I see you sending request without token and in almost all cases you are sending it again unmodified, and the one that I fixed above will send it again with token. Wouldn't it be right to add token every time, and only send it 2nd time if token is outdated?

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.

How to Send Data with POST Requests in Ionic 2

I have created an app using Google API and I'm using Google fusion tables for the backend so I have enabled the fusion table API as well. I'm making an hybrid app using ionic 2. GET works perfectly for reading the table and POST given an
error 401
.
function submitAnswer(button) {
var accessToken = document.getElementById("accessToken").value;
var query = "https://www.googleapis.com/fusiontables/v2/query?sql=INSERT INTO " + answerTableId + "(Answers,QuestionId,UserID) VALUES ('" + button.value + "','" + currentQueNo + "','" + userId + "')"+key+"&access_token="+accessToken;
var xhttp2 = new XMLHttpRequest();
xhttp2.onreadystatechange = function() {
//alert(this.readyState + " " + this.status);
if(this.readyState == 4) {
alert(this.responseText);
}
};
xhttp2.open("POST", query, true);
xhttp2.setRequestHeader('Authorization',accessToken);
xhttp2.send();
}
Maybe you've only forgotten "Bearer " in your Authorization value :
xhr.setRequestHeader('Authorization', 'Bearer ' + oauthToken.access_token);
Or maybe you've just badly encoded your accessToken in your query (you need to use encodeURIComponent(accessToken))
If this is called from a browser and not from NodeJS, you may be blocked by CORS issue.
Also, not related to your question : your way of creating the request is very sensible to SQL injection. Some random user could just delete your whole database without knowing any password.
Sincw you are using ionic 2 while don't create like an interceptor that extends the main angular http like this code sample below should do every trick for you and i suggest you adhere to it. Since it is ionic 2 angular 2+
Firstly, create a class to extend the http class like so api-handler.ts:
import { Storage } from '#ionic/storage';
import { environment } from './environment';
import { Injectable } from '#angular/core';
import { Headers, Http, ConnectionBackend, RequestOptions, RequestMethod, RequestOptionsArgs } from '#angular/http';
import 'rxjs/add/operator/map';
import { Observable } from "rxjs/Observable";
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/finally';
import 'rxjs/add/observable/throw';
import 'rxjs/add/observable/fromPromise';
import 'rxjs/add/operator/mergeMap';
/*
Generated class for the ApiHandler provider.
this is used to make communication with our endpoint where we pass endpoint
header information and any form of manipulation
*/
#Injectable()
export class ApiHandler extends Http {
private bearer: string = 'Plutus';
constructor(backend: ConnectionBackend, defaultOptions: RequestOptions, private _storage: Storage) {
super(backend, defaultOptions);
}
/**
* This is used to call our api service by inputing the service url
* #param service_url
* #param method
* #param params
* #param options
*
* #return Observable<any>
*/
callService(service_url: string, method: RequestMethod, params?: any, options?: RequestOptionsArgs): Observable<any> {
if (params == null) {
params = {};
}
options = this.requestOptions(method, params, options);
let token_promise: Promise<any> = this._storage.get('token');
return Observable.fromPromise(token_promise)
.mergeMap(token => {
console.log("token from storage", token);
if (options.headers == null && token != null) {
options.headers = new Headers({
'Authorization': `${this.bearer} ${token}`
});
}
return super.request(this.getFullUrl(service_url), options)
.catch(this.onCatch);
});
}
/**
* Request options is used to manipulate and handle needed information before
* it is sent to server
* #param options
* #returns {RequestOptionsArgs}
*/
private requestOptions(method: RequestMethod, params: any, options?: RequestOptionsArgs): RequestOptionsArgs {
if (options == null) {
options = new RequestOptions();
}
options.method = method;
if (options.method === RequestMethod.Post || options.method === RequestMethod.Put) {
options.body = params;
} else {
options.params = params;
}
return options;
}
/**
* Build API url.
* and we remove any leading / from the service calls since
* we are not needing then in making request calls
* e.g localhost:1337//base... to localhost:1337/base..
* #param url
* #returns {string}
*/
private getFullUrl(url: string): string {
if (url.charAt(0) == "/") {
url = url.substring(1);
}
return environment.endpoint + url;
}
/**
* Error handler.
* #param error
* #param caught
* #returns {ErrorObservable}
*/
private onCatch(error: any, caught: Observable<any>): Observable<any> {
return Observable.throw(x);
}
}
if you observe the way i added header information on the above code and using the request method which allows for any form of http methods like Request.Get, Request.Post, Request.Put, Request.Delete, etc.
Secondly, in your app.module.ts provide the class as your default http call for any backend communication by adding the below to your providers:[]
{
provide: ApiHandler,
useFactory: (backend: XHRBackend, defaultOptions: RequestOptions, _storage: Storage) => new ApiHandler(backend, defaultOptions, _storage),
deps: [XHRBackend, RequestOptions, Storage]
}
Then finally, to use it in your case just add this to your constructor, then use straight away like this
import { IFeedBack } from './../interfaces/ifeedback';
import { Observable } from 'rxjs/Observable';
import { ApiHandler } from './../util/api-handler';
import { Injectable } from '#angular/core';
import { RequestMethod } from '#angular/http';
import 'rxjs/add/operator/map';
/*
Generated class for the FeedBackServiceProvider provider.
See https://angular.io/docs/ts/latest/guide/dependency-injection.html
for more info on providers and Angular 2 DI.
*/
#Injectable()
export class FeedBackService {
constructor(private _apiHandler: ApiHandler) {
}
/**
* this is used to create new feedback
* #param feedback
*/
create(feedback: IFeedBack): Observable<IFeedBack> {
return this._apiHandler.callService('/feedback', RequestMethod.Post, feedback)
.map(res => <IFeedBack>res.json());
}
}
then you can call the create with the new param to send and then subscribe to it.
Think this should serve you better.

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()

Angular 2 HTTP Service not returning promise

I'm trying to get an angular 2 service to retrieve data from an HTTP request and return it as a promise. When I use the service in the component, the data I'm passing from the service is returned as undefined.
This is my service
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import 'rxjs/add/operator/toPromise';
#Injectable()
export class RecordService {
constructor(private http: Http) {}
getPosts(): Promise<any> {
return this.http
.get('https://jsonplaceholder.typicode.com/posts')
.toPromise()
.then((response: Response) => response.json().data)
.catch(this.handleError);
}
private handleError(error: any): Promise<any> {
console.error('An error occurred', error);
console.log('ERROR');
return Promise.reject(error.message || error);
}
}
and this is my component
import { Component, OnInit } from '#angular/core';
import { RecordService } from './record.service';
import { Router } from '#angular/router';
#Component({
selector: 'record-view',
template: '<h1>This is the record creation page</h1>',
providers: [RecordService]
})
export class RecordComponent implements OnInit{
message: string;
error: any;
constructor(private recordService: RecordService) {
}
ngOnInit() {
this.recordService.getPosts()
.then(data => console.log(data))
.catch(error => console.log(error));
}
}
Any ideas why the data would be undefined?
response.json() already gives you back the data object of your response as JSON, so remove the .data property access.
When you response.json() the result is the exact content from the response of the request you made.
In this case, https://jsonplaceholder.typicode.com/posts returns an array (if open the url in a browser you'll see the array): [{...}, {...}, ...].
From response.json().data remove .data and add || {} if body is null
Finally:
.then((response: Response) => response.json() || {})

Categories

Resources