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

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);
}
...
...
}

Related

NestJS passing Authorization header to HttpService

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);
}
}
}

Angular2 not sending HTTP GET

The code listed in (A) never sends its HTTP request when test() is called whereas the almost identical code in (B) does work. Any idea what the problem is? I know whether the request is sent by watching the server logs. Also, if I make the request in (A) manually by pasting it into the browser, I get the expected response. I'm stumped!
(A)
import { Constants } from '../toplevel/constants'
import { Injectable } from '#angular/core';
import { Http, Response, Headers, RequestOptions } from '#angular/http';
import { Observable} from 'rxjs/Rx';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
#Injectable()
export class SigninService {
constructor (private http: Http, private constants : Constants) {
}
getToken(username: string, password: string) : Observable<string>{
var url = `${this.constants.apiRoot}/users/${username}?${password}`
console.log(`Calling api server with url ${url}`)
return this.http.get(url)
.map((res:Response) => JSON.stringify(res.json()))
.catch((error:any) => Observable.throw(error.json().error || 'Server error'));
}
test() {
this.getToken('jc', 'yadayada')
}
}
(B: excerpt)
getDocument(id: string) : Observable<Document>{
return this.http.get(`${this.apiUrl}/documents/${id}`)
.map((res:Response) => new Document(res.json()['document']))
.catch((error:any) => Observable.throw(error.json().error || 'Server error'));
}
Your sequence is cold. You need to change your test to:
this.getToken('jc', 'yadayada').subscribe()
to make it active and send the request

AngularJs 2 Http CONNECTION ERROR

AngularJs 2 with Webpack.
I am not able to connect to NYT Api.
ALL ENDOPOINTS TESTED AND WORKING PROPERLY
AngularJs 2 in production mode:
enableProdMode();
App:
-1 component
-1 Service
All other components working/displaying properly.
No other services on app yet.
Service returns with error: (in console)
error: "Collection 'topstories' not found"
The Service
import { Injectable } from '#angular/core';
import { Http, Headers, URLSearchParams, Response } from '#angular/http';
import 'rxjs/add/operator/toPromise';
import { Observable } from 'rxjs';
#Injectable()
export class NewsService {
private topStoriesUrl: string = `https://api.nytimes.com/svc/topstories/v2/politics.json`;
// private topStoriesUrl: string = `https://newsapi.org/v1/articles`;
// Injecting Http capabilities
constructor( private http: Http ) {}
// for error handling
private handleError(error: any): Promise<any>{
console.error("FromSERVICE:::---:::--> ", error);
return Promise.reject( error.message || error );
}
getNews(): Observable<any> {
let parms: URLSearchParams = new URLSearchParams();
parms.set("api-key", "184db335652341518bea3e4a5db85494");
// parms.set("source", "associated-press");
// parms.set("apiKey", "e4e2aa62a883464a87547e8de4336f61");
return this.http.get( this.topStoriesUrl, { search: parms } )
.map( (res: Response) => res['articles'] )
.catch( this.handleError );
}
}
The Component
import { Component, OnInit } from '#angular/core';
import { Router } from '#angular/router';
import { Observable } from 'rxjs';
// service for fetcing news from api
import { NewsService } from '../services/news.service';
#Component({
selector: 'main-news',
templateUrl: '../templates/main-news.component.html'
})
export class MainNewsComponent implements OnInit{
private news: Observable<any>;
constructor(
private router: Router,
private newsService: NewsService
) {}
ngOnInit(): void {
this.newsService.getNews().subscribe( {
next: r => this.news = Observable.of<any[]>(["one"]),
error: err => console.error("From COMPONENT--->", err)
} );
}
}
I have tried this call with both Api from different organizations to get
the same error on the URL resource.
I have tried this same call with said URL with a Ruby script (NET/http) and also directly on the browser address bar, to receive a VALID response on these BOTH cases.
NOT SURE WHY IT FAILS WITH ANGULAR.
HELP!!!
From your console error it looks like the url is not found on server.
The error displayed is returned from server its not angularjs specific error.
Notice 404 not found returned.
So check your url and server again.
Turns out I was missing the:
Access-control-allow-origin
header.
Go figure!
Apparently, it is not added automatically by AngularJs 2.
Thanx

How to make an API call in Angular 2?

There are lots of resources out there already but I haven't been able to find one that works for one reason or another. Take a generic example: I want to get the response from http://swapi.co/api/people, which will be a list of people from Star Wars.
import {Injectable } from '#angular/core';
import {Http, Response} from '#angular/http';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/map';
#Injectable()
export class OombaDataService {
constructor(private http: Http) {}
private usersUrl = 'http://swapi.co/api/people/';
getData() {
return this.http.get(this.usersUrl)
.map(this.extractData)
}
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);
}
}
A lot of this should be correct since it's based on Angular's own tutorial on the matter. But for whatever reason, when I call it in my components, it just returns an observable object without the JSON data. What am I missing?
At this method:
private extractData(res: Response) {
let body = res.json();
return body.data || { };
}
At the first line, you parse the result of the API call as JSON into a JavaScript object.
Then you return the property data of that object, if it exists. If it doesn't exist, you return an empty object ({ }).
The thing is that the API at http://swapi.co/api/people/ does not bring a response that contains a data property, which means that the extractData() method is always returning an observable of an empty object ({ }).
Besides that, the getData() really returns an Observable, so to get its value, you must subscribe to it, such as:
#Component({
...
providers: [OombaDataService]
})
export class SomeComponent {
constructor(oombaDataService: OombaDataService) {
oombaDataService.getData().subscribe(
x => {
console.log("VALUE RECEIVED: ",x);
},
x => {
console.log("ERROR: ",x);
},
() => {
console.log("Completed");
}
);
}
}
And, since, as said, that API's response does not have any .data property in it, the extractData() should really be (at least until you figure out what you want):
private extractData(res: Response) {
return res.json();
}
That should get things working. Here's a working plunker.

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