How to set multiple headers data with XMLHttpRequest in async mode? - javascript

My api call requires me to pass the api key in the headers, but I'm getting error back from the api service {"error":"2424452","message":"Invalid Api Key"}
I know my api key is valid as I can make the same api call in Python just fine, example:
req = requests.Session()
req.headers.update({'x-api-key': 'my-api-key', 'X-Product': 'my-product-name'})
req.get(url)
But in javscript, the same call errors out. I believe I'm not setting the headers correctly or something?
var req = new XMLHttpRequest();
req.onreadystatechange=handleStateChange;
req.open("GET", "url", true);
req.setRequestHeader("Host", "api.domain.com", "x-api-key", "my-api-key", "X-Product", "my-product-name");
req.send();
This XMLHttpRequest is not a browser call, rather in an application that support XMLHttpRequest.

setRequestHeader sets one header and takes two arguments (the name and the value).
If you want to set multiple headers, then call setRequestHeader multiple times. Don't add extra arguments to the first call.

In case you don't want to set multiple headers explicitly you can use
function setHeaders(headers){
for(let key in headers){
xhr.setRequestHeader(key, headers[key])
}
}
setHeaders({"Host":"api.domain.com","X-Requested-With":"XMLHttpRequest","contentType":"application/json"})

downloadReportFile(id, query): Observable<Object[]> {
var url = `${environment.baseUrl}report?report_name=${id}${query}`;
return Observable.create(observer => {
let xhr = new XMLHttpRequest();
xhr.open('GET', `${url}`, true);
xhr.setRequestHeader(environment.AUTH_TOKEN_HEADER_KEY, 'Bearer '+
localStorage.getItem(environment.AUTH_TOKEN_STORE_KEY));
xhr.responseType = 'blob';
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
let filename = "Claim_Report.csv"
var contentType = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
var blob = new Blob([xhr.response], { type: "text/plain;charset=utf-8" });
if (typeof window.navigator.msSaveBlob !== 'undefined') {
window.navigator.msSaveBlob(blob, filename);
return;
}
const blobURL = window.URL.createObjectURL(blob);
const tempLink = document.createElement('a');
tempLink.style.display = 'none';
tempLink.href = blobURL;
tempLink.setAttribute('download', filename);
if (typeof tempLink.download === 'undefined') {
tempLink.setAttribute('target', '_blank');
}
document.body.appendChild(tempLink);
tempLink.click();
document.body.removeChild(tempLink);
setTimeout(() => {
// For Firefox it is necessary to delay revoking the ObjectURL
window.URL.revokeObjectURL(blobURL);
}, 100);
} else {
observer.error(xhr.response);
}
}
}
xhr.send();
});
}

Related

Running then method after XMLHttpRequest done

I have a method acting like an async method. After the request sends to the function that was called this request, I want to run something like the then method but then there is no then method for XMLHttpRequest.
the caller function in below code has no then method
let result = dataService.exportfile('get', '/api/overtimeedari/exporttoexcle/', model).
then(() => {
self.loading(false);//غیرفعال کردن حالت لود شدن گرید
buttonListSearch.Excel.loading(false); //غیرفعال کردن حالت لود شدن دکمه اکسل
});
the function called
function exportfile(mehtodtype, url, model) {
debugger;
var qs = "?";
model.map((item) => {
qs = `${qs}${item.name}=${item.value}&`;
});
var request = new XMLHttpRequest();
request.open(mehtodtype, url + qs, true);
request.setRequestHeader('Authorization', "Bearer " + window.localStorage.getItem('token'));
request.responseType = 'blob';
request.onload = function (e) {
if (this.status === 200) {
var blob = this.response;
if (window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveBlob(blob, fileName);
}
else {
var downloadLink = window.document.createElement('a');
var contentTypeHeader = request.getResponseHeader("Content-Type");
downloadLink.href = window.URL.createObjectURL(new Blob([blob], { type: contentTypeHeader }));
downloadLink.download = "Export.xls";
document.body.appendChild(downloadLink);
downloadLink.click();
document.body.removeChild(downloadLink);
}
}
};
request.send();
return request;
}
Given the constraint of not changing exportfile function as per comment
the case is I don't have this ability to change theexportfile function because it has side affects on other functions
the best way to handle this is as follows
let req = dataService.exportfile('get', '/api/overtimeedari/exporttoexcle/', model);
req.addEventListener('loadend', () => {
// do what's needed here
});
since exportfile returns the XMLHttpRequest object, you can listen for the loadend event and do whatever it you're doing there
Note, the loadend event is triggered regardless of success or failure
You could do the above with the load event if you want too - but, I'm unsure what order
x.onload=() => {};
x.addEventListener('load', () => {});
are fired ... also note, do NOT
req.onload=() => {};
since that would overwrite the onload callback inside the function

URL.createObjectURL(blob);

I am creating a PDF and opening in a new browser. My issue is word 'blob:' is getting appended to the URL
blob:http://localhost:3000/a0b859c9-57a0-40b7-a60f-9d2a72ab3c14
and I would like to be 'http://localhost:3000/a0b859c9-57a0-40b7-a60f-9d2a72ab3c14'
Is there a way I could achieve this ? My code is below
const blob = new Blob([response.data], {type : 'application/pdf'});
var pdfFileUrl = URL.createObjectURL(blob);
window.open(pdfFileUrl);
URL.revokeObjectURL(pdfFileUrl);
update:
I tried below code as well. It also gives the same results.
''
const xhr = new XMLHttpRequest();
//Send the proper header information along with the request
// listen for `onload` event
xhr.onload = () => {
// process response
if (xhr.status == 200) {
// parse JSON data
// console.log( "Response Received")
// var pdfurl = URL.createObjectURL(xhr.response) ;
console.log(xhr.response)
window.open(URL.createObjectURL(xhr.response));
} else {
console.log(" In Error block")
console.error('Error!');
}
};
// create a `GET` request
xhr.open('POST', url);
xhr.responseType = 'blob'
xhr.setRequestHeader('Content-Type', 'application/json');
// send request
xhr.send(JSON.stringify(payload));''

Download a file using XHR in iOS Safari

I am trying to add the functionality to download a file hosted in a server. To access the file I have to send the Authorization header, thus I have to send an XHR request to get the file from the server. Since the file content is in a variable, I have to create a data url to make it available as the href attribute of an anchor tag and click it programmatically to download the file.
It's working good in almost all the browser (Except IE11, for which I have written a separate code), but in iOS Safari (in some versions of iOS), it's giving errors. Here's the code that I am using -
var isBrowserIE = window.navigator && window.navigator.msSaveOrOpenBlob;
var dataHref = 'https://example.com/doc.pdf';
var xhr = new XMLHttpRequest();
xhr.open('GET', dataHref, true);
xhr.setRequestHeader('Content-Type', 'application/pdf');
xhr.setRequestHeader('Authorization', 'Bearer ' + token);
xhr.responseType = isBrowserIE ? 'blob' : 'arraybuffer';
xhr.onload = function (e) {
if (this.status == 200) {
//For IE11
if (isBrowserIE) {
// Create a new Blob object using the response data of the onload object
var blob = new Blob([this.response], { type: 'application/pdf' });
var bool = window.navigator.msSaveOrOpenBlob(blob, docName);
if (!bool) {
alert("Download failed, Please try again later");
}
} else {
var uInt8Array = new Uint8Array(this.response);
var i = uInt8Array.length;
var binaryString = new Array(i);
while (i--) {
binaryString[i] = String.fromCharCode(uInt8Array[i]);
}
var data = binaryString.join('');
var base64 = window.btoa(data);
var dataUrl = 'data:application/octet-stream;charset=utf-16le;base64,' + base64;
var element = document.createElement('a');
element.setAttribute('href', dataUrl);
element.setAttribute('download', 'doc.pdf');
element.style.display = 'none';
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
} else {
alert("Download failed, Please try again later");
closeWindow();
}
};
xhr.send();
Here's the possible error I am getting which is related -
Safari cannot open the page.<br><br>The error was: “Data URL decoding failed”.
Is there anything I missed which is the reason for this error? The error is occuring only in iPad 4 and iPad 5, but working in iPad mini and iPhone XR. Not sure why is it working in some versions of iOS devices and not in others.
So, I finally figured it out. Here's my final code with explanations in comments (Sorry for ES5 code, I needed to support IE11 and the current project is not using babel yet) -
/* exported DownloadHandler */
/* global Uint8Array*/
var DownloadHandler = (function() {
function isMobileDevice() {
return navigator.userAgent.match(/Android|iPhone|iPad|iPod|BlackBerry|Opera Mini|IEMobile/i);
}
function isChromeBrowser() {
return navigator.userAgent.match(/Crios|Chrome/i);
}
function isIEBrowser() {
return window.navigator && window.navigator.msSaveOrOpenBlob;
}
function isSafariBrowser() {
return navigator.userAgent.match(/Safari/i);
}
function getResponseType() {
// Both Desktop Chrome and IE supports blob properly
// Chrome also supports Data URI way, but it fails miserably when the file size is more than 2 MB (Not sure about the exact limit though).
if (isIEBrowser() || isChromeBrowser()) {
return 'blob';
} else if (isMobileDevice()) {
return 'arraybuffer';
}
return 'blob';
}
function getBlobUriFromResponse(response) {
var blob = new Blob([response], { type: 'application/pdf' });
var downloadUrl = URL.createObjectURL(blob);
return downloadUrl;
}
function getDataUriFromResponse(response) {
var uInt8Array = new Uint8Array(response);
var i = uInt8Array.length;
var binaryString = new Array(i);
while (i--) {
binaryString[i] = String.fromCharCode(uInt8Array[i]);
}
var data = binaryString.join('');
var base64 = window.btoa(data);
var dataUrl = 'data:application/octet-stream;charset=utf-16le;base64,' + base64;
return dataUrl;
}
function downloadFileUsingXHR(fileName, fileUrl, fileMimeType, requestType, headersList) {
var xhr = new XMLHttpRequest();
xhr.open(requestType, fileUrl, true);
xhr.setRequestHeader('Content-Type', fileMimeType);
for (var i = 0; i < headersList.length; i++) {
var header = headersList[i];
xhr.setRequestHeader(header.key, header.value);
}
xhr.responseType = getResponseType();
xhr.onload = function() {
if (this.status == 200) {
//For IE11
//IE uses blob with vendor specific code
if (isIEBrowser()) {
// Create a new Blob object using the response data of the onload object
var blob = new Blob([this.response], { type: fileMimeType });
var bool = window.navigator.msSaveOrOpenBlob(blob, fileName);
if (!bool) {
alert('Download failed, Please try again later');
}
} else {
var dataUrl;
if (this.responseType === 'blob') {
dataUrl = getBlobUriFromResponse(this.response);
} else {
dataUrl = getDataUriFromResponse(this.response);
}
var element = document.createElement('a');
// Safari doesn't work well with blank targets
if (!isSafariBrowser()) {
element.setAttribute('target', '_blank');
}
element.setAttribute('href', dataUrl);
element.setAttribute('download', fileName);
element.style.display = 'none';
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
} else {
alert('Download failed, Please try again later');
}
};
xhr.send();
}
return {
downloadFileUsingXHR: downloadFileUsingXHR
};
})();
Here's how to use the above code:
DownloadHandler.downloadFileUsingXHR('example.pdf', 'https://example.com/doc.pdf', 'application/pdf','GET',[{key:'Authorization',value:'Bearer ' + token}]);
I'll probably convert it into a library later and post a link here. I'll get the chance to refine the code too

Problem grabbing image from server with https request in PlayCanvas

I'm trying to use the PlayCanvas OAuth and CORS to request an image from a service via HTML request. as I understood the response return a JSON holding the data, and in this question I just want to save the path in the JSON to a .txt file located in the PlayCanvas Assets.
I'm not 100% sure about my code.
I haven't found how to grab the .txt into the JS script (it cannot be attached to an object)
will appreciate help with both
URL is
https://s3-us-west-2.amazonaws.com/ticomsoft-image-repo/1.png
I've tried to use an async request like the example appearing here
https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Synchronous_and_Asynchronous_Requests
in the 'createCORSRequest':
if ("withCredentials" in xhr) {
// Check if the XMLHttpRequest object has a "withCredentials" property.
// "withCredentials" only exists on XMLHTTPRequest2 objects.
xhr.open(method, url, true);
xhr.onload = function (e) {
if (xhr.readyState === 46) {
if (xhr.status === 200) {
console.log(xhr.responseText);
} else {
console.error(xhr.statusText);
}
}
};
xhr.onerror = function (e) {
console.error(xhr.statusText);
};
I tried to place the 'stringify' and 'download' commands in initialize (moved then inside the callback
and finally ended up with what's appearing here
var Https = pc.createScript('https');
var token = 'That's the PlayCanvas Token';
var request = 'curl -H "Authorization: Bearer '+token+'" ';
var ts_URL ='https://s3-us-west-2.amazonaws.com/ticomsoft-image-repo/1.png';
// initialize code called once per entity
Https.prototype.initialize = function() {
var url = request+ts_URL;
// ref: curl -H "Authorization: Bearer nesgdxhiqe7hylfilr6ss1rds0gq1uj8" https://playcanvas.com/api/...
var xhr = createCORSRequest('GET', url);
if (!xhr) {
throw new Error('CORS not supported');
}
};
function createCORSRequest(method, url) {
var xhr = new XMLHttpRequest();
if ("withCredentials" in xhr) {
// Check if the XMLHttpRequest object has a "withCredentials" property.
// "withCredentials" only exists on XMLHTTPRequest2 objects.
if(method=="GET")
{
loadFile(url, DownloadToText(xhr));
}
// else... all the other cases
return xhr;
}
function loadFile(url, callback /*, opt_arg1, opt_arg2, ... */) {
var xhr = new XMLHttpRequest();
xhr.callback = callback;
xhr.arguments = Array.prototype.slice.call(arguments, 2);
xhr.onload = xhrSuccess;
xhr.onerror = xhrError;
xhr.open("GET", url, true);
xhr.send(null);
}
function DownloadToText (ans)
{
JSON.stringify(ans);
download(ans, 'json.txt', 'text/plain');
}
function download(content, fileName, contentType) {
var a = document.createElement("a");
var file = new Blob([content], {type: contentType});
a.href = URL.createObjectURL(file);
a.download = fileName;
a.click();
}
function xhrSuccess() {
this.callback.apply(this, this.arguments);
}
function xhrError() {
console.error(this.statusText);
}
expected results: I expected a json.txt file to be downloaded with the URL of the image inside.
Actual results: when I launched the program and went to console, saw the image 1.png got a 404 Not Found error.
the json.txt was downloaded with '[object XMLHttpRequest]'.
Also
in the F12 i got that the link leading to the error is
https://launch.playcanvas.com/curl%20-H%20%22Authorization:%20Bearer%---theToken---%22%20https://s3-us-west-2.amazonaws.com/ticomsoft-image-repo/1.png
while simply
https://s3-us-west-2.amazonaws.com/ticomsoft-image-repo/1.png leads to the image.
but i can't get away from the prefix if i wanna pass through the OAuth.. which is why i don't understand what was i'm doing wrong.

node js XMLHttpRequest issue

Trying to run this in node js, code is constant throwing error on "xhr.target.status" of function makeCORRequest as if its not inheriting XMLHttpRequest functions for xhr. Code is for discord bot trying to get it to post from teamup calendar.
first function is working (and is where xhr is initialised) and is called by error function.
var logger = require('winston');
var auth = require('./auth.json');
var XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;
function createCORSRequest(method, url) {
var apiKey = 'API_KEY';
var xhr = new XMLHttpRequest();
if ("withCredentials" in xhr) {
// XHR for Chrome/Firefox/Opera/Safari/IE10+.
xhr.open(method, url, true);
xhr.setRequestHeader('Teamup-Token', apiKey);
} else if (typeof XDomainRequest != "undefined") {
// XDomainRequest for IE8/IE9.
xhr = new XDomainRequest();
// XDomainRequest does not support querying HTTPS from HTTP pages
if (window.location.protocol === 'http:') {
url = url.replace('https://', 'http://');
}
if (-1 === ['GET', 'POST'].indexOf(method)) {
alert('XDomainRequest only supports GET and POST methods');
return;
}
if (-1 === url.indexOf('?')) {
url += '?_teamup_token=' + apiKey;
} else {
url += '&_teamup_token=' + apiKey;
}
xhr.open(method, url);
} else {
// CORS not supported.
xhr = null;
}
return xhr;
}
And this is the function that keeps failing. What works fine is xhr.onload =function(xhr), but if(xhr.target.status<400) throws the error "cannot read property"
// Sends the actual CORS request.
function makeCorsRequest(url, successCallback, errorCallback) {
var xhr = createCORSRequest('GET', url);
if (!xhr) {
alert('CORS not supported');
return;
}
// Response handlers.
xhr.onload = function (xhr) {
if (xhr.target.status < 400) {
if (successCallback) successCallback(xhr.target);
} else if (errorCallback) {
errorCallback(xhr.target);
}
};
xhr.onerror = function (xhr) {
if (errorCallback) {
errorCallback(xhr.target);
}
};
xhr.send();
}

Categories

Resources