This is my demo code:
products.service.ts
getProducts(){
this.headers = new Headers();
this.headers.append("Content-Type", 'application/json');
this.headers.append("Authorization", 'Bearer ' + localStorage.getItem('id_token'));
return this.http.get('http://mydomain.azurewebsites.net/api/products',{headers:headers}).map(res => res.json().data);
}
products.component.ts
constructor(private productsService: ProductsService) { }
ngOnInit() {
this.productsService.getProducts().subscribe((res) => {
console.log(res);
});
}
Is it nescessary to import something in the ngModule decorator to consume a REST API or my code is wrong? I can get the desired data with Postman Chrome Extension but not with Angular 2 code.
I hope to have explained my problem well.
Update
These are the errors i get:
Sorry for making you waste your time.
This was the problem:
app.module.ts
providers: [
ProductsService,
// { provide: XHRBackend, useClass: InMemoryBackendService }, // in-mem server
// { provide: SEED_DATA, useClass: InMemoryDataService } // in-mem server data
]
After commenting the in-mem server and and the in-mem server data the problem dissapeared.
You're not setting the headers in the request. You declare the Headers object but you don't actually do anything with it.
You need to set them in the get function like this:
return this.http
.get('http://mydomain.azurewebsites.net/api/products', { headers: headers })
.map(res => res.json().data);
I'd suggest you use ngx-rest-ex: https://www.npmjs.com/package/ngx-rest-ex
npm i -S ngx-rest-ex
It's convenient, you just need to specify the corresponding decorator on top of the method and the return type, it will replace your method body. The return type can be either Promise or Observable depending on the HTTP METHOD annotation that you specified.
My demo code for your case:
import { Injectable, Injector } from '#angular/core';
import { BaseUrl, GET, RESTClient } from 'ngx-rest-ex';
import { Product } from './models';
#Injectable({
providedIn: 'root'
})
#BaseUrl('http://mydomain.azurewebsites.net/api/')
export class ApiService extends RESTClient {
constructor(injector: Injector) { super(injector); }
protected getDefaultHeaders() {
return {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + localStorage.getItem('id_token')
};
}
#GET('products')
getProducts(): Promise<Product[]> {
return;
}
}
Related
I am trying to make an interceptor to refresh the token, but it throws me this error and I don't know why
ERROR TypeError: You provided 'undefined' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.
token-interceptor.service.ts
import { Injectable } from '#angular/core';
import { AuthService } from './auth.service';
import { HttpClient, HttpErrorResponse, HttpHandler, HttpInterceptor, HttpRequest } from '#angular/common/http';
import { environment } from 'src/environments/environment';
import { catchError, map} from 'rxjs/operators';
import { throwError } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class TokenInterceptorService implements HttpInterceptor {
constructor(
private auth: AuthService,
private http: HttpClient
) { }
intercept(req: HttpRequest<any>, next: HttpHandler) {
return next.handle(req).pipe(
catchError((err: any) => {
if (err instanceof HttpErrorResponse) {
if (err.url.includes('signin') || err.url.includes('refreshToken')) {
return next.handle(req)
}
//if error is not about authorization
if (err.status !== 401) {
return next.handle(req)
}
this.renewToken(req).subscribe(request => {
return next.handle(request)
})
} else {
return throwError(err)
}
})
)
}
renewToken(req: HttpRequest<any>) {
return this.http.get(`${environment.API_URL}/refreshToken`, { withCredentials: true }).pipe(
map((res: any) => {
//update access token
this.auth.setToken(res.token)
return req.clone({
setHeaders: {
authorization: `Bearer ${res.token}`
}
})
})
)
}
}
Ignore this: It looks like your post is mostly code; please add some more details. It looks like your post is mostly code; please add some more details.
this piece of code is wrong:
this.renewToken(req).subscribe(request => {
return next.handle(request)
})
istead it should be:
return this.renewToken(req).pipe(switchMap(request => next.handle(request)));
you are just returning nothing in your variant, that is why it doesn't work.
also the whole logic of token interpceptor seems weird to me. I believe you should rethink about how you want it to work. for now as I see you sending request without token and in almost all cases you are sending it again unmodified, and the one that I fixed above will send it again with token. Wouldn't it be right to add token every time, and only send it 2nd time if token is outdated?
I'm trying to get users data by the link given in getsUserUrl.To do that
it requires a token(see the token variable) that I have added in the header. .Whenever I start the server I get an error like this.
Can't resolve all parameters for UserServiceService: (?).at syntaxError (compiler.js:1021)
Here is my code. I don't know what parameter am I missing in Header.
import { Injectable,Pipe } from '#angular/core';
import { HttpHeaders, HttpClient ,HttpErrorResponse,HttpClientModule}
from "#angular/common/http";
import { catchError } from 'rxjs/operators';
import { throwError } from 'rxjs';
import 'rxjs/add/operator/map';
import { Token } from '#angular/compiler';
#Injectable({
providedIn: 'root'
})
export class UserServiceService {
usersData: any[] = [];
user:string
getUserUrl="https://auth.openshift.io/api/search/users?q=rohit";
token=`eysdsfdp.whsofd23.fdiu`;
constructor(private httpclient:HttpClient) {
}
private handleError(err: HttpErrorResponse | any) {
console.error('An error occurred', err);
return throwError(err.message || err);
}
searchUsers()
{
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.token}`
})
};
var res=this.httpclient.get(this.getUserUrl,httpOptions);
return res.pipe(catchError(this.handleError));
}
}
Please let me know what am I doing wrong.
I'm attaching the code photo for your reference
As the person wanted my component code, I'm adding that too here
The Error messages Can't resolve all parameters for UserServiceService: (?) indicates that the angular DI can't find the HttpClient dependency, which is likely caused by you missing to import the HttpClientModule inside your AppModule.
To solve this add the HttpClientModule from #angular/common/http to the imports section of your AppModule.
Remove the following line from your code
import 'rxjs/add/operator/map';
And import the HttpClientModule in your module file. And declare into the imports array. LIke as following
import { HttpClientModule } from '#angular/common/http';
imports: [
HttpClientModule
And remove the importing of the HttpClientModule in your services file
You need to place authorization keyword in quotes as well
const httpOptions = new HttpHeaders({
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.token}`
});
Or you can define it this way also
let httpOptions = new HttpHeaders();
httpOptions = httpOptions .set('h1', 'v1').set('h2','v2');
I have some code which works for me in it's current format but I would like to change it so it uses angular httpClient instead.
Here is the current code:
const xhr = new XMLHttpRequest();
const xml = XMLData;
xhr.open('PUT', 'my url here', true);
xhr.setRequestHeader('Content-type', 'text/xml');
xhr.send(xml);
const response = xhr.responseText;
How can I do this with Angular 5's httpClient PUT?
This is what an PUT request with HttpClient and options could look like. You will need to transform your XMLData, whatever that may be, to a string. The SO question provided by #Vikas in his comment mentions a few libraries that are effective at parsing XML.
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders } from '#angular/common/http';
#Injectable()
export class AppService {
constructor(private http: HttpClient) { }
doPut() {
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'text/xml'
})
};
const xml: string = '<foo>1</foo>';
return this.http.put("/some/url", xml, httpOptions)
.subscribe(result => console.log(result));
}
}
Consolidated version if you prefer:
doPut(xml: string) {
return this.http.put("/some/url", xml, { headers: new HttpHeaders({ 'Content-Type': 'text/xml' }) })
.subscribe(result => console.log(result));
}
The HTTP request will NOT execute unless you subscribe() to the returned Observable produced by put() somewhere. I'd additionally review the documentation for error handling and additional options/functionality.
Hopefully that helps!
This is going to be a long question.
For windows authentication to work with angular I have wrapper for the http calls as shown below
import { Injectable } from '#angular/core';
import { Http, Response, RequestOptions, Headers, RequestOptionsArgs } from '#angular/http';
import { Config } from '../_helpers/config';
import { Observable } from 'rxjs/Observable';
#Injectable()
export class HTTPUtility {
public baseApiUrl: string;
constructor(private http: Http, private config: Config) {
this.baseApiUrl = this.config.getByKey('baseApiUrl') || '';
}
public getApiUrl(url) {
return this.baseApiUrl + url;
}
public get(url: string, options?: RequestOptions) {
if (!options) {
options = this.getDefaultHeaders();
}
return this.http.get(this.getApiUrl(url), options)
.catch(this.handleError);
}
private getDefaultHeaders() {
let headers = new Headers({ 'Content-Type': 'application/x-www-form-urlencoded' });
headers.append('Accept', 'application/json');
return new RequestOptions({ headers, withCredentials: true });
}
public handleError(response: Response) {
return Observable.throw(JSON.parse(response.json().Message) || 'Server error');
}
}
If you observe new RequestOptions({ headers, withCredentials: true }); is allowing browser to send credentials to server for windows authentication.
And it's working great for everything.
Now coming to the issue, I have sampleComponent in which i'm using ServerDataSource as shown below:
import { Component, OnInit, NgZone } from '#angular/core';
import { Http, RequestOptions } from '#angular/http';
import { Ng2SmartTableModule, ServerDataSource } from 'ng2-smart-table';
#Component({
selector: 'criteria',
templateUrl: './criteria.component.html',
styles: [require('./criteria.scss')],
})
export class SampleComponent implements OnInit {
Source: ServerDataSource;
settings: any;
constructor(
private http: Http) {
this.Source = new ServerDataSource(http, { endPoint: 'https://xxxxxx.org/yyy/GetCriterias'});
}
ngOnInit(): void {
// this.settings = {}// assigning the settings.
}
}
As you can see ServerDataSource is accepting Http instance and I have checked there documentation and haven't found any way to pass to RequestOptions. So the web api call made by ng2-smart-table fails with 401 status as credentials is not passed.
To resolve this issue I have made changes directly to ng2-smart-table source file to be specific 'server.data-source.js' and here is the change
ServerDataSource.prototype.createRequestOptions = function () {
var requestOptions = {withCredentials : true}; // this where I have added the withCredntials flag
requestOptions.params = new URLSearchParams();
requestOptions = this.addSortRequestOptions(requestOptions);
requestOptions = this.addFilterRequestOptions(requestOptions);
return this.addPagerRequestOptions(requestOptions);
};
With this change everything is working fine as of now.
But I could have issue in future, if we upgrade the package in that case I have to again make changes.
So if any one can help me to fix the issue in some other way please let me know.
Links: https://github.com/akveo/ng2-smart-table/blob/master/src/app/pages/examples/server/advanced-example-server.component.ts
Thanks.
Context :
Following several tutorials, I am testing authentication with Angular2 and JWT. I come with a component and a service :
app.component.ts
user.service.ts
App component (and template) contains the subscription to an observable that shows the user logged in status. The Observable item is kept in the user service, and changes (fine) when user logs in and out.
The authentication token is written in "localStorage" as "auth_token". It contains a validity value (time) that should force the user to login again after a time.
What I'd like to do is to CHECK the token validity on app init. First, I tried to do it from the user.service CONSTRUCTOR, then (fail), I tried to do it from ngOnInit in the app.component, then (fail again), I tried to do it on event call (click on a button) from the app component, but fails again!
Some shortened code :
//app.component.html
//...
<a md-button class="app-icon-button" aria-label="checklogin" (click)="checkLogin()">
<md-icon svgIcon="check"></md-icon>
</a>
//...
//app.component.ts
//...
checkLogin(){
console.log('CHECK LOGIN FUNCTION');
let token = localStorage.getItem('auth_token');
if(token){
console.log('TOKEN FOUND');
this.userService.checkToken(token);
}else{
console.log('NO TOKEN FOUND');
}
}
//...
//user.service.ts
//...
checkToken(token){
console.log('CHECK TOKEN FUNCTION');
console.log('TOKEN : '+token);
let headers = new Headers();
headers.append('Content-Type','application/json');
return this.http
.post(
'/url/script.php',
JSON.stringify(token),
{ headers }
)
.map(res => res.json())
.map((res) => {
console.log('SCRIPT RESULT : ');
if(res.valid){
console.log('TOKEN IS VALID');
return true;
}else{
console.log('TOKEN NOT VALID');
return false;
}
});
}
//...
I did skip the observable part, and subscription.
Problem :
The problem actually is that the app NEVER CALLS the script!
When I do click on the "checkLogin" button (when token exists),
console shows 'CHECK LOGIN FUNCTION',
console shows 'TOKEN FOUND',
console shows 'CHECK TOKEN FUNCTION',
console shows 'TOKEN : '****************************** (token),
But it never shows 'SCRIPT RESULT',
and when using firebug to check if the http call is done, there is NO CALL to the script.php. Looks like the this.http part is just ignored...
Thanks for reading/help
Service starts working when subscription used only when consumer subscribe to output result, using .subscribe method.
You need: this.userService.checkToken(token).subscribe()
Your checkToken() method is returning an Observable that you need to subsrcibe to. An observable will never to execute unless it's subscribed to.
checkLogin(){
console.log('CHECK LOGIN FUNCTION');
let token = localStorage.getItem('auth_token');
if(token){
console.log('TOKEN FOUND');
this.userService.checkToken(token).subscribe(result => {
console.log(result);
}),
error => {
console.log(error);
});
} else {
console.log('NO TOKEN FOUND');
}
}
Ajax call's which use Observables will work only if you have an subscriber.
So you need to subscribe to that Observable. It is an Angular 2 feature. When you don't subscribe the Observable, it will never make that call.
And also you don't need to return anything from the subscriber, because you actually can't return anything.
this.userService.checkToken(token).subscribe((res) => {
console.log('SCRIPT RESULT : ');
if(res.valid) {
console.log('TOKEN IS VALID');
} else {
console.log('TOKEN NOT VALID');
}
});
checkToken(token){
console.log('CHECK TOKEN FUNCTION');
console.log('TOKEN : '+token);
let headers = new Headers();
headers.append('Content-Type','application/json');
return this.http
.post(
'/url/script.php',
JSON.stringify(token),
{ headers }
)
.map(res => res.json());
}
Have You tried using Postman and try to call function you need?
Also, why do You need to validate a token if angular2-jwt can do this for You?
You can do just like this:
install angular2-jwt with npm.
Include in app.module.ts:
import { AUTH_PROVIDERS } from 'angular2-jwt';
add to providers:
providers: [
AUTH_PROVIDERS,
],
and for example auth.service.ts looks like this:
import { Injectable, Inject } from '#angular/core';
import { Http, Response, Headers, RequestOptions, RequestMethod } from '#angular/http';
import { Router } from '#angular/router';
import { Observable } from 'rxjs/Observable';
import { Configuration } from '../../app.config';
import { RegisterViewModel } from '../../model/viewModel/registerViewModel';
import { LoginViewModel } from '../../model/viewModel/loginViewModel';
import { tokenNotExpired, AuthHttp } from 'angular2-jwt';
#Injectable()
export class AuthService {
private actionUrl: string;
constructor(private _http: Http, private _config: Configuration, private _router: Router, private _authHttp: AuthHttp){
this.actionUrl = _config.apiUrl;
}
register(user: RegisterViewModel){
let headers = new Headers({ 'Content-Type': 'application/json' });
//Admin in this system can only register users. that is why auth
return this._authHttp.post(this.actionUrl + '/Account/Register', JSON.stringify(user), { headers : headers })
.do(response => {
console.log(response.toString());
});
}
login(user: LoginViewModel) {
let headers = new Headers({ 'Content-Type': 'application/x-www-form-urlencoded' });
return this._http.post('http://localhost:56181/api/token', "username=" + user.userName + "&password=" + user.password + "&userId=" + user.userId, { headers : headers })
.do(response => {
if(response){
let authResult = response.json();
this.setUser(authResult);
this._router.navigate(['']);
}
});
}
public isAuthenticated(): boolean {
//angular2-jwt has this function to check if token is valid
return tokenNotExpired();
}
private setUser(authResult: any): void {
localStorage.setItem('id_token', authResult.id_token);
}
public logout(): void {
localStorage.removeItem('id_token');
this._router.navigate(['']);
}
}
also remember that angular2-jwt has default name for token in localstorage as id_token or else you will have to use angular2-jwt help class to specify other token name.
You can check if it is working by simply doing this:
in app.component.ts:
export class AppComponent {
constructor(private _auth: AuthService){
}
}
and in app.component.html:
<li>
<a class="nav-link" [routerLink]="['/login']" *ngIf="!_auth.isAuthenticated()">Login</a>
</li>
<li>
<a class="nav-link" (click)="_auth.logout()" *ngIf="_auth.isAuthenticated()">Log Out</a>
</li>
also You can read a little bit documentation about it in:
https://auth0.com/blog/introducing-angular2-jwt-a-library-for-angular2-authentication/