Sending multipart/mixed request in JavaScript - javascript

i want to send multipart request to Google Search Console as described in here
https://developers.google.com/webmaster-tools/v1/how-tos/batch#batch-example-request
I'm looking for any help how to send request like this in JavaScript. I'm struggling with this for couple days and i'm running out of ideas.
My code:
var xhr = new XMLHttpRequest();
xhr.open('POST', 'https://www.googleapis.com/batch/webmasters/v3');
xhr.setRequestHeader('Authorization', 'Bearer ' + access_token);
xhr.setRequestHeader('Host', 'www.googleapis.com');
xhr.setRequestHeader('Content-Type', 'multipart/mixed; boundary=batch_foobarbaz');
var data = [];
data.push('\n');
data.push('--batch_foobarbaz');
data.push('Content-Type: application/http');
data.push('\n');
data.push('GET /webmasters/v3/sites/' + siteAdress);
data.push('\n');
data.push('--batch_foobarbaz');
data.push('Content-Type: application/http');
data.push('\n');
data.push('GET /webmasters/v3/sites/' + siteAdress2);
data.push('\n');
data.push('--batch_foobarbaz--');
var payload = data.join('\n');
xhr.send(payload);
My request look like this
--batch_foobarbaz
Content-Type: application/http
GET /webmasters/v3/sites/https%3A%2F%2Fdomain.pl%2F
--batch_foobarbaz
Content-Type: application/http
GET /webmasters/v3/sites/https%3A%2F%2Fdomain.pl%2F
--batch_foobarbaz--
Response i reciving
{
"error": {
"code": 400,
"message": "Failed to parse batch request, error: Failed in parsing HTTP headers: (64 bytes redacted). Received batch body: (0) bytes redacted",
"status": "INVALID_ARGUMENT"
}
}

Related

Fetch Request Data is missing Data in iOS

For the past few weeks, I have been trying to send audio files to a server in flask using the fetch function. The code for the post request is as follows
sendDataToServer = async (data, timecode, location = "postFileWebm/") => {
// data is a blob
// timecode is current time in millisecons
// location is the function to run in the flask server
const formData = new FormData();
formData.append("file", data);
const length = data.length || data.size;
console.log( formData.get( "file" ) );
return fetch(`${SERVER_URL}/${location}`, {
headers: {
name: `${this.props.id}${timecode}`,
segment: count,
id: this.props.id,
label: this.state.label,
gameId: this.props.gameId,
"Content-Length": length,
extension: "wav",
"Content-Range": "bytes " + 0 + "-" + length + "/" + length,
"Content-Transfer-Encoding": "binary",
"Accept-Ranges": "bytes",
},
method: "POST",
body: formData,
},);
};
In most devices the following code is functional and the request data is similar to
------WebKitFormBoundarybLCq4cl3vSy70eG4
Content-Disposition: form-data; name="file"; filename="blob"
Content-Type: audio/wav
RIFF$#WAVEfmt »wdata#
------WebKitFormBoundarybLCq4cl3vSy70eG4--
but in iOS the same code is missing the data and is usually
------WebKitFormBoundarybLCq4cl3vSy70eG4
Content-Disposition: form-data; name="file"; filename="blob"
Content-Type: audio/wav
------WebKitFormBoundarybLCq4cl3vSy70eG4--
Wanted to ask then if someone knew how to make this code function in iOS

How I do use oauth in the reddit api in browser?

Nothing I do works, and I keep getting ridiculous CORS errors and other things. I just want to do a normal oath to log a user in, through the browser. I want to use snoowrap, but I can't even get far enough to use it, because i need a refresh token.
I already authorize the app and get the 'code' back from the API, which im then supposed to use by making a post request to https://www.reddit.com/api/v1/access_token.
But I just get CORS errors every time.
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://www.reddit.com/api/v1/access_token. (Reason: missing token ‘access-control-allow-headers’ in CORS header ‘Access-Control-Allow-Headers’ from CORS preflight channel).
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://www.reddit.com/api/v1/access_token. (Reason: CORS request did not succeed).
code:
const redirect_uri = 'https://EXAMPLE.com/reddit/';
const client_id = 'xxxxxxxxxxxxx';
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString); /*global URLSearchParams*/
const code = urlParams.get('code');
var snoowrap = window.snoowrap;
if (code) {
console.log('code gotten', code);
const data = {
grant_type: 'authorization_code',
code: code,
redirect_uri: redirect_uri
};
ajax('https://www.reddit.com/api/v1/access_token', data, 'Basic client_id:', result => {
console.log(result);
const r = new snoowrap({
userAgent: 'skeddit',
clientId: client_id,
clientSecret: 'fFP-6BKjFtvYpIkgFGww-c6tPkM',
refreshToken: '',
});
r.getHot().map(post => post.title).then(console.log);
});
}
//GET: ajax(String url, Function success)
//POST: ajax(String url, Object postData, Function success)
function ajax(url, arg2, arg3, arg4) {
if (typeof arg2 == 'function')
var success = arg2;
else {
var postData = arg2;
var headers = arg3;
var success = arg4;
}
console.log('AJAX - STARTING REQUEST', url)
//start new request
var xhttp = new XMLHttpRequest({mozSystem: true});
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
success(JSON.parse(this.response));
xhttp = null;
console.log('AJAX - COMPLETE', this.response);
}
};
if (postData) {
//post request
console.log('post data: ', postData);
var formData = new FormData();
for ( var key in postData ) {
formData.append(key, postData[key]);
}
xhttp.open("POST", url, true);
xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhttp.setRequestHeader("Authorization", headers);
xhttp.send(formData);
}
else {
//get request
xhttp.open("GET", url, true);
xhttp.send();
}
return xhttp;
}
I don't even understand why something would prevent me from doing a POST request to a public api
After hours of searching I found a solution:
If you're creating a browser-only JS app (no server), you should select your app type as "installed app" (instead of "web app") in the reddit console.
Then you have to send an Authorization header whose value is your Client Id, as stated here reddit/wiki/OAuth2
const fd = new FormData();
fd.append("code", code);
fd.append("grant_type", "authorization_code");
fd.append("redirect_uri", "your_redirect_uri");
const r = await fetch("https://www.reddit.com/api/v1/access_token", {
headers: {
Authorization:
"Basic " + btoa(unescape(encodeURIComponent(CLIENT_ID + ":" + ""))),
},
method: "POST",
body: fd,
});

Using Autodesk's Forge OSS, I can upload to a bucket, but the body of the download is empty

I'm using Autodesk's Forge Object Storage Service and while I can upload my file to my bucket, when I try to download it, the body comes out empty. However, when using Head, the Data-Size is correct.
Here is my upload (note that I'm using the signed url upload API):
var url = uploadOptions.url;
var fileReader = new FileReader();
// UploadOptions.Body contains a Blob
fileReader.readAsBinaryString(uploadOptions.Body);
fileReader.onloadend = function (e) {
var xhr = new XMLHttpRequest();
var lastLoadedValue = 0;
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && (xhr.status === 200 || xhr.status === 201)) {
console.log('UPLOAD FINISHED:', xhr.responseText);
callback();
}
};
xhr.open("PUT", url, true);
xhr.withCredentials = true;
// uploadOptions.ContentType = 'application/octet-stream'
xhr.setRequestHeader('Content-Type', uploadOptions.ContentType);
xhr.send(e.target.result);
Here is my download:
superagent
.get(_autodesk_api_baseurl
+ baseUrl
+ downloadOptions.bucket
+ '/objects/'
+ encodeURIComponent(downloadOptions.key))
.set('Authorization', 'Bearer '
+ token.access_token)
.query({'response-content-type': 'application/octet-stream'})
.end(function (err, resp) {
if (typeof callback === 'function') {
// All works fine
callback(undefined, resp);
}
});
And then, in the callback, I print my response and the body is empty.
I even wrote the JSON encoded response to a file to get this:
{
"req": {
"method": "GET",
"url": "https://developer.api.autodesk.com/oss/v2/buckets/storage.vcs.prod.mevsg.autodesk.com/objects/assets%2FNT5NR9KJU2PH%2Fea02ec77505f2ea2defac93fe231764f2916e4d1aeaac7d92945a08a0086c60667369431361d5aa426d4cccca49b9e4c7cb70bc6ebf700258a3cb37617eacfa0"
},
"header": {
"access-control-allow-credentials": "true",
"access-control-allow-headers": "Authorization, Accept-Encoding, Range, Content-Type",
"access-control-allow-methods": "GET",
"access-control-allow-origin": "*",
"content-disposition": "attachment; filename=\"ea02ec77505f2ea2defac93fe231764f2916e4d1aeaac7d92945a08a0086c60667369431361d5aa426d4cccca49b9e4c7cb70bc6ebf700258a3cb37617eacfa0\"",
"content-encoding": "gzip",
"content-type": "application/octet-stream",
"date": "Thu, 30 Jun 2016 18:03:10 GMT",
"etag": "\"8ad9c59b256cef48798a94c0295023088016d43a\"",
"server": "Apigee Router",
"vary": "Accept-Encoding",
"transfer-encoding": "chunked",
"connection": "Close"
},
"status": 200
}
As you can see, there is no body. But when I use Head on the object, I get the right number of bytes.
Can someone tell me what I'm doing wrong?
I tried hard-coding Content-Type to application/x-www-form-urlencoded and then I could download the file (there were bytes in the body), but the bytes were changed a little. For example, 208 (11010000) became 80 (1010000). As you can see, the first bit was reversed. With that content-type, I could not open the file. Which way should I use?
UPDATE:
With the help of Augusto, I found the problem.
Superagent doesn't seem to work, but Request does.
Not sure if it had an real impact, but I set the encoding for the downloaded buffer to base64
I needed to upload the Blob directly. I didn't have to use a FileReader to read the bytes.
Here's what solved the problem:
For the upload, I needed to only send the Blob. Here's the updated code:
var url = uploadOptions.url;
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && (xhr.status === 200 || xhr.status === 201)) {
console.log(xhr.status);
console.log('UPLOAD FINISHED:', xhr.responseText);
callback();
}
};
xhr.open("PUT", url, true);
xhr.withCredentials = true;
// Send the Blob directly!!
xhr.setRequestHeader('Content-Type', uploadOptions.ContentType);
xhr.send(uploadOptions.Body);
For the download, the library Superagent didn't work, but Request did. I also changed the encoding of the buffer to base64. Here's the code:
request({
url: _autodesk_api_baseurl
+ baseUrl
+ downloadOptions.bucket
+ '/objects/'
+ encodeURIComponent(downloadOptions.key),
method: "GET",
headers: {
'Authorization': 'Bearer ' + token.access_token
},
encoding: null
}, function (error, response, body) {
//Error handling goes here
if (typeof callback === 'function') {
callback(null, new Buffer(body, 'base64'));
}
});
After, that, I can just write the buffer to a file and open it. Thanks to those who helped answer.
This section doesn't seem right:
.query({'response-content-type': 'application/octet-stream'})
It should be
.set('response-content-type', 'application/octet-stream')
Anyway, Augusto is correct - you don't need to provide any additional header other than Authorization
Don't believe you need content-type to download a file, see more here. Can you request the details of the file?
This the curl to download it that should work:
curl -v "https://developer.api.autodesk.com/oss/v2/buckets/storage.vcs.prod.mevsg.autodesk.com/objects/assets%2FNT5NR9KJU2PH%2Fea02ec77505f2ea2defac93fe231764f2916e4d1aeaac7d92945a08a0086c60667369431361d5aa426d4cccca49b9e4c7cb70bc6ebf700258a3cb37617eacfa0"
-X GET
-H "Authorization: Bearer AbCdEfGhIjKlMnOpQrStUvXwYz"
And here is a code I used with NodeJS/Request module. It's in fact quite generic, but if I pass the /oss/ URL resource, it works fine. I'm not sure which library you're using, sorry.
function download(resource, token, onsuccess) {
console.log('Downloading ' + config.baseURL + resource); // debug
request({
url: config.baseURL + resource,
method: "GET",
headers: {
'Authorization': 'Bearer ' + token,
},
encoding: null
}, function (error, response, body) {
onsuccess(new Buffer(body, 'base64'));
});
}

Unable to perform POST request using jQuery to obtain Strava OAuth token

I'm encountering an issue while trying to retrieve an access token using the Strava API: https://strava.github.io/api/v3/oauth/
My initial code request and callback function properly, but when attempting to hit the /oauth/token URL I fail in two different ways. The first:
console.log('Posting to /token with code: ' + code);
Ember.$.ajax({
type: 'POST',
url: 'https://www.strava.com/oauth/token',
data: 'client_id=<myid>&client_secret=<mysecret>&code=' + code,
success: function(data) {
var jsonData = JSON.stringify(data);
var accessToken = jsonData.access_token;
console.log('Received access token: ' + accessToken);
if (accessToken) {
this.get("controllers.application").set('settings.strava.accessKey', accessToken);
}
},
error: function(jqXHR, textStatus) {
console.log('API auth error occurred: ' + JSON.stringify(error));
throw new Error(error);
}
});
Prints Posting to /token with code: 3ae248f... and the HTTP request comes back with a 200 response (in the Chrome debugger Network tab), but I can't actually see the response contents/data in the debugger, and the browser console complains with:
XMLHttpRequest cannot load https://www.strava.com/oauth/token. No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://localhost:4200' is therefore not allowed
access.
But then if I add a few options to my above request:
crossDomain: true,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'DELETE, HEAD, GET, OPTIONS, POST, PUT',
'Content-Type': 'application/json;charset=UTF-8'
},
Then an OPTIONS request goes out first (to the /oauth/token endpoint), and comes back with 302 Found, but I then see a different error in the browser console:
XMLHttpRequest cannot load https://www.strava.com/oauth/token. Response for preflight is invalid (redirect)
CORS is not something I have a huge amount of experience with, this is where I have run out of ideas.
The following works fine in my Cordova App:
var c_id = "YOUR_ID_HERE";
var c_secret = "YOUR_SECRET_HERE";
var access_code = "YOUR_AUTH_HTTP_CODE_HERE";
var params = "client_id=" + c_id + "&client_secret=" + c_secret + "&code=" + access_code;
xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
alert(xmlhttp.responseText);
}
}
xmlhttp.open("POST", "https://www.strava.com/oauth/token", true);
xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xmlhttp.setRequestHeader("Content-length", params.length);
xmlhttp.setRequestHeader("Connection", "close");
xmlhttp.send(params);
For the preflight requests response should ends up with status 200 Ok and contains at least Access-Control-Allow-Origin: your origin

Sending plain ajax HTTP request with custom header

I have an existing java client on top of which IOS, andriod developers prepared a simple http request based applications. And am trying to achieve same in HTML5 app.
And the difficulty right now am facing is sending an custom header within the AJAX request like authorization with encrypted login details.
I tried to achieve same on various REST clients and able to send "AUTHORIZATION : BASIC XXXXXX=" in request header. And getting proper json response"
But if i try same using ajax call am not able to send similar request header. Request sending as OPTIONS instead of GET and the authorization tag is not going properly as a header instead it's going as "Access-Control-Request-Headers:authorization".
and here is the snippets i have tried.
<script>
//$.ajaxSetup({ headers: { 'Authorization': 'Basic XXXXXXX='} })
// get form data for POSTING
var vFD = new FormData(document.getElementById('upload_form'));
var oXHR = new XMLHttpRequest();
oXHR.open('POST', "https://123.123.123.123:229/");
//oXHR.send(vFD);
var body = 'Basic XXXXXXX=';
var mUrl = "https://123.123.123.123:229/?json";
var client = new XMLHttpRequest();
client.open('GET', mUrl, true);
client.withCredentials = true;
client.crossDomain = true,
client.setRequestHeader('Authorization', 'Basic XXXXXXX=');
client.send(body);
simpleHttpRequest();
function simpleHttpRequest() {
alert("calling ");
var headers = {
"Authorization": "Basic XXXXXXX="
};
$.ajaxSetup({ "headers": headers });
$.ajaxSetup({ 'cache': false });
$.ajax({
type: "GET",
withCredentials: true,
// data: {
// address: 'http://www.google.com'
// },
crossDomain: true,
Headers: { "Authorization": "Basic XXXXXXX=" },
dataType: "jsonp",
url: mUrl,
cache: false
});
}
xhrToSend();
function xhrToSend() {
// Attempt to creat the XHR2 object
var xhr;
try {
xhr = new XMLHttpRequest();
} catch (e) {
try {
xhr = new XDomainRequest();
} catch (e) {
try {
xhr = new ActiveXObject('Msxml2.XMLHTTP');
} catch (e) {
try {
xhr = new ActiveXObject('Microsoft.XMLHTTP');
} catch (e) {
statusField('\nYour browser is not' +
' compatible with XHR2');
}
}
}
}
xhr.withCredentials = true;
xhr.open('GET', mUrl, true);
xhr.setRequestHeader("Authorization", "numberOfBLObsSent");
xhr.send();
};
</script>
And all the different ways getting failed. Please help me.
Thanks in advance.
The issue is related to the cross-domain nature of the request. When you make a cross-domain request which contains custom headers, the request is first "preflighted" to the server via the OPTIONS method, and the server must respond with a header Access-Control-Allow-Headers: your-custom-header. Once this is received, the ajax client will then (automatically) issue the actual request.
More on preflighted requests

Categories