NestJS passing Authorization header to HttpService - http

I have a NestJS application which acts as a proxy between a front-end and multiple other back-ends.
I basically want to be able to pass a specific header (Authorization) from incoming #Req (requests) in the controller to the HttpService that then talks to the other back-ends.
user controller (has access to request) ->
user service (injects httpService that somehow already picks the Authorization header) -> External backends.
Right now I need to extract the token from #Headers and then pass token to service which has to paste it to all HttpService calls.
Thanks in advance!

I'm not sure if this will help you, but maybe if you get the header from the controller and put it in your services function...
// Controller:
#Get()
getAll(#Request() req){
const header = req.headers;
return this._zoneService.sendToHttp(header);
}
Maybe microservices can be better ?

Besides the middleware answer, I have another version using interceptor:
#Injectable()
export class HttpServiceInterceptor implements NestInterceptor {
constructor(private httpService: HttpService) {}
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
// ** if you use normal HTTP module **
const ctx = context.switchToHttp();
const token = ctx.getRequest().headers['authorization'];
// ** if you use GraphQL module **
const ctx = GqlExecutionContext.create(context);
const token = ctx.getContext().token;
if (ctx.token) {
this.httpService.axiosRef.defaults.headers.common['authorization'] =
token;
}
return next.handle().pipe();
}
}
If you use GraphQLModule, do not forget to pass token to context:
GraphQLModule.forRoot({
debug: true,
playground: true,
autoSchemaFile: 'schema.gql',
context: ({ req }) => {
return { token: req.headers.authorization };
},
}),
Once preparation work is ready, let's use interceptors
The interceptor can be injected into a certain controller:
#UseInterceptors(HttpServiceInterceptor)
export class CatsController {}
or register a global interceptor like following:
#Module({
providers: [
{
provide: APP_INTERCEPTOR,
useClass: HttpServiceInterceptor,
},
],
})
export class AppModule {}

Ok, I managed to solve this by actually using a middleware that I initially thought. I am not a 100% sure this is the NEST way but intercepting the underlying axios reference of the HttpService did the trick:
#Injectable()
export class BearerMiddleware implements NestMiddleware {
constructor(private readonly httpService: HttpService) {}
use(req: Request, res: Response, next: Function) {
this.httpService.axiosRef.interceptors.request.use(request => {
request.headers = {
...request.headers,
Authorization: req.headers.Authorization || '',
};
return request;
});
next();
}
}

Required import HttpModule into your module
import { HttpModule } from '#nestjs/axios';
#Module({
providers: [],
controllers: [YourController],
imports: [
HttpModule,
],
exports: [],
})
export class YourModule {}
In your controller
import { HttpService } from '#nestjs/axios';
import { Controller, HttpStatus, Res, BadRequestException } from '#nestjs/common';
#Controller('your-controller')
export class YourController {
constructor(
private httpService: HttpService,
) {}
async fetchApi(#Res() res) {
const urlAPI = 'https://xxx.vn';
try {
const source = await this.httpService.get(urlAPI,
{
headers: { Authorization: 'Basic XXX-TOKEN' },
},
);
return res.status(HttpStatus.OK).json({ ...source });
} catch (error) {
throw new BadRequestException(error.response?.statusText);
}
}
}

Related

angular2: how to get Full path on CanLoad guard while maintaining redirect url

I am using angular 2.4 version and router version "^3.4.10".
I am trying to handle redirect url using auth guard service.
When user hit url 'domain/assignment/3/detail' and if user is not login then user redirected to 'domain/login' page.
and when user successfully login in to system then redirected to 'domain/assignment/3/detail' previous url which user tries to access.
I have implemented CanLoad guard on assignment module. so when user tries to access url 'domain/assignment/3/detail' and if user is not login, url stores into redirectUrl property of authservice (this.authService.redirectUrl).
so here is the issue comes in my case. i am not able to get full path of the url which user hit.
i am getting 'assignment' instead 'assignment/3/detail' within CanLoad guard.
how can i get full path so that i can redirect user to proper path within CanLoad guard.
CanLoad:
canLoad(route: Route): boolean {
let url = `/${route.path}`; // here i got url path 'assignment' instead 'assignment/3/detail'
return this.checkLogin(url);
}
Main routing app.routes.ts
const routes: Routes = [
{ path: '', redirectTo: 'login', pathMatch: 'full' },
{ path: 'login', component: LoginComponent},
{
path: 'assignment',
loadChildren: './assignment/assignment.module#AssignmentModule',
canLoad: [AuthGuard]
},
{ path: '**', redirectTo: '', pathMatch: 'full' }];
Assignment routing: assignment-routing.ts
const assignmentRoutes: Routes = [
{
path: '',
component: AssignmentComponent,
canActivate: [AuthGuard]
children: [
{
path: '',
canActivateChild: [AuthGuard],
children: [
{
path: ':assignmentId/detail', component: AssignmentDetailComponent,
canActivate: [AuthGuard]
}
]
}]
}];
AuthGuard: auth-gurad.service.ts
import { Injectable } from '#angular/core';
import {
CanActivate, Router,
ActivatedRouteSnapshot,
RouterStateSnapshot,
CanActivateChild,
NavigationExtras,
CanLoad, Route
} from '#angular/router';
import { AuthService } from './auth.service';
#Injectable()
export class AuthGuard implements CanActivate, CanActivateChild, CanLoad {
constructor(private authService: AuthService, private router: Router) { }
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
let url: string = state.url;
return this.checkLogin(url);
}
canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
return this.canActivate(route, state);
}
canLoad(route: Route): boolean {
let url = `/${route.path}`; // here i got url path 'assignment' instead 'assignment/3/detail'
return this.checkLogin(url);
}
checkLogin(url: string): boolean {
if (this.authService.isLoggedIn) {
if(this.authService.redirectUrl!=null){
let redirectUrl = this.authService.redirectUrl;
this.authService.redirectUrl = null;
this.this.router.navigate([redirectUrl]);
}
return true;
}
// Store the attempted URL for redirecting
this.authService.redirectUrl = url;
// Navigate to the login page
this.router.navigate(['/login']);
return false;
}
}
If you look the signature of the canLoad method, there is is a second parameter segments which you can use to generate the url.
canLoad(route: Route, segments: UrlSegment[]): Observable<boolean> | Promise<boolean> | boolean
You can generate the full path using following code:
canLoad(route: Route, segments: UrlSegment[]): boolean {
const fullPath = segments.reduce((path, currentSegment) => {
return `${path}/${currentSegment.path}`;
}, '');
console.log(fullPath);
}
Note: With this method you will get the full path, but you will not get any query parameters.
I have this exact same problem. These issues are related
https://github.com/angular/angular/issues/17359
https://github.com/angular/angular/issues/12411
And they even have an open PR about this which hasn't been merged yet
https://github.com/angular/angular/pull/13127
The workaround for now seems to be to replace the canLoad method with the canActivate, there you can have access to the full path, the bad thing of course is that you're loading the module
Edit: Also for you Assignment routing, there's no need to call canActivate for each child route. Have it defined on the parent route should apply the guard for all child routes.
This workaround from this reply on the github issue worked very well for me.
export class AuthnGuard implements CanLoad {
constructor(private authnService: AuthnService, private router: Router) { }
canLoad( ): Observable<boolean> | Promise<boolean> | boolean {
const navigation = this.router.getCurrentNavigation();
let url = '/';
if (navigation) {
url = navigation.extractedUrl.toString();
}
this.authnService.storeRedirectUrl(url);
There is another temporary workaround instead of replacing canLoad with canActivate:
You can store the url via redirectUrl = location.pathname and redirect later via this.router.navigateByUrl(redirectUrl);. Of course, if you work on a subpath (like domain) you have to adjust the pathname a little bit.
In Angular 9, following reply worked for me.
My code
export class AuthGuard implements CanLoad {
constructor(private router: Router, private loginService: LoginService) { }
canLoad( ): Observable<boolean> | Promise<boolean> | boolean {
if (this.loginService.isLoggedIn()) return true;
this.router.events.pipe(first(_ => _ instanceof NavigationCancel)).subscribe((event: NavigationCancel) => {
this.router.navigate(['/login'], {
queryParams: {
redirectTo: event.url
}
});
});
return false;
}
}
information available on
this.router.getCurrentNavigation().extractedUrl
relates to this issue https://github.com/angular/angular/issues/30633
I'm using
this.router.getCurrentNavigation().extractedUrl.toString()
to access the full route, since
segments.reduce((path, currentSegment) => {
return `${path}/${currentSegment.path}`;
}, '')
will not provide the full path if you have nested routes (only child routes).

Angular2 ( 2.2.1 ) Http post request progress bar [duplicate]

Is there currently a way within Angular 2 to retrieve the progress (i.e. percentage done) of an ajax call, using the angular2/http module?
I use the following code to make my HTTP calls:
let body = JSON.stringify(params);
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
this.http.post(url, body, options)
.timeout(10000, new Error('Timeout exceeded during login'))
.toPromise()
.then((res) => {
...
}).catch((err) => {
...
});
The goal is to write a synchronisation system. The post will return a lot of data, and I want to give the user an indication on how long the syncing will take.
Currently (from v. 4.3.0, when using new HttpClient from #ngular/common/http) Angular provides listening to progress out of the box. You just need to create HTTPRequest object as below:
import { HttpRequest } from '#angular/common/http';
...
const req = new HttpRequest('POST', '/upload/file', file, {
reportProgress: true,
});
And when you subscribe to to request you will get subscription called on every progress event:
http.request(req).subscribe(event => {
// Via this API, you get access to the raw event stream.
// Look for upload progress events.
if (event.type === HttpEventType.UploadProgress) {
// This is an upload progress event. Compute and show the % done:
const percentDone = Math.round(100 * event.loaded / event.total);
console.log(`File is ${percentDone}% uploaded.`);
} else if (event instanceof HttpResponse) {
console.log('File is completely uploaded!');
}
});
More info here.
You could leverage the onprogress event provided by XHR (see this plunkr: http://plnkr.co/edit/8MDO2GsCGiOJd2y2XbQk?p=preview).
This allows to get hints about the progress of the download. This isn't supported out of the box by Angular2 but you can plug it by extended the BrowserXhr class:
#Injectable()
export class CustomBrowserXhr extends BrowserXhr {
constructor(private service:ProgressService) {}
build(): any {
let xhr = super.build();
xhr.onprogress = (event) => {
service.progressEventObservable.next(event);
};
return <any>(xhr);
}
}
and override the BrowserXhr provider with the extended:
bootstrap(AppComponent, [
HTTP_PROVIDERS,
provide(BrowserXhr, { useClass: CustomBrowserXhr })
]);
See this question for more details:
Angular2 / RxJS - updating variable after getting data from Http observable
When you make http cals in angular2, it returns an Observable of type Response, this response is created inside class called XHRConnection where all the magic happens.
The XHRConnection builds the response by listening to XMLHttpRequest's load event, this means it will return one response at the end of the request.
Now to be able to alter this behavior we need to make our connection class listen to the progress event.
So we need to create custom Connection class, to handle the response as we see fit.
I did it this way,
Take note that my php API returns multi response in a single request and this responses are plain strings.
my_backend.ts
import {Injectable} from "angular2/core";
import {Observable} from "rxjs/Observable";
import {Observer} from "rxjs/Observer";
import {Connection,ConnectionBackend} from "angular2/src/http/interfaces";
import {ReadyState, RequestMethod, ResponseType} from "angular2/src/http/enums";
import {ResponseOptions} from "angular2/src/http/base_response_options";
import {Request} from "angular2/src/http/static_request";
import {Response} from "angular2/src/http/static_response";
import {BrowserXhr} from "angular2/src/http/backends/browser_xhr";
import {Headers} from 'angular2/src/http/headers';
import {isPresent} from 'angular2/src/facade/lang';
import {getResponseURL, isSuccess} from "angular2/src/http/http_utils"
export class MyConnection implements Connection {
readyState: ReadyState;
request: Request;
response: Observable<Response>;
constructor(req: Request, browserXHR: BrowserXhr, baseResponseOptions?: ResponseOptions) {
this.request = req;
this.response = new Observable<Response>((responseObserver: Observer<Response>) => {
let _xhr: XMLHttpRequest = browserXHR.build();
_xhr.open(RequestMethod[req.method].toUpperCase(), req.url);
// save the responses in array
var buffer :string[] = [];
// load event handler
let onLoad = () => {
let body = isPresent(_xhr.response) ? _xhr.response : _xhr.responseText;
//_xhr.respons 1 = "Loading data!"
//_xhr.respons 2 = "Loading data!Ready To Receive Orders."
// we need to fix this proble
// check if the current response text contains the previous then subtract
// NOTE: I think there is better approach to solve this problem.
buffer.push(body);
if(buffer.length>1){
body = buffer[buffer.length-1].replace(buffer[buffer.length-2],'');
}
let headers = Headers.fromResponseHeaderString(_xhr.getAllResponseHeaders());
let url = getResponseURL(_xhr);
let status: number = _xhr.status === 1223 ? 204 : _xhr.status;
let state:number = _xhr.readyState;
if (status === 0) {
status = body ? 200 : 0;
}
var responseOptions = new ResponseOptions({ body, status, headers, url });
if (isPresent(baseResponseOptions)) {
responseOptions = baseResponseOptions.merge(responseOptions);
}
let response = new Response(responseOptions);
//check for the state if not 4 then don't complete the observer
if(state !== 4){
//this will return stream of responses
responseObserver.next(response);
return;
}
else{
responseObserver.complete();
return;
}
responseObserver.error(response);
};
// error event handler
let onError = (err: any) => {
var responseOptions = new ResponseOptions({ body: err, type: ResponseType.Error });
if (isPresent(baseResponseOptions)) {
responseOptions = baseResponseOptions.merge(responseOptions);
}
responseObserver.error(new Response(responseOptions));
};
if (isPresent(req.headers)) {
req.headers.forEach((values, name) => _xhr.setRequestHeader(name, values.join(',')));
}
_xhr.addEventListener('progress', onLoad);
_xhr.addEventListener('load', onLoad);
_xhr.addEventListener('error', onError);
_xhr.send(this.request.text());
return () => {
_xhr.removeEventListener('progress', onLoad);
_xhr.removeEventListener('load', onLoad);
_xhr.removeEventListener('error', onError);
_xhr.abort();
};
});
}
}
#Injectable()
export class MyBackend implements ConnectionBackend {
constructor(private _browserXHR: BrowserXhr, private _baseResponseOptions: ResponseOptions) {}
createConnection(request: Request):MyConnection {
return new MyConnection(request, this._browserXHR, this._baseResponseOptions);
}
}
And in the app.component.ts
import {Component, provide} from 'angular2/core';
import {HTTP_PROVIDERS,XHRBackend} from 'angular2/http';
import {MyBackend} from './my_backend';
#Component({
selector: 'my-app',
providers: [
HTTP_PROVIDERS,
MyBackend,
provide(XHRBackend, {useExisting:MyBackend})
]
.
.
.
Now calling http.get will return a steam of responses.
#Bartek Chichoki's answer is correct but it was not working for my case.
Adding observe: 'events' did the trick for me
const req = new HttpRequest('POST', '/upload/file', file, {
reportProgress: true,
observe: 'events'
});
Hope it helps
I strongly recomend using this
https://www.npmjs.com/package/angular-progress-http
otherwise messing around with xhr will make you miss sessions cookies and other stuffs
besides it'll be more portable and way easier to implement

How do I defer loading a component until my signalr service is initialized?

I have the following types:
DataService - Gets data from the server using signalr hub.
AppComponent - which is the entry point for my main application
The data service constructor is as follows.
constructor(private http: Http) {
var hub = $.connection.spwHub;
hub.client.loadEmployees = this.loadEmployees;
$.connection.hub.start().done(() => {
...
});
}
My AppComponent is as follows:
constructor(service: DataService) {
this.company = service.getCompany();
service.getEmployees().then(employees => this.employees = employees);
this.departments = service.getDepartments();
}
I get the following error of course because the hub async call has not returned before the hub connection is made.
EXCEPTION: Error in ./AppComponent class AppComponent_Host - inline template:0:0 caused by: SignalR: Connection has not been fully initialized. Use .start().done() or .start().fail() to run logic after the connection has started.
What is the best way to deal with this issue in AngularJs2?
You can use the APP_INITIALIZER hook to perform logic, get something prepped, whatever, that you need before the rest of the application runs.
In your app.module.ts (or whatever your main module is):
import { APP_INITIALIZER, NgModule } from "#angular/core";
export function init_app(employeeService: EmployeeService) {
return () => employeeService.getEmployees();
}
#NgModule({
<...>
providers: [EmployeeService, {
provide: APP_INITIALIZER,
useFactory: init_app,
deps: [ EmployeeService ],
multi: true
}]
})
export class AppModule { }
The service is returning a Promise which will be automatically handled:
getEmployees() {
return <...function stuff ...>
.toPromise();
}
And here's the github issue where this is documented (no doc on the angular.io site yet): https://github.com/angular/angular/issues/9047
After searching and finding nothing I gave the idea that components that don't need to be loaded should probably be deferred by default. This means that the answer is a no-brainer.
// start.component.ts
constructor() {
// Start the connection
var hub = $.connection.spwHub;
$.connection.hub.start().done(() => {
// This loads the next component and runs the constructor
this.initialized = true;
});
}
// start.component.html
<div *ngIf="initialized">
<main-component></main-component>
<div>
// This type is lazy loaded as soon as the initialized equals true.
// main.component.ts
constructor(employeeService: EmployeeService) {
// Finally, load the needed data.
this.employees = employeeService.getEmployees();
}

Angular2 http get request with Observables and dynamic url params. How to?

Having this angular2 service taken from official docs with Observable, trying to modify to can pass on fly to the base heroesUrl dynamic parameters like app/heroes/{{country}} and use it like
getHeroes(country) {}
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Hero } from './hero';
import { Observable } from 'rxjs/Observable';
#Injectable()
export class HeroService {
constructor (private http: Http) {}
private heroesUrl = 'app/heroes'; // URL to web API
getHeroes (): Observable<Hero[]> {
return this.http.get(this.heroesUrl)
.map(this.extractData)
.catch(this.handleError);
}
private extractData(res: Response) {
let body = res.json();
return body.data || { };
}
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
return Observable.throw(errMsg);
}
}
How would I do that?
I think you just need to do following things if I understood your point,
getHeroes(country) {}
export class HeroService {
constructor (private http: Http) {}
private heroesUrl = 'app/heroes'; // URL to web API
getHeroes (country): Observable<Hero[]> { //<-----added parameter
return this.http.get(this.heroesUrl + '/' + country) //<-----changed
.map(this.extractData)
.catch(this.handleError);
}
...
...
}

What is httpinterceptor equivalent in angular2?

In angularjs, we have http interceptor
$httpProvider.interceptors.push('myHttpInterceptor');
with which we can hook into all http calls, and show or hide loading bars, do logging, etc..
What is the equivalent in angular2?
As #Günter pointed it out, there is no way to register interceptors. You need to extend the Http class and put your interception processing around HTTP calls
First you could create a class that extends the Http:
#Injectable()
export class CustomHttp extends Http {
constructor(backend: ConnectionBackend, defaultOptions: RequestOptions) {
super(backend, defaultOptions);
}
request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
console.log('request...');
return super.request(url, options).catch(res => {
// do something
});
}
get(url: string, options?: RequestOptionsArgs): Observable<Response> {
console.log('get...');
return super.get(url, options).catch(res => {
// do something
});
}
}
and register it as described below:
bootstrap(AppComponent, [HTTP_PROVIDERS,
new Provider(Http, {
useFactory: (backend: XHRBackend, defaultOptions: RequestOptions) => new CustomHttp(backend, defaultOptions),
deps: [XHRBackend, RequestOptions]
})
]);
The request and requestError kinds could be added before calling the target methods.
For the response one, you need to plug some asynchronous processing into the existing processing chain. This depends on your need but you can use operators (like flatMap) of Observable.
Finally for the responseError one, you need to call the catch operator on the target call. This way you will be notified when an error occurs in the response.
This links could help you:
Handling refresh tokens using rxjs
Angular 2 - How to get Observable.throw globally
update
The new HttpClient module introduced in Angular 4.3.0 supports interceptors https://github.com/angular/angular/compare/4.3.0-rc.0...4.3.0
feat(common): new HttpClient API HttpClient is an evolution of the
existing Angular HTTP API, which exists alongside of it in a separate
package, #angular/common/http. This structure ensures that existing
codebases can slowly migrate to the new API.
The new API improves significantly on the ergonomics and features of
the legacy API. A partial list of new features includes:
Typed, synchronous response body access, including support for JSON body types
JSON is an assumed default and no longer needs to be explicitly parsed
Interceptors allow middleware logic to be inserted into the pipeline
Immutable request/response objects
Progress events for both request upload and response download
Post-request verification & flush based testing framework
original
Angular2 doesn't have (yet) interceptors. You can instead extend Http, XHRBackend, BaseRequestOptions or any of the other involved classes (at least in TypeScript and Dart (don't know about plain JS).
See also
RFC: Http interceptors and transformers
Introduce an interception mechanism
Interceptors in Angular2
Angular2 - set headers for every request
There's an implementation for a Http #angular/core-like service in this repository: https://github.com/voliva/angular2-interceptors
You just declare the provider for that service on bootstrap, adding any interceptors you need, and it will be available for all the components.
import { provideInterceptorService } from 'ng2-interceptors';
#NgModule({
declarations: [
...
],
imports: [
...,
HttpModule
],
providers: [
MyHttpInterceptor,
provideInterceptorService([
MyHttpInterceptor,
/* Add other interceptors here, like "new ServerURLInterceptor()" or
just "ServerURLInterceptor" if it has a provider */
])
],
bootstrap: [AppComponent]
})
DEPRICATED SINCE Angular 4.3 (HttpInterCeptors are Back in 4.3)
You can create your own custom HTTP Class and use rxjs Subject Service to reuse your custom Http Class and implement your behaviors in a custom class.
Implementation of your Custom Http Class with "HttpSubjectService" which contains some rxjs Subjects.
import { Injectable } from '#angular/core';
import { Http, ConnectionBackend, Request, RequestOptions, RequestOptionsArgs, Response } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import { HttpSubjectService } from './httpSubject.service';
#Injectable()
export class CustomHttp extends Http {
constructor(backend: ConnectionBackend, defaultOptions: RequestOptions, private httpSubjectService: HttpSubjectService) {
super(backend, defaultOptions);
//Prevent Ajax Request Caching for Internet Explorer
defaultOptions.headers.append("Cache-control", "no-cache");
defaultOptions.headers.append("Cache-control", "no-store");
defaultOptions.headers.append("Pragma", "no-cache");
defaultOptions.headers.append("Expires", "0");
}
request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
//request Start;
this.httpSubjectService.addSpinner();
return super.request(url, options).map(res => {
//Successful Response;
this.httpSubjectService.addNotification(res.json());
return res;
})
.catch((err) => {
//Error Response.
this.httpSubjectService.removeSpinner();
this.httpSubjectService.removeOverlay();
if (err.status === 400 || err.status === 422) {
this.httpSubjectService.addHttp403(err);
return Observable.throw(err);
} else if (err.status === 500) {
this.httpSubjectService.addHttp500(err);
return Observable.throw(err);
} else {
return Observable.empty();
}
})
.finally(() => {
//After the request;
this.httpSubjectService.removeSpinner();
});
}
}
Custom module to register your CustomHttp class - here you overwrite the default Http implementation from Angular with your own CustomHttp Implementation.
import { NgModule, ValueProvider } from '#angular/core';
import { HttpModule, Http, XHRBackend, RequestOptions } from '#angular/http';
//Custom Http
import { HttpSubjectService } from './httpSubject.service';
import { CustomHttp } from './customHttp';
#NgModule({
imports: [ ],
providers: [
HttpSubjectService,
{
provide: Http, useFactory: (backend: XHRBackend, defaultOptions: RequestOptions, httpSubjectService: HttpSubjectService) => {
return new CustomHttp(backend, defaultOptions, httpSubjectService);
},
deps: [XHRBackend, RequestOptions, HttpSubjectService]
}
]
})
export class CustomHttpCoreModule {
constructor() { }
}
now we need the HttpSubjectService Implementation where we can SubScribe to our rxjs Subjects when they get called with the "next" statement.
import { Injectable } from '#angular/core';
import { Subject } from 'rxjs/Subject';
#Injectable()
export class HttpSubjectService {
//https://github.com/ReactiveX/rxjs/blob/master/doc/subject.md
//In our app.component.ts class we will subscribe to this Subjects
public notificationSubject = new Subject();
public http403Subject = new Subject();
public http500Subject = new Subject();
public overlaySubject = new Subject();
public spinnerSubject = new Subject();
constructor() { }
//some Example methods we call in our CustomHttp Class
public addNotification(resultJson: any): void {
this.notificationSubject.next(resultJson);
}
public addHttp403(result: any): void {
this.http403Subject.next(result);
}
public addHttp500(result: any): void {
this.http500Subject.next(result);
}
public removeOverlay(): void {
this.overlaySubject.next(0);
}
public addSpinner(): void {
this.spinnerSubject.next(1);
}
public removeSpinner(): void {
this.spinnerSubject.next(-1);
}
}
to call your custom Implementations we need to Subscribe to the Subjects in the e.g. "app.component.ts".
import { Component } from '#angular/core';
import { HttpSubjectService } from "../HttpInterception/httpSubject.service";
import { Homeservice } from "../HttpServices/home.service";
#Component({
selector: 'app',
templateUrl: './app.component.html',
})
export class AppComponent {
private locals: AppLocalsModel = new AppLocalsModel();
constructor(private httpSubjectService : HttpSubjectService, private homeService : Homeservice) {}
ngOnInit(): void {
this.notifications();
this.httpRedirects();
this.spinner();
this.overlay();
}
public loadServiceData(): void {
this.homeService.getCurrentUsername()
.subscribe(result => {
this.locals.username = result;
});
}
private overlay(): void {
this.httpSubjectService.overlaySubject.subscribe({
next: () => {
console.log("Call Overlay Service");
}
});
}
private spinner(): void {
this.httpSubjectService.spinnerSubject.subscribe({
next: (value: number) => {
console.log("Call Spinner Service");
}
});
}
private notifications(): void {
this.httpSubjectService.notificationSubject.subscribe({
next: (json: any) => {
console.log("Call Notification Service");
}
});
}
private httpRedirects(): void {
this.httpSubjectService.http500Subject.subscribe({
next: (error: any) => {
console.log("Navigate to Error Page");
}
});
this.httpSubjectService.http403Subject.subscribe({
next: (error: any) => {
console.log("Navigate to Not Authorized Page");
}
});
}
}
class AppLocalsModel {
public username : string = "noch nicht abgefragt";
}
SINCE ANGULAR 4.3 you can Use InterCeptors
In Angular 4.3 you have native Interceptors where you can implement your own stuff like a redirect for server error 500
import { Injectable } from '#angular/core';
import { Router } from '#angular/router';
import { HttpInterceptor, HttpHandler, HttpRequest, HttpEvent, HttpResponse } from '#angular/common/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
#Injectable()
export class SxpHttp500Interceptor implements HttpInterceptor {
constructor(public router: Router) { }
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(req).do(evt => { }).catch(err => {
if (err["status"]) {
if (err.status === 500) {
this.router.navigate(['/serverError', { fehler: JSON.stringify(err) }]);
}
}
return Observable.throw(err);
});
}
}
you need to Register this in your core module in the providers Array
import { HTTP_INTERCEPTORS } from '#angular/common/http';
import { Router } from '#angular/router';
import { SxpHttp500Interceptor } from "./sxpHttp500.interceptor";
....
providers: [
{
provide: HTTP_INTERCEPTORS, useFactory: (router: Router) => { return new SxpHttp500Interceptor(router) },
multi: true,
deps: [Router]
}
]
With the Angular 4.3.1 release, there's now an interface called HttpInterceptor.
Here's the link to the docs:
https://angular.io/api/common/http/HttpInterceptor
Here's an implementation sample.
This would be the interceptor class implementation.
Is basically written as any other service:
#Injectable()
export class ExceptionsInterceptor implements HttpInterceptor {
constructor(
private logger: Logger,
private exceptionsService: ExceptionsService,
private notificationsService: NotificationsService
) { }
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(request)
.do((event) => {
// Do nothing here, manage only errors
}, (err: HttpErrorResponse) => {
if (!this.exceptionsService.excludeCodes.includes(err.status)) {
if (!(err.status === 400 && err.error['_validations'])) {
this.logger.error(err);
if (!this.notificationsService.hasNotificationData(err.status)) {
this.notificationsService.addNotification({ text: err.message, type: MessageColorType.error, data: err.status, uid: UniqueIdUtility.generateId() });
}
}
}
});
}
}
Then since you'll treat this like a normal service, you have to add this line inside your app module's providers:
{ provide: HTTP_INTERCEPTORS, useClass: ExceptionsInterceptor, multi: true }
Hope it can help.
Angular 4.3 now supports Http interceptor out-of-the-box.
Check it out how to use them:
https://ryanchenkie.com/angular-authentication-using-the-http-client-and-http-interceptors
I have released interceptor with following node module. We was create this module for our internal purpose finally we released in npm package manager
npm install angular2-resource-and-ajax-interceptor
https://www.npmjs.com/package/angular2-resource-and-ajax-interceptor
As #squadwuschel pointed out, work is underway to get this functionality into #angular/http. This will be in the form of a new HttpClient API.
See https://github.com/angular/angular/pull/17143 for more details and current status.
Angular2 donot support httpinterceptor like angular1
Here is awesome example of use of httpinterceptor in angular2.
https://github.com/NgSculptor/ng2HttpInterceptor
Try Covalent from Teradata, they provides lots of extensions for Angular and Angular Material.
Check HTTP part, it provides the missing http interceptor in Angular and RESTService(similar to restangular).
I have implemented JWT token authentication via Covalent HTTP in my sample, Please check here.
https://github.com/hantsy/angular2-material-sample/blob/master/src/app/core/auth-http-interceptor.ts
Read my development notes for it, Handle token based Authentication via IHttpInterceptor.

Resources