I've encountered problems trying to resolve an http.post request before doing some other code.
Here's the place:
getRefresh(token){
debugger;
console.log("sono dentro get refresh");
console.log(token);
let params = new URLSearchParams();
params.append('grant_type','refresh_token');
params.append('refresh_token',token);
params.append('client_id','web_app');
let headers = new Headers({'Content-type': 'application/x-www-form-urlencoded; charset=utf-8','Authorization': 'Basic '+btoa("web_app:")});
let options = new RequestOptions({headers: headers});
this._http.post('http://localhost:9997/oauth/token',params.toString(),options)
.map(res => res.json())
.subscribe(
data => this.saveToken(data),
err => alert('Invalid Credentials')
);
}
Following the control flow via Browser Console, I've noticed that saveToken method is not executed asap.
The fact is that I need that method for registering some cookies, reused here:
if (!Cookie.get("access_token")) {
this.getRefresh(Cookie.get("refresh_token"));
cookie = Cookie.get("access_token");
} else {
cookie = Cookie.get("access_token");
}
var headers = new Headers({
'Content-type': 'application/x-www-form-urlencoded; charset=utf-8',
'Authorization': 'Bearer ' + cookie
});
var options = new RequestOptions({headers: headers});
return this._http.get(resourceUrl, options)
.map((res: Response) => res.text())
.catch((error: any) => Observable.throw(error().json().error || 'Server error'));
How can I resolve the async problem of observable?
Change your getRefresh(token) method to return the observable and use that in your calling code:
function getRefresh(token) {
debugger;
console.log("sono dentro get refresh");
console.log(token);
let params = new URLSearchParams();
params.append('grant_type', 'refresh_token');
params.append('refresh_token', token);
params.append('client_id', 'web_app');
let headers = new Headers({ 'Content-type': 'application/x-www-form-urlencoded; charset=utf-8', 'Authorization': 'Basic ' + btoa("web_app:") });
let options = new RequestOptions({ headers: headers });
return this._http.post('http://localhost:9997/oauth/token', params.toString(), options)
.map(res => res.json())
.flatMap(
data => this.saveToken(data),
err => alert('Invalid Credentials')
);
}
function getResource() {
var cookie = Cookie.get("access_token");
var headers = new Headers({
'Content-type': 'application/x-www-form-urlencoded; charset=utf-8',
'Authorization': 'Bearer ' + cookie
});
var options = new RequestOptions({ headers: headers });
return this._http.get(resourceUrl, options)
.map((res: Response) => res.text())
.catch((error: any) => Observable.throw(error().json().error || 'Server error'));
}
function callingCodeFunction() {
if (!Cookie.get("access_token")) {
this.getRefresh(Cookie.get("refresh_token"))
.flatMap((result) => {
getResource();
});
} else {
getResource()
}
}
*I've not run this code - so there may be errors, but it should serve to demonstrate the principle.
Related
I'm trying to fetch data from the server, and I want to try to resolve the response body as json, if failed return it as plain text
fetch(`/devapi/${url}`, {
method: 'GET',
headers: new Headers({
Authorization: `Bearer ${localStorage.getItem("token")}`,
}),
})
.then((res) =>
res
.json()
.then((res) => res?.body?.data || res?.body || res)
.catch((err) => res.text())
)
.then((val) => console.log(val));
when the response is NOT a valid json, ten res.text() is called, but it seems that calling .text() after .json causes an error
Failed to execute 'text' on 'Response': body stream already read
You could clone the response, and then use it in error handler:
Try this:
fetch(`/devapi/${url}`, {
method: 'GET',
headers: new Headers({
Authorization: `Bearer ${localStorage.getItem("token")}`,
}),
})
.then((res) => {
const clone = res.clone();
return res
.json()
.then((res) => res?.body?.data || res?.body || res)
.catch((err) => clone.text())
})
.then((val) => console.log(val));
or use XHR and process response in a try/catch:
const xhr = new XMLHttpRequest();
xhr.open('POST', `/devapi/${url}`, true);
xhr.setRequestHeader('Authorization', `Bearer ${localStorage.getItem("token")}`);
xhr.onload = function() {
let res;
try {
res = JSON.parse(xhr.response);
} catch (err) {
res = xhr.responseText;
}
console.log(res);
};
xhr.send();
I can get the data by request from this code.
let request = require('request');
let options = {
'method': 'POST',
'url': 'https://example.com/api',
'headers': {
'Content-Type': 'application/x-www-form-urlencoded'
},
form: {
'client_id': '12345678',
'client_secret': 'abcdefg'
}
};
request(options, function (error, response) {
if (error) throw new Error(error);
console.log(response.body);
});
However, I got '404.00.001' when I use "fetch" to access the same API. Is there any thing wrong in this code?
const fetch = require("node-fetch");
const url = "https://example.com/api";
var headers = {
'Content-Type': 'application/x-www-form-urlencoded'
};
var data = JSON.stringify( {
'client_id': '12345678',
'client_secret': 'abcdefg'
});
fetch(url, {method: 'POST', headers: headers, body: data})
.then(response => response.json())
.then((resp) => {
console.log(resp);
})
.catch(error => console.error('Unable to fetch token.', error));
'Content-Type': 'application/x-www-form-urlencoded' does not say JSON so why do you have var data = JSON.stringify?
The documentation tells you how to encode data as form parameters.
const { URLSearchParams } = require('url');
const params = new URLSearchParams();
params.append('a', 1);
I want to set token in authorization header but the token is set null. I tried to get the token from chrome storage but token is not set though i get the token from the storage when i console the result['user_token'] inside the callback.
here is the code
var token = null; // i need this token in fetchTopics as well
function fetchCurrentUser() {
const apiUrl = `api2.navihq.com/get_current_user`;
chrome.storage.sync.get(['user_token'], function(result) {
token = result['user_token'];
});
console.log('token', token); // getting null
const headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('Authorization', `Token: ${token}`)
fetch(apiUrl, {
method: 'GET',
headers
})
.then(function(response) {
console.log('response', response);
return response.json()
})
.then(function(data) {
console.log('data', data);
return JSON.parse(atob(data.user))
})
}
$(window).bind('load', function() {
document.addEventListener('click', init);
fetchCurrentUser();
fetchTopics();
});
How do i now set the token in authorization header?
The sync in chrome.storage.sync.get doesn't mean it's synchronous
the fact it takes a callback shows it is Asynchronous - not that taking a callback guarantees something is asynchronous of course, but in this case it's clear that it is
So put the fetch inside the callback
function fetchCurrentUser() {
const apiUrl = `api2.navihq.com/get_current_user`;
chrome.storage.sync.get(['user_token'], function(result) {
var token = result['user_token'];
console.log('token', token); // getting null
const headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('Authorization', `Token: ${token}`)
fetch(apiUrl, {
method: 'GET',
headers
}).then(function(response) {
console.log('response', response);
return response.json()
}).then(function(data) {
console.log('data', data);
return JSON.parse(atob(data.user))
})
});
}
Alternatively you can "promisify" the chrome.storage.sync.get function
function fetchCurrentUser() {
const chromeStorageGetPromise = key =>
new Promise(resolve => chrome.storage.sync.get(key, resolve));
const apiUrl = `api2.navihq.com/get_current_user`;
chromeStorageGetPromise(['user_token'])
.then(({user_token: token}) => {
console.log('token', token);
const headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('Authorization', `Token: ${token}`);
return fetch(apiUrl, {
method: 'GET',
headers
});
}).then(response => {
console.log('response', response);
return response.json();
}).then(data => {
console.log('data', data);
return JSON.parse(atob(data.user));
})
}
I building web app which is using OAuth authorization. To get access to data i need ask endpoint for token and place it in Authorization header.
I have made interceptor in Angular which put right header for API call and that part works just fine.
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if(!(req.headers.get("Content-Type") === "application/x-www-form-urlencoded")) {
const authReq = req.clone({
headers: req.headers.set('Authorazation', 'Bearer ' + this.callToken()
.then(resp => resp)
.catch(err => console.log(err)))
});
return next.handle(authReq)
}else {
return next.handle(req);
}
}
but i have this method callToken() where I used promis to call for token.
Now this method return promise I cant put it in header in right type its always appear like
callToken(): Promise<any> {
let url = 'url';
let body = "body";
let promise = fetch(url, {
body: body,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
method: 'POST',
});
return promise
.then(resp => resp.json())
.then(json => json.access_token);
}
funny thing is that when I say resp => console.log(resp.acces_token) token just pretty printed.
I try use callbacks as well
callForToken(): string {
let url = 'url';
let body = 'body';
let option = {
headers: new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded')
};
let token;
this.http.post<Token>(url, body, option).subscribe(
res => {
token = res.access_token;
}
);
return token;
}
but because of async call I can't reach token data outside of method
I still try to found solution on this.
Now I end up with
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (!(req.headers.get('Content-Type') === 'application/x-www-form-urlencoded')) {
return Observable.fromPromise(this.handleAccess(req, next));
}
}
private async handleAccess(req: HttpRequest<any>, next: HttpHandler): Promise<HttpEvent<any>> {
const token = await this.callToken();
console.log(token);
let changeRequest;
if (token) {
changeRequest = req.clone({
headers: req.headers.set('Authorization', 'Bearer ' + token)
});
}
return next.handle(changeRequest).toPromise();
}
callToken(): Promise<Token> {
let url = 'url';
let body = 'body';
return fetch(url, {
body: body,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
method: 'POST',
}).then(resp => resp.json())
.then(json => json.acces_token)
.catch(err => console.error(err));
}
but still token is undefined
EDIT: actually that's work. I messed up with URL and body
I am trying to login on a local webservice using xml:
Here is the code:
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'text/xml',
'Accept': 'text/xml',
'Response-Type': 'text'
})
};
login() {
const postedData = `
<authenticationDetail>
<userName>myusername</userName>
<password>mypassword</password>
</authenticationDetail>`;
return this.http.post('http://localhost/login.ws', postedData, httpOptions)
.subscribe(
result => {
console.log('This result is' + result);
},
error => {
console.log('There was an error: ', error);
}
);
}
The error I'm getting is:
Http failure during parsing for 'http://localhost/login.ws'
What is the problem here? How can I fix this?
Have your tried
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'text/xml',
'Accept': 'text/xml',
'Response-Type': 'text'
}),
responseType: 'text'
};
The responseType has to be set to text not only in the headers but also in httpOptions. Otherwise Angular will parse the response of the call as JSON.
Try and change your request to this:
login(){
const headers = new HttpHeaders();
headers = headers.append('Content-Type': 'text/xml');
headers = headers.append('Accept', 'text/xml');
let body = '<authenticationDetail>'
'<username>Username</username>'
'<password>Password</password>'
'</authenticationDetail>';
return this.http.post('localhost/login.ws',body , { headers: headers, responseType: 'text' })
.subscribe(
res => {
parseString(res.text(), (err, result) => {
if (err) {
return console.log('invalid XML');
}
console.log(result);
})
},error => {
console.log('There was an error: ', error);
}
);
}
Try
const postedData = '<authenticationDetail>' +
'<userName>myusername</userName>' +
'<password>mypassword</password>' +
'</authenticationDetail>';
Template string adds whitespace char "new line", try the classic approach with regular strings.