Angular 5 - Http Interceptor and behavior Subject Issue - http

I am facing issue when i am using http interceptors(to intercept the http request and response) and behvior subject ( for communication between 2 services based on the interceptors) . I have a scenario where i need to monitor all the http calls going on in the application and make a specific http post call only when there are no other http calls are going in the application .
I have interceptor service where i am intercepting all the http request and responses and when no call is happening ,counter variable is 0 then , using a behavior subject ok$ ,and subscribing it in the other Service 2 and from there making a specific http post call . This subscribing is not happening second time , when the value of ok$ is changed.
Interceptor service :
import { Injectable } from '#angular/core';
import { HttpResponse, HttpEvent, HttpClient, HttpHeaders, HttpInterceptor, HttpHandler, HttpRequest, HttpErrorResponse } from '#angular/common/http';
import { BehaviorSubject } from 'rxjs';
import 'rxjs/add/operator/do';
#Injectable()
export class InterceptorService {
counter: number = 0;
public ok$: BehaviorSubject<any>;
constructor() {
this.ok$ = new BehaviorSubject(false);
}
checkCounter() {
if (this.counter === 0) {
setTimeout(() => {
this.checkCounterFinally();
}, 1000);
}
}
checkCounterFinally() {
if (this.counter === 0) {
this.ok$.next(true);
}
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (this.counter === -1)
this.counter = 1;
else
this.counter++;
return next.handle(req).do(
(event: HttpEvent<any>) => {
if (event instanceof HttpResponse) {
console.log(event);
this.counter--;
this.checkCounter();
}
},
(error: any) => {
(event instanceof HttpErrorResponse)
{
if (error instanceof HttpErrorResponse) {
if (error.status != 200) {
console.log(error);
}
}
}
}
);
}
}
Service 2: Making Rest call :
import { Injectable } from '#angular/core';
import { LogService } from '../common/log.service';
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { Observable, BehaviorSubject } from 'rxjs';
import { InterceptorService } from './interceptor.service';
#Injectable()
export class WorkerService {
data: string;
storage = this.LogService.storage;
RestUrl = // some url
constructor(private service1: LogService, private httpClient: HttpClient,
private interceptor: InterceptorService) {
this.service1.logData.subscribe((val) => {
this.storage.setItem("key", "value");
});
this.interceptor.ok$.subscribe((value) => {
if (value === true) {
this.getDataFromLocalStorage();
}
});
}
getDataFromLocalStorage(): void {
//getting data from the local storage and making rest call to server
}
pushDatatoServer(data: string) {
this.httpClient.post(this.RestUrl, this.data, this.httpHeaderObjRequestParam )
.subscribe((response) => {
// do something
}
}
}

Related

how to show id, compliantype in angular 7 with using asp.net core web api>

i am using asp.net core web api for backend and angular 7 for front end .i created database using code first
approach and then i added one more table called Complains .now i wan to return complains table id and two or three more columns from complains table using get request. then get these values in angular and show some where .
//this is interface method
Object UserComplainInformation(Complains complains);
//this is service class which implements above interface
public Object UserComplainInformation(Complains complains)
{
var resp = new
{
Id = _appDbContext.Complains.FindAsync(complains.Id),
Type =complains.Type
};
try
{
_appDbContext.Complains.FindAsync(resp);
return resp;
}
catch(Exception ex)
{
throw ex;
}
//Controller
[HttpGet]
// [Route("complainInformation")]
public Object UserComplainInformation(Complains complain)
{
return _complains.UserComplainInformation(complain);
}
//angular service code
import { Injectable } from '#angular/core';
import { ToastrService } from 'ngx-toastr';
import { ConfigService } from './util/config.service';
import { HttpClient } from '#angular/common/http';
#Injectable({
providedIn: 'root'
})
export class ComplainHistoryService {
BaseUrl : string ='';
constructor( private config:ConfigService, private http:HttpClient) {
this.BaseUrl =config.getApiURI();
}
ngOnInit(){ }
getUserComplainHistory(){
return this.http.get(this.BaseUrl +'/complianInformation');
}
}
//.ts file usercomplainhistory
import { Component, OnInit } from '#angular/core';
import { Router } from '#angular/router';
import { ComplianHistoryService } from 'src/shared/complain-history.service';
#Component({
selector: 'app-user-complians-history',
templateUrl: './user-complains-history.component.html',
styles: []
})
export class UserComplainsHistoryComponent implements OnInit {
userDetails = sessionStorage.getItem('FullName');
userComplainDetails;
constructor(private router:Router, private complainService: ComplainHistoryService) { }
ngOnInit() {
this.complainService.getUserComplainHistory().subscribe(
res =>{
this.userComplainDetails = res;
console.log(res);
},
err =>{
console.error(err);
}
)
}
}
//this is html file where i want to show id and some more fields
<ul class="list-group">
<li class="list-group-item"><strong>FullName : </strong>{{userDetails}}</li>
<li class="list-group-item"><strong>Complian Id : </strong>{{userComplianDetails}}</li>
</ul>

REST API auth error : WooCommerce

Been trying to fetch products from WooCommerce REST API now forever and my brain is bleeding :'( I followed all instructions on woocommerce and github/woocommerce and I can't for my life get anything in Postman with Basic Auth:
But when I select Auth 1.0 - I get all products:
But then if I take the Auth 1.0 generated URL and put it in my browser:
..Instructions under Authentication over HTTP (here) describes the parameters which are generated in URL automatically when i select Auth 1.0 in Postman - but how am I gonna generate those in my React component?
const APP_URL = 'http://0.0.0.0:80'
const CONSUMER_KEY = 'ck_xxxx'
const CONSUMER_SECRET = 'cs_xxxx'
const PRODUCT_URL = `${APP_URL}/wp-json/wc/v2/products?consumer_key=${CONSUMER_KEY}&consumer_secret=${CONSUMER_SECRET}`
fetch(PRODUCT_URL).then(res => {
if(res.status === 200){
return json
} else {
console.log("ERROR RESPONSE STATUS: " + status);
}
}).then( json => {
console.log(json)
})
})
So thankful for all input!
I think this problem may be solved by below code using "Interceptor" concept...
import {
Injectable,
// Injector
} from '#angular/core';
import {
HttpRequest,
HttpHandler,
HttpEvent,
HttpInterceptor,
HttpErrorResponse
} from '#angular/common/http';
// import { Router } from '#angular/router';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
// import { AuthService } from './auth.service';
import { config } from '../config/config';
#Injectable()
export class AppInterceptor implements HttpInterceptor {
constructor(
// private injector: Injector,
// private router: Router
) { }
private includeWooAuth(url) {
const wooAuth = `consumer_key=${config.consumerKey}&consumer_secret=${config.secretKey}`;
const hasQuery = url.includes('?');
let return_url = '';
if (hasQuery) {
return_url = wooAuth;
} else {
return_url = '?' + wooAuth;
}
return return_url;
}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// const auth = this.injector.get(AuthService);
const authRequest = request.clone({
setHeaders: {
// Authorization: `Bearer ${auth.getToken()}`
},
url: `${config.basePath}/${request.url}${this.includeWooAuth(request.url)}`
});
return next.handle(authRequest)
.catch(err => {
if (err instanceof HttpErrorResponse && err.status === 0) {
console.log('Check Your Internet Connection And Try again Later');
} else if (err instanceof HttpErrorResponse && err.status === 401) {
// auth.setToken(null);
// this.router.navigate(['/', 'login']);
}
return Observable.throw(err);
});
}
}
This code will be kept into http.interceptor.ts. Obviously, you should initialize the consumer key and other details of woocommerce API into a constant variable. After that you create a service for show the list of the product like this:
retriveProducts(query: ProductQuery = {}): Observable<RetriveProductsResponse> {
return this.httpClient.get(`products`, {params: this.wooHelper.includeQuery(query), observe: 'response'})
.pipe(
map(value => this.wooHelper.includeResponseHeader(value)),
catchError(err => this.wooHelper.handleError(err)));
}
And call this service to the product.ts file like this:
getProducts() {
this.woocommerceProductsService.retriveProducts()
.subscribe(productResponse => {
this.products = productResponse.products;
}, error => this.errorMessage = <any>error);
}
I have used these above code into my project. I think it will help you.

Extend http on dont call request method in Angular 2

I new in Angular 2 and i'm trying create an App with JWT. So, to do this I follow the post http://www.adonespitogo.com/articles/angular-2-extending-http-provider/.
But i'm a issue, the request method is never call, after login i have to refresh the page to send the token.
Here my classes
http.service.ts
import { Injectable } from '#angular/core';
import { Http, XHRBackend, RequestOptions, Request, RequestOptionsArgs, Response, Headers } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
#Injectable()
export class HttpService extends Http {
constructor (backend: XHRBackend, options: RequestOptions) {
let token = localStorage.getItem('auth_token'); // your custom token getter function here
options.headers.set('Authorization', `Bearer ${token}`);
options.headers.append('Content-Type', 'application/json');
super(backend, options);
}
request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
let token = localStorage.getItem('auth_token');
if (typeof url === 'string') { // meaning we have to add the token to the options, not in url
if (!options) {
// let's make option object
options = {headers: new Headers()};
}
options.headers.set('Authorization', `Bearer ${token}`);
options.headers.append('Content-Type', 'application/json');
} else {
// we have to add the token to the url object
url.headers.set('Authorization', `Bearer ${token}`);
url.headers.append('Content-Type', 'application/json');
}
return super.request(url, options)
.catch(this.catchAuthError(this));
}
private catchAuthError (self: HttpService) {
// we have to pass HttpService's own instance here as `self`
return (res: Response) => {
console.log(res);
if (res.status === 401 || res.status === 403) {
// if not authenticated
console.log(res);
}
return Observable.throw(res);
};
}
}
app.module.ts
providers: [{
provide: HttpService,
useFactory: (backend: XHRBackend, options: RequestOptions) => {
return new HttpService(backend, options);
},
deps: [XHRBackend, RequestOptions]
}, LoggedInGuard, UserService],
picture.service.ts
#Injectable()
export class PictureService {
url: string = 'v1/pictures';
constructor(private http: HttpService) { }
list(): Observable<PictureComponent[]> {
return this.http
.get(this.url)
.map(res => res.json());
}
}
Component to consume picture.service.ts
#Component({
moduleId: module.id,
selector: 'picture-list',
templateUrl: './pictureList.component.html'
})
export class ListagemComponent {
pictures: PictureComponent[] = [];
service: PictureService;
msg: String = '';
constructor(service: PictureService){
this.service = service;
this.service
.list()
.subscribe(pictures => {
this.pictures = pictures;
}, err => console.log(err));
}
}
thanks for help
Did you provide picture.service in your module? If not, you should provide it in your #component or in your module if you want it globally.
I extend XHRBackend
import { Injectable } from '#angular/core';
import { Request, XHRBackend, BrowserXhr, ResponseOptions, XSRFStrategy, Response } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
#Injectable()
export class ExtendedXHRBackend extends XHRBackend {
constructor(browserXhr: BrowserXhr, baseResponseOptions: ResponseOptions, xsrfStrategy: XSRFStrategy) {
super(browserXhr, baseResponseOptions, xsrfStrategy);
}
createConnection(request: Request) {
let token = localStorage.getItem('token');
request.headers.set('x-access-token', `${token}`);
request.headers.set('Content-Type', 'application/json');
let xhrConnection = super.createConnection(request);
xhrConnection.response = xhrConnection.response.catch((error: Response) => {
if (error.status === 401 || error.status === 403) {
console.log('access not alowed');
localStorage.removeItem('token');
}
return Observable.throw(error);
});
return xhrConnection;
}
}
and use on app module
providers: [{ provide: XHRBackend, useClass: ExtendedXHRBackend }]
After this I resolve the issue and works preety well

catch error in http get

I'm trying to make http call and if there will be any error do my things.
Here is my code:
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/throw';
// Operators
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/switchMap';
import 'rxjs/add/operator/toPromise';
#Injectable()
export class HttpCallService {
constructor(private http: Http) { }
getHeroes(): Observable<Hero[]> {
console.log('entered');
return this.http.get('Url')
.map(this.extractData)
.catch(this.handleError);
}
private extractData(res: Response) {
console.log('extract entered');
let body = res.json();
return body.data || {};
}
private handleError(error: Response | any) {
console.log('error entered');
let errMsg: string;
if (error instanceof Response) {
const body = error.json() || '';
const err = body.error || JSON.stringify(body);
errMsg = `${error.status} - ${error.statusText || ''} ${err}`;
} else {
errMsg = error.message ? error.message : error.toString();
}
console.error(errMsg);
return Observable.throw(errMsg);
}
}
export class Hero {
constructor(
public id: Date,
public author: string,
public text: string
) { }
}
When I call it it logs in console only entered which is in getHeroes. I know there will be error I haven't valid url but why it doesn't go to catch?
You need to invoke the subscribe() method to make actual HTTP call. Your method getHeroes() just declares that it'll return the Observable when someone will subscribe to it. That's why you only see the log from getHeroes() - extractData() and handleErrors() are not even invoked.
You need to do getHeroes().subscribe() somewhere in your code.

angular2 – handle same response error multiple times

I am building an angular2 app. I have a global service called HttpClient which is handling all requests before angulars builtin http service gets fired. Also this service handles all my response errors by checking the status codes:
import { Injectable } from '#angular/core';
import { Headers, Http, Response, } from '#angular/http';
import { MessageProvider } from '../../providers/message/message.provider'
#Injectable()
export class HttpClient {
private webApi = 'http://localhost:8080/api/v1/';
constructor(
private http: Http,
private messageProvider: MessageProvider) { }
get(url: string): Promise<Response> {
return this.http.get(this.webApi + url, {headers: this.createAuthorizationHeader()})
.toPromise()
.catch(this.handleError.bind(this));
}
post(url: string, data: Object): Promise<Response> {
return this.http.post(this.webApi + url, data, {headers: this.createAuthorizationHeader()})
.toPromise()
.catch(this.handleError.bind(this));
}
put(url: string, data: Object): Promise<Response> {
return this.http.put(this.webApi + url, data, {headers: this.createAuthorizationHeader()})
.toPromise()
.catch(this.handleError.bind(this));
}
delete(url: string): Promise<Response> {
return this.http.delete(this.webApi + url, {headers: this.createAuthorizationHeader()})
.toPromise()
.catch(this.handleError.bind(this));
}
private handleError (error: any) {
var status: number = error.status;
if(status == 401) {
this.messageProvider.setMessage(error);
this.messageProvider.message.text = "You have to be logged in to reach this page.";
}
let errMsg = (error.message)
? error.message
: status
? `${status} - ${error.statusText}`
: 'Server error';
console.error(errMsg); // log to console instead
return error;
}
private createAuthorizationHeader() {
let headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('Accept', 'application/json');
if (localStorage.getItem('token'))
headers.append('Authorization', 'Bearer ' + localStorage.getItem('token'));
return headers;
}
}
Now, lets pretend the calling component was about login:
import { Component, Input, OnInit, OnDestroy } from '#angular/core';
import { Router } from '#angular/router';
import { Login } from '../../core/classes/login/login'
import { AuthenticationProvider } from '../../providers/authentication/authentication.provider'
import { MessageProvider } from '../../providers/message/message.provider'
#Component({
selector: 'my-login',
templateUrl: 'app/components/login/login.component.html'
})
export class LoginComponent implements OnInit, OnDestroy {
#Input() login: Login;
error: any;
constructor(
private authProvider: AuthenticationProvider,
private route: Router,
private messageProvider: MessageProvider) { }
ngOnInit() {
this.login = new Login();
}
ngOnDestroy() {
this.messageProvider.setDefault();
}
onSubmit() {
this.authProvider.login(this.login)
.then(auth => {
if (this.authProvider.isAdmin())
this.route.navigateByUrl('admin/users');
else if (this.authProvider.isLoggedIn())
this.route.navigateByUrl('/');
})
.catch(error => {console.log(error);});
}
}
In this case I don't want the error from the HttpClient ("You have to be logged in to reach this page.") but a more customized message like "No user found". I know that I could do something like the following but there is no error anymore:
onSubmit() {
this.authProvider
.login(this.login)
.then(auth => {
if (this.authProvider.isAdmin())
this.route.navigateByUrl('admin/users');
else if (this.authProvider.isLoggedIn())
this.route.navigateByUrl('/');
})
.catch(error => {
var status: number = error.status;
if(status == 401) {
this.messageProvider.setMessage(error);
this.messageProvider.message.text = "No user found.";
}
});
}
So is there a way to maybe cause another error in the catch() function within the HttpClient? So I can handle the error again in my LoginComponent.
I think you can throw in the catch method to essentially "map" your error. If you want to also update your messageProvider then you could do...
.catch(error => {
var status: number = error.status;
var newError = {};
if(status == 401) {
this.messageProvider.setMessage(error);
this.messageProvider.message.text = "No user found.";
newError.errorMessage = "No user found.";
}
throw newError;
});
Confirmed with this example:
var obs = Observable.of(12);
obs.map((value) => {
throw "blah";
}).catch((error) => {
if(error === "blah") {
throw "baz";
} else {
return Observable.of("Hello");
}
}).subscribe((value) => {
console.log("GOOD: " + value);
}, (error) => {
console.log("ERR: " + error);
});
//Logs ERR: baz

Resources