I would like to send Time zone Offset in request header. But I have not find any way to do it.
My code is given below:
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '#angular/common/http';
import { Observable } from 'rxjs';
import { Injectable } from '#angular/core';
import { SessionProvider } from './session.provider';
#Injectable({ providedIn: 'root' })
export class TokenInterceptor implements HttpInterceptor {
constructor(private provider: SessionProvider) {}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const update = addHeaders(req, this.provider.session);
const request = req.clone(update);
return next.handle(request);
}
}
function addHeaders(req: HttpRequest<any>, session: any): any {
let headers = req.headers;
if (session?.stationId) {
headers = headers.set('wid', session.stationId);
}
if (session?.accessToken) {
headers = headers.set('Authorization', 'Bearer ' + session.accessToken);
}
return { headers };
}
I want to add Time zone Offset in addHeaders function. If someone knows how it can be done them please share your thoughts
It can be done as follow:
function addHeaders(req: HttpRequest<any>, session: any): any {
let headers = req.headers;
if (session?.stationId) {
headers = headers.set('wid', session.stationId);
}
if (session?.accessToken) {
headers = headers.set('Authorization', 'Bearer ' + session.accessToken);
}
headers = headers.set('tz-offset', getTimeZoneOffset());
return { headers };
}
function getTimeZoneOffset(): string {
const tzOffset = new Date().toTimeString().match(/([\+-]\d{4})/)[0];
return [tzOffset.slice(0, 3), ':', tzOffset.slice(3)].join('');
}
Related
I just developed sign in authentication for an Angular app and I went to test it. Sign up works just fine. Then I sign out and attempt to sign in and the header does not update as expected, instead I get this error in console:
Uncaught DOMException: Blocked a frame with origin
"chrome-extension://hdokiejnpimakedhajhdlcegeplioahd" from accessing a
cross-origin frame.
at e [as constructor] (chrome-extension://hdokiejnpimakedhajhdlcegeplioahd/lpfulllib.js:1:1441712)
at new e (chrome-extension://hdokiejnpimakedhajhdlcegeplioahd/lpfulllib.js:1:1444920)
at chrome-extension://hdokiejnpimakedhajhdlcegeplioahd/lpfulllib.js:1:1461728
But I am not authenticated because I get:
{authenticated: false, username: null}
authenticated: false
username: null
Even though the GET request itself went through successfully, but there is a problem there, because it's not supposed to be a GET but a POST request. Why does it think it's a GET request?
My signin() method inside my auth service clearly shows it's a post request:
signin(credentials: SigninCredentials) {
return this.http.post(this.rootUrl + "/auth/signin", credentials).pipe(
tap(() => {
this.signedin$.next(true);
})
);
}
Here is my auth http interceptor code:
import { Injectable } from "#angular/core";
import {
HttpEvent,
HttpInterceptor,
HttpHandler,
HttpRequest,
HttpEventType,
} from "#angular/common/http";
import { Observable } from "rxjs";
#Injectable()
export class AuthHttpInterceptor implements HttpInterceptor {
intercept(
req: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
// Modify or log the outgoing request
const modifiedReq = req.clone({
withCredentials: true,
});
return next.handle(modifiedReq);
}
}
Now I do not think the issue is in my AuthHttpInterceptor, I believe the problem is in AuthService:
import { Injectable } from "#angular/core";
import { HttpClient } from "#angular/common/http";
import { BehaviorSubject } from "rxjs";
import { tap } from "rxjs/operators";
interface UsernameAvailableResponse {
available: boolean;
}
interface SignupCredentials {
username: string;
password: string;
passwordConfirmation: string;
}
interface SignupResponse {
username: string;
}
interface SignedinResponse {
authenticated: boolean;
username: string;
}
interface SigninCredentials {
username: string;
password: string;
}
#Injectable({
providedIn: "root",
})
export class AuthService {
rootUrl = "https://api.my-email.com";
signedin$ = new BehaviorSubject(false);
constructor(private http: HttpClient) {}
usernameAvailable(username: string) {
return this.http.post<UsernameAvailableResponse>(
this.rootUrl + "/auth/username",
{
username,
}
);
}
signup(credentials: SignupCredentials) {
return this.http
.post<SignupResponse>(this.rootUrl + "/auth/signup", credentials)
.pipe(
tap(() => {
this.signedin$.next(true);
})
);
}
checkAuth() {
return this.http
.get<SignedinResponse>(this.rootUrl + "/auth/signedin")
.pipe(
tap(({ authenticated }) => {
this.signedin$.next(authenticated);
})
);
}
signout() {
return this.http.post(this.rootUrl + "/auth/signout", {}).pipe(
tap(() => {
this.signedin$.next(false);
})
);
}
signin(credentials: SigninCredentials) {
return this.http.post(this.rootUrl + "/auth/signin", credentials).pipe(
tap(() => {
this.signedin$.next(true);
})
);
}
}
I see you have no HttpHeaders. While I do not see your backend configuration, I suspect the mis configuration causes your exception.
You can update your Angular interpector to something like this:
import {
HttpEvent,
HttpHandler,
HttpHeaders,
HttpInterceptor,
HttpRequest,
} from '#angular/common/http'
import { Injectable } from '#angular/core'
import { Observable } from 'rxjs'
#Injectable()
export class AuthHttpInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// const token = localStorage.getItem('access_token')
// const userToken = localStorage.getItem('id_token')
if (token) {
const authRequest = req.clone({
setHeaders: {
'Content-Type': 'application/json',
// Authorization: `Bearer ${token}`,
// userID: `${userToken}`,
},
})
return next.handle(authRequest)
} else {
const headers = new HttpHeaders({
'Content-Type': 'application/json',
})
const cloned = req.clone({
headers,
})
return next.handle(cloned)
}
}
}
Also make sure that your backend is aligned and headers such as Access-Control-Allow-Origin configured to true
That is caused by LastPass extension. Deactivate it and the error will disappear.
I have a little issue from an Angular app to get a code from my own server.
I´ve built up a little Spotify App for learning more about Angular 10 and have a little backend that I only use for get the Bearer code to call the Spotify API, but the fact is that in my Angular front I can´t save the code.
Tis is my service call code to the back:
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { map } from 'rxjs/operators';
import { environment } from '../../environments/environment';
#Injectable({
providedIn: 'root'
})
export class SpotifyService {
token: any;
constructor(private http: HttpClient) {
console.log('Spotify service ready');
this.getAccesToken().subscribe(data => this.token = data['access_token']);
}
getAccesToken(){
return this.http.get(environment.server + `${environment.client_id}/${environment.client_secret}`)
.pipe(
map(res => res)
);
}
getQuery(query: any){
const headers = new HttpHeaders({
'Authorization': `Bearer ${this.token}`
});
const url = `https://api.spotify.com/v1/${query}`;
return this.http.get(url, {headers});
}
I´ve checked the recibed data and I get the request perfectly, but can´t save the data of the suscriber into a variable.
Thanks in advance!
Assuming your component code looks like below, you can make some adjustments and try it.
export class SomeComponent implements OnInit {
spotifyData;
user;
token;
constructor(private spotifyService: SpotifyService, private http: HttpClient) { }
ngOnInit() {
this.user = JSON.parse(localStorage.getItem('user'));
console.log(this.user);
this.token = this.user.token;
this.getSpotifyData();
}
getSpotifyData() {
this.spotifyService.getQuery(this.user, { headers: new HttpHeaders().set('Authorization', 'Bearer ' + this.token) }).subscribe(res => {
console.log(res);
if (res) {
spotifyData = res;
} else {
spotifyData = []
}
});
}
I'm building the front-end of an application in Angular 8. This application uses an OAuth 2 implementation to manage authentication (password grant) so any HTTP request (with the exception of ones to the token endpoint) needs to have on its header a valid access_token.
To provide said token I've made an Angular interceptor that retrieve the token from another service and then attach it to the intercepted HTTP request. The token retrieval method doesn't give directly the token but an observable which eventually resolves to a valid token, I made this choice because the access token may not be instantly available, if the token is expired the application needs to refresh it with an HTTP call and then the refreshed token can be passed to the HTTP interceptor.
The problem which I encounter is that despite my many attempts the interceptor doesn't wait for the token to be retrieved so at the end the interceptor is skipped and the HTTP request is made without any token attached.
This is the code of my interceptor, retrieveValidToken is the Observable which returns the token.
import { Injectable } from '#angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '#angular/common/http';
import { FacadeService } from './facade.service';
import { Observable } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class HttpInterceptorService implements HttpInterceptor {
constructor(private facadeService: FacadeService) { }
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (req.url.includes('localhost:3000') && !req.url.endsWith('token')) {
this.facadeService.retrieveValidToken()
.subscribe(
(res) => {
const clone = req.clone({ setHeaders: { Authorization: `Bearer ${res}` } });
return next.handle(clone);
},
(err) => {
const clone = req.clone({ setHeaders: { Authorization: `Bearer ` } });
return next.handle(clone);
}
);
} else {
return next.handle(req);
}
}
}
Observables are asynchronous. The code outside the subscribe method will not wait for the code inside.
You should return observable by itself, not only result inside its subscription:
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (req.url.includes('localhost:3000') && !req.url.endsWith('token')) {
return this.facadeService.retrieveValidToken()
.subscribe(
res => {
const clone = req.clone({ setHeaders: { Authorization: `Bearer ${res}` } });
return next.handle(clone);
}
);
} else {
return next.handle(req);
}
}
Something similar:
How use async service into angular httpClient interceptor
The problem is that 'intercept' method should return observable immediately, so instead of subscribing to 'this.facadeService.retrieveValidToken()' use the following code:
return this.facadeService.retrieveValidToken().pipe(
mergeMap(token =>
next.handle(req.clone({ setHeaders: { Authorization: 'Bearer ${token}' }))
)
)
I dont know how to make an API call to such a method:
[HttpGet]
[ActionName("GetSupport")]
public HttpResponseMessage GetSupport(int projectid)
Because it is GET but still got a parameter to pass, how to do this?
Would it be something like this?
let headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('projectid', this.id);
this.http.get('http://localhost:63203/api/CallCenter/GetSupport', { headers: headers })
Having something like this:
let headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('projectid', this.id);
let params = new URLSearchParams();
params.append("someParamKey", this.someParamValue)
this.http.get('http://localhost:63203/api/CallCenter/GetSupport', { headers: headers, search: params })
Of course, appending every param you need to params. It gives you a lot more flexibility than just using a URL string to pass params to the request.
EDIT(28.09.2017): As Al-Mothafar stated in a comment, search is deprecated as of Angular 4, so you should use params
EDIT(02.11.2017): If you are using the new HttpClient there are now HttpParams, which look and are used like this:
let params = new HttpParams().set("paramName",paramValue).set("paramName2", paramValue2); //Create new HttpParams
And then add the params to the request in, basically, the same way:
this.http.get(url, {headers: headers, params: params});
//No need to use .map(res => res.json()) anymore
More in the docs for HttpParams and HttpClient
For Angular 9+ You can add headers and params directly without the key-value notion:
const headers = new HttpHeaders().append('header', 'value');
const params = new HttpParams().append('param', 'value');
this.http.get('url', {headers, params});
Above solutions not helped me, but I resolve same issue by next way
private setHeaders(params) {
const accessToken = this.localStorageService.get('token');
const reqData = {
headers: {
Authorization: `Bearer ${accessToken}`
},
};
if(params) {
let reqParams = {};
Object.keys(params).map(k =>{
reqParams[k] = params[k];
});
reqData['params'] = reqParams;
}
return reqData;
}
and send request
this.http.get(this.getUrl(url), this.setHeaders(params))
Its work with NestJS backend, with other I don't know.
Just offering up a helpful piece of code for anyone interested in the HttpParams direction mentioned in the answer from 2017.
Here is my go-to api.service which kind of "automates" params into that HttpParams for requests.
import { HttpClient, HttpParams } from '#angular/common/http';
import { Injectable } from '#angular/core';
import { Params } from '#angular/router';
import { Observable } from 'rxjs';
import { environment } from '#env';
#Injectable({
providedIn: 'root'
})
export class ApiService {
constructor(private http: HttpClient) { }
public get<T>(path: string, routerParams?: Params): Observable<T> {
let queryParams: Params = {};
if (routerParams) {
queryParams = this.setParameter(routerParams);
}
console.log(queryParams);
return this.http.get<T>(this.path(path), { params: queryParams });
}
public put<T>(path: string, body: Record<string, any> = {}): Observable<any> {
return this.http.put(this.path(path), body);
}
public post<T>(path: string, body: Record<string, any> = {}): Observable<any> {
return this.http.post(this.path(path), body);
}
public delete<T>(path: string): Observable<any> {
return this.http.delete(this.path(path));
}
private setParameter(routerParams: Params): HttpParams {
let queryParams = new HttpParams();
for (const key in routerParams) {
if (routerParams.hasOwnProperty(key)) {
queryParams = queryParams.set(key, routerParams[key]);
}
}
return queryParams;
}
private path(path: string): string {
return `${environment.api_url}${path}`;
}
}
An easy and usable way to solve this problem
getGetSuppor(filter): Observale<any[]> {
return this.https.get<any[]>('/api/callCenter/getSupport' + '?' + this.toQueryString(filter));
}
private toQueryString(query): string {
var parts = [];
for (var property in query) {
var value = query[propery];
if (value != null && value != undefined)
parts.push(encodeURIComponent(propery) + '=' + encodeURIComponent(value))
}
return parts.join('&');
}
Can anyone help what I am doing incorrect, anything missing?
I am getting undefined for --'this.ack.length'
this._activeChannelService.requestChannelChange(this.selectedchannel.channelName)
.subscribe(
ack => {
this.ack= ack;
console.log(" test: ", this.ack.length);
},
err => {
console.log(err);
});enter code here
ack is of time
ack:Iack[];
Iack has two field of type string. result and message
I need to iterate through array of Iack[] to get the result and message
if message=success then call the another service
service
requestChannelChange (name: string): Observable<Iack[]> {
alert('in servicerequestChannelChange');
let headers = new Headers({'Content-Type': 'application/json'});
let options = new RequestOptions({headers: headers});
let postchannelname = "channelName=" + name;
let requestt = new IRequest(name);
console.log(JSON.stringify(requestt));
return this._http.post(this._activateChangeUrl, JSON.stringify(requestt),{ headers: headers })
//.map(this.extractData)
.map((res:Response) => res.json() as Iack[])
.do(data => console.log("All: " + JSON.stringify(data)))
.catch(this.handleError);
}
You can use observable in your TS service:
import { Injectable } from '#angular/core';
import { IPost } from './IPost';
import { Http, Response, RequestOptions, Headers } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/catch';
#Injectable()
export class PostServices {
private _webApiBaseUrl = "http://localhost:62806/v1/Posts"
private _http : Http;
constructor(http : Http){
this._http = http;
}
getAll(): Observable<IPost[]> {
return this._http.get(this._webApiBaseUrl + '/all', this.getHeaders())
.map((response: Response) => response.json())
.do(data => console.log(`All Data: \n ${ JSON.stringify(data) }`))
.catch(this.handleError);
}
private handleError(error: Response){
console.error(error);
return Observable.throw(error.json().error || 'Server Error');
}
private getHeaders()
{
let headers = new Headers();
headers.append("Authorization", "");
headers.append("Content-Type", "application/json");
return new RequestOptions({ headers: headers });
}
}
Usage in your TS class:
import { Component, OnInit } from '#angular/core';
import { IPost } from './IPost';
import { PostServices } from './posts.service';
#Component({
selector: 'app-posts',
templateUrl: './posts.component.html',
styleUrls: ['./posts.component.css']
})
export class PostsComponent implements OnInit {
posts: IPost[];
errorMessage: string;
private _postService: PostServices;
constructor(postService: PostServices) {
this._postService = postService;
}
ngOnInit() {
this._postService.getAll()
.subscribe(
data => {this.posts = data; console.log("data.length: " + data.length)}, // here
error => this.errorMessage = <any>error
);
}
}
enter code here is executed before this.ack= ack; is executed
This is a function
ack => {
this.ack= ack;
console.log(" test: ", this.ack.length);
}
that you pass to subscribe(...) and the observable calls it when the data arrives which can take a looong time when it's a call to a server.
enter code here is executed immediately.
You'll have to check for success within the service subscription. An observable is an asynchronous call, so any calls you want to make regarding the data in that async call must be made within it to remain a safe call.
So, make your seconds service call, within the subscription.