Angular 2 custom Http service ( on every request ) - javascript

What I want to achieve is to handle somehow every Http request I'm making and on every request change my variable state. So I made my custom Http service that wraps Angular 2 Http service:
import {Injectable} from '#angular/core';
import {Http, Headers, Response} from '#angular/http';
import {Observable} from "rxjs";
import 'rxjs/add/operator/map';
#Injectable()
export class HttpClientService {
public isLoading: boolean = false;
constructor(private http: Http) {}
get(url) {
let headers = new Headers();
this.isLoadingHttp(true);
return this.http.get(url, {
headers: headers
});
}
isLoadingHttp( state: boolean ): void {
this.isLoading = state;
}
}
So I have isLoading variable and isLoadingHttp function.
First question - Basically, on GET method started I'm setting variable to true, but how do I know when request has made and response is ready?
Second question: Do I need to make isLoading and Observable? I want to access it from my AppComponent and manipulate when to display loader whenever it has changed.

#Injectable()
export class HttpClientService {
private _isLoading: number = 0;
public get isLoading () {
return this._isLoading;
}
constructor(private http: Http) {}
get(url) {
let headers = new Headers();
this._isLoading++;
return this.http.get(url, {
headers: headers
})
.finally(_ => this._isLoading--);
}
}
There can be more than one active request at a time.
The finally operator needs to be imported like any other operator.
#Injectable()
export class HttpClientService {
private requestCounter: number = 0;
private isLoading: Subject<number> = new BehaviorSubject<number>(requestCounter);
public readonly isLoading$:Observable<number> = this._isLoading.asObservable().share();
constructor(private http: Http) {}
get(url) {
let headers = new Headers();
this.isLoading.next(++this.requestCounter);
return this.http.get(url, {
headers: headers
})
.finally(_ => this.isLoading.next(--this.requestCounter));
}
}
of if you don't care how many outstanding request there are, but just if there are any
#Injectable()
export class HttpClientService {
private requestCounter: number = 0;
private isLoading: Subject<boolean> = new BehaviorSubject<boolean>(false);
public readonly isLoading$:Observable<boolean> = this._isLoading.asObservable().share();
constructor(private http: Http) {}
get(url) {
let headers = new Headers();
this.requestCounter++;
if(this.requestCounter == 1) {
this.isLoading.next(true);
}
return this.http.get(url, {
headers: headers
})
.finally(_ => {
this.requestCounter--;
if(this.requestCounter == 0) {
this.isLoading.next(false));
}
})
}
}

Related

NestJS Interceptor - Append data to incoming request Header or Body

I am trying to modify an NestJS incoming request and append some data either to header or Body. I was able to replace all the body data with my data but i would like to append and not remove the incoming body data.
Here is the code i have
export class MyInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const request = context.switchToHttp().getRequest();
const token = request.headers['authorization'];
if (token) {
const decoded = jwt_decode(token);
request.body['userId'] = decoded['id'];
}
return next.handle();
}
}
Thanks in advance
I have added two examples as after running testing for the interceptor, it passed without any issue. Of course, my example will be very different to your set up, however, hopefully it'll give you enough insight:
The test file:
test('should not mutate entire request body object', () => {
const dto = {
username: 'testuser',
email: 'test#domain.com',
};
const headers = {
authorization: 'Bearer sdkfjdsakfjdkjfdal',
};
return request(app.getHttpServer())
.post('/')
.send(dto)
.set(headers)
.expect(({ body }) => {
expect(body.userId).toBeDefined();
delete body.userId;
expect(body).toStrictEqual(dto);
});
});
I understand your problem as attempting to obtain information about the authenticated user, and return it/use it later on? However, your current implementation seems to completely override the request.body instead of append your property to the original object.
Interceptor:
#Injectable()
export class HttpRequestBodyInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable {
const request = context.switchToHttp().getRequest();
const token = request.headers['authorization'];
if (token) {
// decode token
request.body['userId'] = 'user_123456789';
}
return next.handle();
}
}
Controller:
#Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
#Post()
#UseInterceptors(HttpRequestBodyInterceptor)
getHello(#Req() req): string {
return req.body;
}
}
This returns the correct response and the test will pass. However, you may find a more robust solution would be:
#Injectable()
export class HttpRequestBodyInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable {
const request = context.switchToHttp().getRequest();
const token = request.headers['authorization'];
if (token) {
// decode token
request.userId = 'user_123456789';
}
return next.handle();
}
}
And then access this in your controller by:
#Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
#Post()
#UseInterceptors(HttpRequestBodyInterceptor)
getHello(#Req() req) {
return {
userId: req.userId,
...req.body,
};
}
}
Finally, if your only need for an interceptor is to obtain that userId property, you may find that https://docs.nestjs.com/security/authentication#jwt-functionality is useful.
#Injectable()
export class JwtInterceptor implements NestInterceptor {
constructor(private readonly jwtService: JwtService, private readonly
userService: UserService) { }
async intercept(context: ExecutionContext, next: CallHandler):
Promise<Observable<any>> {
var request: WsArgumentsHost = context.switchToWs();
var { handshake: { headers: { authorization } } } =
request.getClient();
try {
var jwt = authorization.split(" ")[1];
var { phone } = await this.jwtService.verify(jwt, jwtConstraints)
var user: User = await this.userService.findUserByPhoneNumber(phone);
request.getData()["user"]=user;
return next.handle().pipe(map((data) => { return { ...data, 'user': "david" }; }));
i hope this will help someone in future while working with socket.i wanted the user object in the body after they pass authentication .the above trick worked out for me

How do I chain service methods in Angular?

I need to chain two services together in order to make a successful HTTP request. The first service creates an Authorization Header, and the second service makes the call to the Angular Http service.
How do I chain these calls so that the Authorization Header service returns before the main HTTP call is made?
When I run this code I get an error of .toPromise() of undefined
get(url) {
const headers = new Headers();
this.createAuthorizationHeader(headers,function(){
return this.http.get(url, {headers: headers});
});
}
Authorization Header Service:
import { Injectable } from '#angular/core';
import { Http, Headers, Response } from '#angular/http';
import { environment } from '../../environments/environment';
import { ErrorHandlerService } from './error-handler.service';
import { tokenNotExpired, JwtHelper } from 'angular2-jwt';
#Injectable()
export class HttpClient {
jwtHelper: JwtHelper = new JwtHelper();
count = 1;
constructor(private http: Http, private _errorHandler: ErrorHandlerService)
{ }
createAuthorizationHeader(headers: Headers) {
let token = '';
if (sessionStorage.getItem('token')) {
token = sessionStorage.getItem('token')
}
if (token && typeof token === 'string') {
if (this.jwtHelper.isTokenExpired(token) && this.count === 1) {
this.refreshToken()
}
else {
headers.append('Authorization', 'Bearer ' + token);
}
}
}
get(url) {
const headers = new Headers();
this.createAuthorizationHeader(headers);
return this.http.get(url, {
headers: headers
});
}
refreshToken(): any {
this.getRefreshAWSToken(sessionStorage.getItem('refresh_token'))
.then(resp => {
return this.getwithheader(environment.BASE_URL + '/token', JSON.parse(resp).id_token).toPromise()
.then((resp1: Response) => {
console.log(resp1)
const newJwttoken = JSON.parse(resp1.text()).token;
sessionStorage.setItem('token', newJwttoken);
const headers = new Headers();
headers.append('Authorization', 'Bearer ' + newJwttoken);
})
.catch(err => this._errorHandler.handleError(err));
});
}
}
Http Request Service:
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import 'rxjs/add/operator/toPromise';
import { DataTableParams } from 'angular-4-data-table';
import { SharedService } from './shared.service';
import { HttpClient } from './http.service';
import { Observable } from 'rxjs/rx';
import { ErrorHandlerService } from './error-handler.service';
#Injectable()
export class DeviceService {
BASE_URL: String;
constructor(private http: HttpClient,
private _sharedService: SharedService,
private _errorHandler: ErrorHandlerService) {
this.BASE_URL = this._sharedService.BASE_URL;
};
getCarriers(): any {
return this.http.get(this.BASE_URL + '/lookup/carriers').toPromise()
.then((resp: Response) => resp.text())
.catch(err => this._errorHandler.handleError(err));
}
}

How to pass objects between server side node and client side angular 2? [duplicate]

How to make AJAX call with angular2(ts)?
I read the tutorial on angularjs.org. But there is nothing about AJAX.
So I really want to know how to make AJAX call with angular2(ts).
You will want to look at the api docs for the http module. The http class can get resources for you using AJAX. See the Angular HttpClient Guide for more examples.
import { Component } from '#angular/core';
import { Http } from '#angular/http';
#Component({
selector: 'http-app',
templateUrl: 'people.html'
})
class PeopleComponent {
constructor(http: Http) {
http.get('people.json')
// Call map on the response observable to get the parsed people object
.map(res => res.json())
// Subscribe to the observable to get the parsed people object and attach it to the
// component
.subscribe(people => this.people = people);
}
}
import { Component, OnInit } from '#angular/core';
import { Observable } from 'rxjs/Observable';
import { Http, Response, Headers, RequestOptions } from '#angular/http';
import 'rxjs/add/operator/map';
#Component({
selector: 'dashboard',
templateUrl: './dashboard.component.html',
styleUrls: ['./dashboard.component.css'],
providers: [RemoteService]
})
export class DashboardComponent implements OnInit {
allData = [];
resu: string;
errData: string;
name: string = "Deepak";
constructor(private http: Http){}
ngOnInit(){}
onSubmit(value: any) {
//console.log(value.message);
let headers = new Headers({ 'Content-Type': 'application/json'});
let options = new RequestOptions({ headers: headers });
let body = JSON.stringify(value);
this.http.post('127.0.0.1/myProject/insertData.php', body, headers)
.subscribe(
() => {alert("Success")}, //For Success Response
err => {console.error(err)} //For Error Response
);
}
}
json-data.service.ts
import { Injectable } from '#angular/core';
import { Http, Response, RequestOptions, Headers } from "#angular/http";
import 'rxjs/add/operator/map';
import { Observable } from 'rxjs/Observable';
#Injectable()
export class JsonDataService {
errorMessage: any;
constructor(private http: Http) {
}
getData(): Observable<JsonData[]> {
console.log('Retriving Data from Server.......');
return this.http.get('http://883.82.3:8086/restfullDataApi/UserService/jsondata')
.map(this.extractData)
.catch(this.handleError);
}
getSolrData() {
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
let url = "http://883.8.2:8086/PI3_Solr_WebService/solrService"; /
return this.http.post(url).map((res: Response) => res.json());
}
let body = res.json();
return body || [];
}
private handleError(error: any) {
// In a real world app, we might use a remote logging infrastructure
// We'd also dig deeper into the error to get a better message
let errMsg = (error.message) ? error.message :
error.status ? `${error.status} - ${error.statusText}` : 'Server error';
console.error(errMsg); // log to console instead
alert("Server Error!");
return Observable.throw(errMsg);
}
AJAX is fully transparent in angularjs, see the links and examples below.
https://docs.angularjs.org/api/ng/service/$http
$http({
method: 'GET',
url: '/someUrl'
}).then(function successCallback(response) {
// this callback will be called asynchronously
// when the response is available
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
https://docs.angularjs.org/api/ngResource/service/$resource
var User = $resource('/user/:userId', {userId:'#id'});
User.get({userId:123}, function(user) {
user.abc = true;
user.$save();
});

Angular 2 - Cannot instantiate cyclic dependency

I create custom XHRBackend class to catch 401 error globally. In AuthService I have 2 methods which use http - login and refreshToken. So i have dependency chain like that: Http -> customXHRBackend -> AuthService -> Http. How can I fix this?
export class CustomXHRBackend extends XHRBackend {
constructor(browserXHR: BrowserXhr,
baseResponseOptions: ResponseOptions,
xsrfStrategy: XSRFStrategy,
private router: Router,
private authService: AuthService) {
super(browserXHR, baseResponseOptions, xsrfStrategy);
}
createConnection(request: Request): XHRConnection {
let connection: XHRConnection = super.createConnection(request);
connection.response = connection.response
.catch(this.handleError.bind(this));
return connection;
}
handleError(error: Response | any) {
console.log('ERROR',error['status']);
if(error['status'] === 401) {
this.authService.logout();
this.router.navigate(['/']);
}
return Observable.throw(error);
}
}
AuthService.ts
#Injectable()
export class AuthService {
private loggedIn: boolean = false;
constructor(private http: Http) {
this.loggedIn = !!localStorage.getItem('authToken');
}
login(email: string, password: string): Observable<Response> {
let headers: Headers = new Headers();
headers.set('Content-Type', 'application/json');
return this.http.post('https://httpbin.org/post',
{
email: email,
password: password
},
{
headers: headers
})
.map((response) => {
let res = response.json();
// if (res['success']) {
if (res) {
localStorage.setItem('authToken', res['token']);
localStorage.setItem('refreshToken', res['refreshToken']);
console.log('logged');
this.loggedIn = true;
}
return response;
}
);
}
logout(): void {
localStorage.removeItem('authToken');
this.loggedIn = false;
console.log('Logged out');
}
isLogged(): boolean {
return this.loggedIn;
}
refreshToken(): Observable<Response> {
let headers: Headers = new Headers();
headers.set('token', localStorage.getItem('token'));
headers.set('refreshToken', localStorage.getItem('refreshToken'));
return this.http.get('https://httpbin.org/get', {
headers: headers
});
}
}
Include CustomXHRBackend in app.module.ts
{
provide: XHRBackend,
useFactory: (browserXHR: BrowserXhr,
baseResponseOptions: ResponseOptions,
xsrfStrategy: XSRFStrategy,
router: Router,
authService: AuthService) => {
return new CustomXHRBackend(browserXHR, baseResponseOptions, xsrfStrategy, router, authService);
},
deps: [BrowserXhr, ResponseOptions, XSRFStrategy, Router, AuthService]
}
How about HTTP Interceptors... There's a blog post here.
If you Google you'll find more...
Here's how you hook one into you App Module
you can clone the request in you interceptor and add X-CustomAuthHeader into headers etc.
Please see in your constructor where you inject dependency. You can't inject in a few Services the same dependency.Example: CustomXHRBackend => AuthService, AuthService => CustomXHRBackend

Value become null for this in Promise callback

I am using the following code value of this become null when i call it inside the then function here is the code. Am i doing something wrong or it is like this or there is any work around to resolve this issue
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Headers, RequestOptions } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import { CanActivate, Router } from '#angular/router';
import { AuthService } from '../services/auth.service';
import { WebAPISettings } from '../services/webapisettings.service';
#Injectable()
export class LoginService {
//_ngWEBAPISettings: WebAPISettings;
//_authService: AuthService;
constructor(private http: Http, private ngWEBAPISettings: WebAPISettings, private authService: AuthService) {
//this._ngWEBAPISettings = ngWEBAPISettings;
//this._authService = authService;
}
public login(username: string, password: string): Promise<any> {
let data = "grant_type=password&username=" + username + "&password=" + password;
let headers = new Headers({ 'Content-Type': 'application/x-www-form-urlencoded' });
let options = new RequestOptions({ headers: headers });
try {
debugger;
return this.http.post(this.ngWEBAPISettings.apiServiceBaseUri + "token", data, options)
.toPromise()
.then(function (res: Response) {
debugger;
let body = res.json();
//let _authService: AuthService = new AuthService();
this.authService.fillAuthDataFromLogin(body);
//this.router.navigate(['/Home']);
return body.data || {};
})
.catch(this.handleError);
}
catch (error) {
console.log(error);
}
}
private extractData() {
}
private handleError(error: any) {
debugger;
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);
}
}
and i am debugging it in the chrome here is the screenshot please help me in fixing it.
after using the arrow function same thing check the screen shot
one thing to mention i am using Angular2 RC4.
You could use an arrow function to be able to use the lexical this:
return this.http.post(this.ngWEBAPISettings.apiServiceBaseUri + "token", data, options)
.toPromise()
.then((res: Response) => { // <-----
(...)
});
This way, this will correspond to the instance of the LoginService service.
See this doc for more details:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions

Categories

Resources