All calls to my api are made through a service I created with the "GET, POST, PUT, DELETE, PATCH" methods. If any of these calls fail I would like to show the error in an alert and if the error status is 401 redirect the user to the login. How can I make a generic error handler?
api.service.ts
import { Injectable } from '#angular/core';
import { Http, RequestOptions, RequestOptionsArgs, Response, URLSearchParams } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import lodash from 'lodash';
#Injectable()
export class ApiService {
url: string = 'https://leetags-api.herokuapp.com';
options: RequestOptions = new RequestOptions({
withCredentials: true
});
constructor(private http: Http) {}
get(endpoint: string, params?: any, options: RequestOptionsArgs = {}): Observable<Response> {
if (params) {
const urlSearchParams: URLSearchParams = new URLSearchParams();
lodash.forEach(params, (value: any, key: string): void => urlSearchParams.set(key, value));
options.search = !options.search ? urlSearchParams : options.search;
}
return this.http.get(`${this.url}/${endpoint}`, this.options.merge(options));
}
post(endpoint: string, body: any, options?: RequestOptionsArgs): Observable<Response> {
return this.http.post(`${this.url}/${endpoint}`, body, this.options.merge(options));
}
put(endpoint: string, body: any, options?: RequestOptionsArgs): Observable<Response> {
return this.http.put(`${this.url}/${endpoint}`, body, this.options.merge(options));
}
delete(endpoint: string, options?: RequestOptionsArgs): Observable<Response> {
return this.http.delete(`${this.url}/${endpoint}`, this.options.merge(options));
}
patch(endpoint: string, body: any, options?: RequestOptionsArgs): Observable<Response> {
return this.http.put(`${this.url}/${endpoint}`, body, this.options.merge(options));
}
}
You can also make a catch() method in your service and assign it to the catch handler of your http call. The .catch block is automatically called whenever a api call fails.
Your above http statement should then look like following (for GET):
get() {
return this._http.get(this.getUserUrl)
.map((response: Response) => response.json())
.catch(this.myCatchFunction);
}
myCatchFunction(error: Response){
//look here for specific codes present in response error and handle accordingly
}
Hope this helps
Related
I just developed sign in authentication for an Angular app and I went to test it. Sign up works just fine. Then I sign out and attempt to sign in and the header does not update as expected, instead I get this error in console:
Uncaught DOMException: Blocked a frame with origin
"chrome-extension://hdokiejnpimakedhajhdlcegeplioahd" from accessing a
cross-origin frame.
at e [as constructor] (chrome-extension://hdokiejnpimakedhajhdlcegeplioahd/lpfulllib.js:1:1441712)
at new e (chrome-extension://hdokiejnpimakedhajhdlcegeplioahd/lpfulllib.js:1:1444920)
at chrome-extension://hdokiejnpimakedhajhdlcegeplioahd/lpfulllib.js:1:1461728
But I am not authenticated because I get:
{authenticated: false, username: null}
authenticated: false
username: null
Even though the GET request itself went through successfully, but there is a problem there, because it's not supposed to be a GET but a POST request. Why does it think it's a GET request?
My signin() method inside my auth service clearly shows it's a post request:
signin(credentials: SigninCredentials) {
return this.http.post(this.rootUrl + "/auth/signin", credentials).pipe(
tap(() => {
this.signedin$.next(true);
})
);
}
Here is my auth http interceptor code:
import { Injectable } from "#angular/core";
import {
HttpEvent,
HttpInterceptor,
HttpHandler,
HttpRequest,
HttpEventType,
} from "#angular/common/http";
import { Observable } from "rxjs";
#Injectable()
export class AuthHttpInterceptor implements HttpInterceptor {
intercept(
req: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
// Modify or log the outgoing request
const modifiedReq = req.clone({
withCredentials: true,
});
return next.handle(modifiedReq);
}
}
Now I do not think the issue is in my AuthHttpInterceptor, I believe the problem is in AuthService:
import { Injectable } from "#angular/core";
import { HttpClient } from "#angular/common/http";
import { BehaviorSubject } from "rxjs";
import { tap } from "rxjs/operators";
interface UsernameAvailableResponse {
available: boolean;
}
interface SignupCredentials {
username: string;
password: string;
passwordConfirmation: string;
}
interface SignupResponse {
username: string;
}
interface SignedinResponse {
authenticated: boolean;
username: string;
}
interface SigninCredentials {
username: string;
password: string;
}
#Injectable({
providedIn: "root",
})
export class AuthService {
rootUrl = "https://api.my-email.com";
signedin$ = new BehaviorSubject(false);
constructor(private http: HttpClient) {}
usernameAvailable(username: string) {
return this.http.post<UsernameAvailableResponse>(
this.rootUrl + "/auth/username",
{
username,
}
);
}
signup(credentials: SignupCredentials) {
return this.http
.post<SignupResponse>(this.rootUrl + "/auth/signup", credentials)
.pipe(
tap(() => {
this.signedin$.next(true);
})
);
}
checkAuth() {
return this.http
.get<SignedinResponse>(this.rootUrl + "/auth/signedin")
.pipe(
tap(({ authenticated }) => {
this.signedin$.next(authenticated);
})
);
}
signout() {
return this.http.post(this.rootUrl + "/auth/signout", {}).pipe(
tap(() => {
this.signedin$.next(false);
})
);
}
signin(credentials: SigninCredentials) {
return this.http.post(this.rootUrl + "/auth/signin", credentials).pipe(
tap(() => {
this.signedin$.next(true);
})
);
}
}
I see you have no HttpHeaders. While I do not see your backend configuration, I suspect the mis configuration causes your exception.
You can update your Angular interpector to something like this:
import {
HttpEvent,
HttpHandler,
HttpHeaders,
HttpInterceptor,
HttpRequest,
} from '#angular/common/http'
import { Injectable } from '#angular/core'
import { Observable } from 'rxjs'
#Injectable()
export class AuthHttpInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// const token = localStorage.getItem('access_token')
// const userToken = localStorage.getItem('id_token')
if (token) {
const authRequest = req.clone({
setHeaders: {
'Content-Type': 'application/json',
// Authorization: `Bearer ${token}`,
// userID: `${userToken}`,
},
})
return next.handle(authRequest)
} else {
const headers = new HttpHeaders({
'Content-Type': 'application/json',
})
const cloned = req.clone({
headers,
})
return next.handle(cloned)
}
}
}
Also make sure that your backend is aligned and headers such as Access-Control-Allow-Origin configured to true
That is caused by LastPass extension. Deactivate it and the error will disappear.
I have upgraded my angular app from 4 to latest version 7. After so many errors I finally got it to work but now there is one problem: services are not working, like it is not giving any error in the console but not working as well.
I think the problem is with my Http interceptor and the factory in which I am missing something.
Can someone tell me what the issue is, exactly?
Http interceptor
export class InterceptedHttp extends HttpClient
constructor(
backend: HttpBackend,
private store: Store<any>,
private spinnerService: SpinnerService
) {
super(backend);
}
request( url: string | HttpRequest<any>, options?: any): Observable<any> {
this.showLoader();
return this.tryCatch(super.request(this.getRequestOptionArgs(options)))
}
get(url: string, options?: any): Observable<any> {
url = this.updateUrl(url);
return this.tryCatch(super.get(url));
}
post(url: string, body: string, options?: any): Observable<any> {
url = this.updateUrl(url);
return this.tryCatch(
super.post(url, body)
);
}
put(url: string, body: string, options?: any): Observable<any> {
url = this.updateUrl(url);
return this.tryCatch(
super.put(url, body)
);
}
delete(url: string, options?: any): Observable<any> {
url = this.updateUrl(url);
return this.tryCatch(super.delete(url));
}
patch(url: string, body: any, options?: any): Observable<any> {
url = this.updateUrl(url);
return this.tryCatch(
super.patch(url, body)
);
}
private updateUrl(req: string) {
return environment.origin + req;
}
private getRequestOptionArgs(options?: any): any {
if (options.headers == null) {
options.headers = new HttpHeaders();
}
options.headers.append('Content-Type', 'application/json');
options.headers.append(
'Authorization',
` Bearer ${sessionStorage.AccessToken}`
);
return options;
}
private tryCatch(obs: Observable<any>) {
return obs.pipe(catchError((error: HttpResponse<any>, caught) => {
if (error.status === 401 && sessionStorage.AccessToken) {
sessionStorage.clear();
this.store.dispatch({type: 'LOGOUT'});
}
this.hideLoader();
return observableThrowError(error);
}));
}
Http factory
export function httpFactory(xhrBackend: HttpXhrBackend,
store: Store<any>, spinnerService: SpinnerService): HttpClient {
return new InterceptedHttp(xhrBackend, store, spinnerService);
}
provider in app module
{
provide: HttpClient,
useFactory: httpFactory,
deps: [HttpXhrBackend, Store, SpinnerService]
},
Whenever I login it just starts loading, nothing else, no error or anything and when I comment out the provider in the app module it says "404 not found error".
Any help?
Thanks
Can't comment on how you did interceptors in Angular 4. But since 4.3 HttpInterceptor was introduced so here is an example on how you do http interceptors in Angular 7:
#Injectable()
export class ApiInterceptor implements HttpInterceptor {
constructor(private someService: SomeService) {}
intercept(
request: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
return this.someService.getBaseUrl().pipe(
mergeMap(baseUrl => {
const apiRequest = data
? request.clone({ url: `${baseUrl}${request.url}` })
: request;
return next.handle(apiRequest);
})
);
}
}
Here is an example of a intercept that does nothing but returning the request unchanged:
intercept(
request: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
return next.handle(request);
});
How to provide it:
import { HTTP_INTERCEPTORS } from '#angular/common/http';
// ...
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: ApiInterceptor, multi: true }
]
You can inspect the request object to check the request type (POST, GET) and do what ever you want with it.
This might be obvious (or not) but HttpInterceptor only works if your requests are made with the HttpClient introduced in Angular 4.3. If your requests are made by other http client (the older client from HttpModule, native code or other library) it will not work, AFAIK.
Also if you try to lazy load the interceptor it will not be straightforward (not even sure if its possible), so first try to get it to work like this.
I am new to angularJs2. I have created following service:
import { Injectable, OnInit } from '#angular/core';
import { customType } from '../models/currentJobs';
import { Headers, Http } from '#angular/http';
import 'rxjs/add/operator/toPromise';
#Injectable()
export class JobService implements OnInit {
constructor(private http: Http) { }
ngOnInit(): void {
this.getCurrentJobs();
}
private headers: Headers = new Headers({ 'Content-Type': 'application/json' });
private ordersUrl: string = 'http://localhost:35032/api/order/';
public orders: customType[];
getCurrentJobs(): Promise<customType[]> {
var jobs = this.http.get(this.ordersUrl)
.toPromise()
.then(response => {
this.orders = response.json() as customType[];
})
.catch(this.handleError);
return jobs;//this line throws error
}
private handleError(error: any): Promise<any> {
console.error('An error occurred', error);
return Promise.reject(error.message || error);
}
}
Following are my Typescript compile configuration of Vs2017
When I compile the code using visual studio 2017 I get following error
**TS2322 Build:Type 'Promise<void>' is not assignable to type 'Promise<customType[]>'.**
Help me to fix this error.
You are not returning anything inside your then which makes jobs be of type Promise<void>. Return the array inside then:
getCurrentJobs(): Promise<customType[]> {
var jobs = this.http.get(this.ordersUrl)
.toPromise()
.then(response => {
this.orders = response.json() as customType[];
return this.orders;
})
.catch(this.handleError);
return jobs;
}
See the chaining behaviour of promises: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then#Chaining
I've added the 'catch' operator, and swapped your interface import for an interface definition in the code (as I don't obviously have access to yours). I can't really test this without the rest of your project code, but it looks right to me and doesn't throw any errors in VSC.
import { Injectable, OnInit } from '#angular/core';
import { Headers, Http } from '#angular/http';
import 'rxjs/add/operator/toPromise';
import 'rxjs/add/operator/catch';
export interface customType{
}
#Injectable()
export class JobService implements OnInit {
constructor(private http: Http) { }
private jobs: Promise<customType[]>;
ngOnInit(): void {
this.jobs = this.getCurrentJobs();
}
private headers: Headers = new Headers({ 'Content-Type': 'application/json' });
private ordersUrl: string = 'http://localhost:35032/api/order/';
public orders: customType[];
getCurrentJobs(): Promise<customType[]> {
return this.http.get(this.ordersUrl)
.map(response => response.json())
.catch(this.handleError)
.toPromise();
}
private handleError(error: any): Promise<any> {
console.error('An error occurred', error);
return Promise.reject(error.message || error);
}
}
I'm making an HTTP request interceptor and I'm overriding the Http module get method so I can automatically add a JWT token to the headers.
I've tried it with the following code, but I get the following error when returning from the method:
Type Promise<Observable<Response>> is not assignable to type Observable<Response>.
Even when I try to return it within a then() it gives me the same error.
How can I get the correct Observable<Response> and return it?
// ...
#Injectable()
export class AdalHttp extends Http {
constructor(backend: ConnectionBackend, defaultOptions: RequestOptions) {
super(backend, defaultOptions);
}
get(url: string, options?: RequestOptionsArgs): Observable<Response> {
return this.getWithAdal(url, options); // <-- ERROR HERE
}
private getWithAdal(url: string, options?: RequestOptionsArgs): Promise<Observable<Response>> {
return new Promise((resolve, reject) => {
this.authenticate((authResponse) => {
if (!options) {
options = { headers: new Headers() };
}
options.headers.set('Authorization', 'Bearer ' + authResponse.accessToken);
resolve(super.get(url, options));
}, err => reject(err));
});
}
// ...
}
get method should return Observable. It calls getWithAdal method and returns immediately the object returned by getWithAdal. So getWithAdal should also return Observable.
Use Observable.fromPromise to convert promise to Observable, change signature of getWithAdal method to return Observable
get(url: string, options?: RequestOptionsArgs): Observable<Response> {
return this.getWithAdal(url, options); // <-- ERROR HERE
}
private getWithAdal(url: string, options?: RequestOptionsArgs): Observable<Response> {
return Observable.fromPromise(new Promise((resolve, reject) => {
// just make it simpler for the example.... keep your code here as is
let options = {headers: new Headers()};
options.headers.set('Authorization', 'Bearer ');
resolve(super.get(url, options));
}));
}
I was reading angular2 code and I found some confusing syntax for me.
The full code is below.(from https://github.com/domfarolino/angular2-login-seed)
import { Injectable, Inject } from '#angular/core';
//import { Control } from '#angular/common';
import { Http, Response, Headers, RequestOptions, RequestOptionsArgs } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/throw';
/**
* Import interfaces that service depends on
*/
import { User } from './user';
#Injectable()
export class UserService {
constructor (private http: Http, #Inject('apiBase') private _apiBase: string) {
}
private _loginApi = this._apiBase + '/authorize/local';
private _logoutApi = this._apiBase + '/logout';
private _authenticatedApi = this._apiBase + '/api/authenticated';
private _registerApi = this._apiBase + '/api/users/register';
private _userExistsApi = this._apiBase + '/api/users/exists';
login(user) {
let body = JSON.stringify(user);
let headers = new Headers();
headers.append('Content-Type', 'application/json');
return this.http.post(this._loginApi, body, <RequestOptionsArgs> {headers: headers, withCredentials: true})
.map((res: Response) => res)
.catch(this.handleError);
}
authenticated() {
return this.http.get(this._authenticatedApi, <RequestOptionsArgs> {withCredentials: true})
.map((res: Response) => res.json())
.catch(this.handleError);
}
logout() {
return this.http.get(this._logoutApi, <RequestOptionsArgs> {withCredentials: true})
.map((res: Response) => res.json())
.catch(this.handleError);
}
register(user) {
let body = JSON.stringify(user);
let headers = new Headers();
headers.append('Content-Type', 'application/json');
return this.http.post(this._registerApi, body, <RequestOptionsArgs> {headers: headers, withCredentials: true})
.map((res: Response) => res)
.catch(this.handleError);
}
getUsers() {
return this.http.get(this._apiBase + "/api/users?limit=5&desc=true", <RequestOptionsArgs> {withCredentials: true})
.map((res: Response) => res.json())
.catch(this.handleError);
}
getMe() {
return this.http.get(this._apiBase + '/api/users/me/', <RequestOptionsArgs> {withCredentials: true})
.map((res: Response) => res.json().me)
.catch(this.handleError);
}
private handleError (error: Response) {
// in a real world app, we may send the server to some remote logging infrastructure
// instead of just logging it to the console
return Observable.throw(error || "Server Error");
}
}
and I can't find out what below code as a parameter means.
<RequestOptionsArgs> {headers: headers, withCredentials: true}
Is there anyone can give me an idea?
The syntax <Type> variable is a cast. See Type Assertions on the documentation
Sometimes you’ll end up in a situation where you’ll know more about a value than TypeScript does. Usually this will happen when you know the type of some entity could be more specific than its current type.
Type assertions are a way to tell the compiler “trust me, I know what I’m doing.” A type assertion is like a type cast in other languages, but performs no special checking or restructuring of data. It has no runtime impact, and is used purely by the compiler. TypeScript assumes that you, the programmer, have performed any special checks that you need.
It shows two examples, it is possible to cast using:
<string> somevar
and also with
somevar as string
The two samples are equivalent. Using one over the other is mostly a choice of preference; however, when using TypeScript with JSX, only as-style assertions are allowed.
class is work as a datatype here . ..
Ex .
Class student {
Name : String ,
RollNo: Number
}
now if i declare a variable
Public Students:Obect
Now i can push in students with a object having name and roll no.