Angular 2 And Controlling HTTP Request and Response - angularjs

In Angular 2 I am trying to control http request/response to set/read some headers when sending request and getting response.
I just override HttpRequest like this, and it is working for me :
#Injectable()
export class HttpRequest extends RequestOptions {
constructor() {
super({
method: RequestMethod.Get,
headers: new Headers({
'X-Some-Header': 'some-content'})
});
}
}
But for overriding Response I have problem :
#Injectable()
export class HttpResponse extends Response {
constructor(responseOptions: ResponseOptions) {
super(responseOptions);
console.log("RESPONSE IS CREATED!!!!!!!");
}
}
The constructor never being called and here it is the bootstrap:
bootstrap(AppComponent,
[ ROUTER_PROVIDERS
,HTTP_PROVIDERS
,provide(RequestOptions, {useClass: HttpRequest})
,provide(Response, {useClass: HttpResponse})
]);
The reason for overriding the response is to read some response headers and control for 404-Not Page Found globally and ....
Thanks,

You can't override Response I remember seeing that it is being created using new Response in one of the related Http classes instead of requesting it from the injector, which is reasonable because Response needs to be a dufferent instance for each request.

You could implement it in a few ways: create base service class or provide custom xhr implementation:
#Injectable()
export class CustomBrowserXhr extends BrowserXhr {
constructor() {}
build(): any {
let xhr:XMLHttpRequest = super.build();
/*...add headers, listeners etc...*/
return <any>(xhr);
}
}
bootstrap(AppComponent, [
HTTP_PROVIDERS,
provide(BrowserXhr, { useClass: CustomBrowserXhr })
]);

Related

ionic 2 http post service is not working

hey i m new in ionic 2 http request, and facing problem in http post services, following is my code you please see and help me to finding problem.
import { Component } from '#angular/core';
import { NavController } from 'ionic-angular';
import { Http, Headers, RequestOptions } from '#angular/http';
import 'rxjs/add/operator/map';
#Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
data:any;
name:any;
error:any;
constructor(public navCtrl: NavController, public http:Http) {
}
postit(){
var headers=new Headers();
headers.append('Content-Type', 'application/json');
var data = JSON.stringify({
username: "username",
password: "password"
});
this.http.post('url',JSON.stringify(data),{headers:headers})
.subscribe(res=>{
this.data=res.json();
this.name=this.data.userId;
console.log(res.json());
},
(err)=>{
this.error=err;
console.log("some error:",err);
});
}
}
url is of login page which need credentials. Is this code fine specially json data object.
please help me i have wasted my 4 days in this problem.
thank you.
You are calling JSON.stringify twice - once when you create the data and once when you send the POST. Remove the second one and leave only 'data'.

ionic calling a service

I defined a service in IONIC like this (file reddit.service.ts):
import {Injectable} from '#angular/core';
import {Http} from '#angular/http';
import 'rxjs/Rx';
#Injectable()
export class RedditService{
http:any;
baseUrl: String;
counstructor(http:Http){
this.http = http;
this.baseUrl = 'https://www.reddit.com/r';
}
getPosts(category, limit){
return this.http.get(this.baseUrl+'/'+category+'/top.json?limit='+limit).map(res => res.json());
}
}
I am calling this service like this (file reddits.ts):
getPosts(category, limit){
this.redditService.getPosts(category, limit).subscribe(response => {console.log(response);
});
The error Message I am getting is:
Error: Uncaught (in promise): TypeError: Cannot read property 'get' of undefined
TypeError: Cannot read property 'get' of undefined
at RedditService.getPosts (reddit.service.ts:16)
Why I am getting this error? What is wrong?
You have a typo:
counstructor(http:Http)
should be:
constructor(http:Http)
and usually we just inject providers into constructor like...
#Injectable()
export class RedditService{
baseUrl: String;
constructor(private http:Http){
this.baseUrl = 'https://www.reddit.com/r';
}
}
UPDATE:
As the HttpModule used to be included in the IonicModule before, it no longer is, and therefore the HttpModule needs to be imported in the NgModule and set in the imports array.
It seems like you have http service haven't injected properly inside Service constructor. Also make sure you have imported Http & Injectable injector correctly.
#Injectable()
export class RedditService {
//make sure you have below dependency inject with private access specifier.
//since we mentioned it private, http will be available in component context
constructor(private http: Http) { }
getPosts(category, limit){
return this.http.get(this.baseUrl+'/'+category+'/top.json?limit='+limit).map(res => res.json());
}
}
Additionally please add HttpModule in imports of your AppModule to make http API working.

angular 2, http service not requesting [duplicate]

When I make a post request the angular 2 http is not sending this request
this.http.post(this.adminUsersControllerRoute, JSON.stringify(user), this.getRequestOptions())
the http post is not sent to the server but if I make the request like this
this.http.post(this.adminUsersControllerRoute, JSON.stringify(user), this.getRequestOptions()).subscribe(r=>{});
Is this intended and if it is can someone explain me why ? Or it is a bug ?
Since the post method of the Http class returns an observable you need to subscribe it to execute its initialization processing. Observables are lazy.
You should have a look at this video for more details:
https://egghead.io/lessons/rxjs-rxjs-observables-vs-promises
You must subscribe to the returned observable if you want the call to execute.
See also the following angular documentation "Communicating with backend services using HTTP".
Starting the request
For all HttpClient methods, the method doesn't begin its HTTP request until you call subscribe() on the observable the method returns.
This is true for all HttpClient methods.
You should always unsubscribe from an observable when a component is destroyed.
All observables returned from HttpClient methods are cold by design.
Execution of the HTTP request is deferred, letting you extend the observable with additional operations such as tap and catchError before anything actually happens.
Calling subscribe() triggers execution of the observable and causes HttpClient to compose and send the HTTP request to the server.
Think of these observables as blueprints for actual HTTP requests.
In fact, each subscribe() initiates a separate, independent execution of the observable.
Subscribing twice results in two HTTP requests.
const req = http.get<Heroes>('/api/heroes');
// 0 requests made - .subscribe() not called.
req.subscribe();
// 1 request made.
req.subscribe();
// 2 requests made.
On a related note: The AsyncPipe subscribes (and unsubscribes) for you automatically.
Get method doesn't require to use the subscribe method but post method requires the subscribe. Get and post sample codes are below.
import { Component, OnInit } from '#angular/core'
import { Http, RequestOptions, Headers } from '#angular/http'
import 'rxjs/add/operator/map'
import 'rxjs/add/operator/catch'
import { Post } from './model/post'
import { Observable } from "rxjs/Observable";
#Component({
templateUrl: './test.html',
selector: 'test'
})
export class NgFor implements OnInit {
posts: Observable<Post[]>
model: Post = new Post()
/**
*
*/
constructor(private http: Http) {
}
ngOnInit(){
this.list()
}
private list(){
this.posts = this.http.get("http://localhost:3000/posts").map((val, i) => <Post[]>val.json())
}
public addNewRecord(){
let bodyString = JSON.stringify(this.model); // Stringify payload
let headers = new Headers({ 'Content-Type': 'application/json' }); // ... Set content type to JSON
let options = new RequestOptions({ headers: headers }); // Create a request option
this.http.post("http://localhost:3000/posts", this.model, options) // ...using post request
.map(res => res.json()) // ...and calling .json() on the response to return data
.catch((error:any) => Observable.throw(error.json().error || 'Server error')) //...errors if
.subscribe();
}
}

Injected service is undefined right in the constructor

For the note, I'm quite uninitiated to Angular (1 or 2 for that matter).
I'm trying to write a "super" layer of Http to avoid having to put the same headers everywhere.
import {Http, ConnectionBackend, RequestOptions, Response, Headers} from '#angular/http';
import {Observable} from 'rxjs';
import {LoadingService} from "../../services/loading.service";
export class HttpLoading extends Http {
constructor(backend: ConnectionBackend, defaultOptions: RequestOptions,
private _ls: LoadingService )
{
super(backend, defaultOptions);
}
getPostPutHeader() {
var authHeader = new Headers();
authHeader.append("Authorization", "Bearer "+ localStorage.getItem('token') );
authHeader.append('Content-Type', 'application/json');
return authHeader;
}
post(url: string, data:any):Observable<Response> {
this._ls.isLoading = true; // Exception here: this._ls is undefined
return super.post(url, data, { headers: this.getPostPutHeader() })
.map(res => {
this._ls.isLoading = false;
return res;
});
}
}
And a service to tell when a request is executing; it's injected in the above class HttpLoading.
import {Injectable} from '#angular/core';
#Injectable()
export class LoadingService {
isLoading: boolean = false;
}
I have a bunch of stuff in my bootstrap, including HttpLoading, LoadingService and ConnectionBackend (for this last one, I get an exception if it's not here).
bootstrap(AppComponent, [
ConnectionBackend,
HttpLoading,
APP_ROUTER_PROVIDERS,
HTTP_PROVIDERS,
LoadingService,
disableDeprecatedForms(),
provideForms()
])
The problem is that the first time I call HttpLoading's post method (in yet another service), I get an exception at this._ls.isLoading, because this._ls is undefined, and I can't figure why.
Please tell me if you need more information.
Edit
LoadingService is correctly injected in my AppComponent (main component).
//imports
//#Component
export class AppComponent {
requesting:boolean = false;
constructor(public authService: AuthService, private router: Router, private _ls: LoadingService) {
}
navigate(route:string) {
this._ls.isLoading = true;
this.router.navigate([route])
.then(() => this._ls.isLoading = false);
}
}
Potential solution
It seems that your public/private parameters must be placed first in the list. I'll let someone more skilled than me explain why, though...
export class HttpLoading extends Http {
constructor(private _ls: LoadingService, backend: ConnectionBackend, defaultOptions: RequestOptions) {
super(backend, defaultOptions);
}
I would configure your HttpLoading class this way in the providers when bootstrapping your application:
bootstrap(AppComponent, [
(...)
HTTP_PROVIDERS,
{
provide:Http,
useFactory: (backend: XHRBackend, defaultOptions: RequestOptions, loadingService: LoadingService) => {
return new HttpLoading(backend, defaultOptions, loadingService);
},
deps: [XHRBackend, RequestOptions, LoadingService]
}
]);
The reason for this is that you want to use your own class for the Http provider. You need to change the class behind the Http provider by your HttpLoading class. Be careful to define it after HTTP_PROVIDERS.
To be able to inject the instance of XHRBackend to your class, you need to use useFactory...
Ok , I know that may seem trivial, but try to create a variable and initialize it in the constructor
To extend #Thierry Templier's answer. I am using Angular v4, and my experience is that you need to provide ALL the dependencies that your extending constructor needs, AND in the right order - I guess it's a legacy way of doing it, from angular 1.x.
My example:
// This is my extended class (relevant part only)
#Injectable()
export class HttpService extends Http {
constructor(
backend: ConnectionBackend,
defaultOptions: RequestOptions,
private router: Router,
private loaderService: LoaderService,
private modalService: ModalService,
private localStorageService: LocalStorageService
)
{
super(backend, defaultOptions)
}
// This is the provider factory defined in app.module.ts:
export function httpClientFactory(
backend: XHRBackend,
defaultOptions: RequestOptions,
router: Router,
loaderService: LoaderService,
modalService: ModalService,
localStorageService: LocalStorageService
) : Http
{
return new HttpService(
backend,
defaultOptions,
router,
loaderService,
modalService,
localStorageService
);
}
This is the configuration (just left the relevant part) in app.module.ts:
providers: [
ModalService
LocalStorageService,
LoaderService,
{
provide: HttpService,
useFactory: httpClientFactory,
deps: [XHRBackend, RequestOptions, Router, LoaderService, ModalService, LocalStorageService]
}
Note: notice the order of declaring the deps in the config compared to the factory constructor .. it is the same

Angular2 http does not include the X-XSRF-TOKEN [duplicate]

In Angular1 the problem can be solved by configuring $http-provider. Like:
app.config(function($httpProvider) {
$httpProvider.defaults.xsrfCookieName = 'csrftoken';
$httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
});
What is a good practice to do the same in Angular2?
In Angular2 to work with http requests we need to use class Http. Of course that's not a good practice to add CSRF-line to each call of post-function.
I guess in Angular2 I should create own class that inherits Angular2's Http class and redefine the post-function. Is it the right approach or is there a more elegant method?
Now that Angular 2 is released the following seems to be the correct way of doing this, by using CookieXSRFStrategy.
I've configured my application to have a core module but you can do the same in your main application module instead:
import { ModuleWithProviders, NgModule, Optional, SkipSelf } from '#angular/core';
import { CommonModule } from '#angular/common';
import { HttpModule, XSRFStrategy, CookieXSRFStrategy } from '#angular/http';
#NgModule({
imports: [
CommonModule,
HttpModule
],
declarations: [ ],
exports: [ ],
providers: [
{
provide: XSRFStrategy,
useValue: new CookieXSRFStrategy('csrftoken', 'X-CSRFToken')
}
]
})
export class CoreModule {
},
Solution for Angular2 is not as easy as for angular1.
You need:
Pick out csrftoken cookie value.
Add this value to request headers with name X-CSRFToken.
I offer this snippet:
import {Injectable, provide} from 'angular2/core';
import {BaseRequestOptions, RequestOptions} from 'angular2/http'
#Injectable()
export class ExRequestOptions extends BaseRequestOptions {
constructor() {
super();
this.headers.append('X-CSRFToken', this.getCookie('csrftoken'));
}
getCookie(name) {
let value = "; " + document.cookie;
let parts = value.split("; " + name + "=");
if (parts.length == 2)
return parts.pop().split(";").shift();
}
}
export var app = bootstrap(EnviromentComponent, [
HTTP_PROVIDERS,
provide(RequestOptions, {useClass: ExRequestOptions})
]);
Victor K's answer is perfectly valid however as of angular 2.0.0-rc.2, a preferred approach would be to use CookieXSRFStrategy as below,
bootstrap(AngularApp, [
HTTP_PROVIDERS,
provide(XSRFStrategy, {useValue: new CookieXSRFStrategy('csrftoken', 'X-CSRFToken')})
]);
For later versions of angular you cannot call functions in decorators. You have to use a factory provider:
export function xsrfFactory() {
return new CookieXSRFStrategy('_csrf', 'XSRF-TOKEN');
}
And then use the factory:
providers: [
{
provide: XSRFStrategy,
useFactory : xsrfFactory
}],
Otherwise the compiler will tell you off.
What I have also seen is that ng build --watch will not report this error until you kick it off again.
I struggled with this for a few days. The advice in this article is good, but as of August, 2017 is deprecated (https://github.com/angular/angular/pull/18906). The angular2 recommended approach is simple, but has a caveat.
The recommend approach is to use HttpClientXsrfModule and to configure it to recognize django's default csrf protection. According to the django docs, django will send the cookie csrftoken and expect the client to return the header X-CSRFToken. In angular2, add the following to your app.module.ts
import { HttpClientModule, HttpClientXsrfModule } from '#angular/common/http';
#NgModule({
imports: [
HttpClientModule,
HttpClientXsrfModule.withOptions({
cookieName: 'csrftoken',
headerName: 'X-CSRFToken',
})
], ...
The caveat is that angular2's XSRF Protection only applies to mutating requests:
By default, an interceptor sends this cookie [header] on all mutating requests
(POST, etc.) to relative URLs but not on GET/HEAD requests or on
requests with an absolute URL.
If you need to support an API that performs mutation on GET/HEAD, you will need to create your own custom interceptor. You can find an example and a discussion of the issue here.
Victor K had the solution, I'll just add this comment here as to what I did:
I created the component "ExRequestOptions" as Victor K said, but I also added a method "appendHeaders" to that component:
appendHeaders(headername: string, headervalue: string) {
this.headers.append(headername, headervalue);
}
Then I had this in my main.ts:
import {bootstrap} from 'angular2/platform/browser'
import {AppComponent} from './app.component'
import {HTTP_PROVIDERS, RequestOptions} from 'angular2/http';
import 'rxjs/Rx';
import {ExRequestOptions} from './transportBoxes/exRequestOptions';
import {provide} from 'angular2/core';
bootstrap(AppComponent,[ HTTP_PROVIDERS,
provide(RequestOptions, {useClass: ExRequestOptions})]);
I'm not sure the bootstrapping had any effect, so i also did this where
I would post data:
let options = new ExRequestOptions();
options.appendHeaders('Content-Type', 'application/json');
return this.http.post('.....URL', JSON.stringify(registration),
options)
Currently, I solve anything with custom headers using a wrapper service around the Http Service. You can add whatever header manually and inject additional services for storing/retrieving values. This strategy also works for JWTs, for example. Have a look at the code below, I hope it helps.
import {Injectable} from '#angular/core';
import {Http, Headers, RequestOptions} from '#angular/http';
#Injectable()
export class HttpService {
constructor(private http: Http) {
}
private get xsrfToken() {
// todo: some logic to retrieve the cookie here. we're in a service, so you can inject anything you'd like for this
return '';
}
get(url) {
return this.http.get(url, this.getRequestOptions())
.map(result => result.json())
.catch(error => error.json());
}
post(url, payload) {
return this.http.post(url, payload, this.getRequestOptions())
.map(result => result.json())
.catch(error => error.json());
}
private getRequestOptions() {
const headers = new Headers({'Content-Type': 'application/json', 'X-XSRF-TOKEN': this.xsrfToken});
return new RequestOptions({headers: headers});
}
}

Resources