HTTP POST Request with JSON payload in Google Apps Script - javascript

I'm having trouble getting Google App Script's UrlFetchApp class to correctly format a JSON payload in a HTTP request.
Here's how I'm formatting the payload:
var data = {
'RequestData': {
'ItemNumber': '1',
'StockItemId': '2',
'ImageUrl': 'www.example.com'
}
};
var payload = JSON.stringify(data);
Surely enough, Logger.log(payload) returns:
{"RequestData":{"ItemNumber":"1","StockItemId":"2","ImageUrl":"www.example.com"}}
However, when I integrate this with the actual request:
var url = 'https://example.com/api/method';
var options = {
'method': 'post',
'contentType':'application/json',
'payload': payload,
'muteHttpExceptions': true
};
and examine Logger.log(UrlFetchApp.getRequest(url, options).toSource()), I get this:
({method:"post",
payload:"{\"RequestData\":{\"ItemNumber\":\"1\",\"StockItemId\":\"2\",\"ImageUrl\":\"www.example.com\"}}",
followRedirects:true,
validateHttpsCertificates:true,
useIntranet:false, contentType:"application/json",
url:"https://example.com/api/method"})
i.e. all the quotes are escaped, as if JSON.stringify is being called twice on the payload.
I thought GAS might be calling an equivalent of JSON.stringify on the payload behind the scenes because contentType is specified as "application/json" but if I pass the data raw without stringifying it, I get:
({method:"post",
payload:"RequestData=%7BItemNumber%3D1,+StockItemId%3D2,+ImageUrl%3Dwww.example.com%7D",
followRedirects:true,
validateHttpsCertificates:true,
useIntranet:false, contentType:"application/json",
url:"https://example.com/api/method"})
which is weird and (I'm pretty sure) an invalid JSON string. Either way, the request fails (400 Bad Request) and I'm pretty sure it's because I can't get the JSON across properly.
All the docs and examples I can find seem to suggest that JSON.stringify(payload) is the way to go but I'm clearly having issues...
Have also had the thought that this might have something to do with the implementation of the .toSource() method and that my problem might lie elsewhere but I have no way of knowing / otherwise being able to check the request body I'm sending.
Any insight would be much appreciated!

Related

AJAX request posting data in body instead of URL concatenation

I see many websites pursuing their requests through data concatenation in their URL, for example: example.com/api?data=111.
On my website I handle the request in another way, through AJAX:
return $.ajax({ url: '../api.php',
data: {data: '111'},
type: 'post'
});
I was wondering if in my method there is any flaw or if it can be used like the standard method of URL concatenation.
Update for context
function createAJAXRequestToGetCategories() {
return $.ajax({
url: '../server.php',
data: {
method: 'requestCategories'
},
type: 'post'
});
}
ajaxRequest = createAJAXRequestToGetCategories();
ajaxRequest.done(functionToExecute);
functionToExecute(responseData) {}
Some differences that come to mind:
You cannot use the body in the same way during GET requests, obviously this is not what you are doing above but a lot of requests you are likely seeing are GET requests
Debugging, seeing someone hitting /api.php?action=create.user and receiving 500s in your logs is somewhat easier to debug than all of your endpoints under /api.php
URL character limits, just last week I managed to go over the limit for characters in an endpoint request, i.e. /api.php?id=1,2,3,4,5... (general limit is 2083 char), using the body circumvents this to an extent (your server will still limit data posted to an extent)
There may be more, compelling arguments either way. I typically use URL params for purpose/identification- and the body for actual data.

Best way to replace left and right parenths in a Get request

For my GET request, my server is rejecting it with status: 403, "HTTP/1.1 403 Forbidden"
My GET request object as is follows :
"{"method":"GET","url":"api/myapi/GETstuff","params":{"from":"2017-06-02","to":"2017-06-02","myData":"DATA (AND SOME/MORE DATA)"}}"
The javascript code is:
function getMyData(params){
var url = 'myapi/getStuff';
var req = {
method: 'GET',
url: url,
params: params
};
$http(req) // details omitted for brevity
.success()
.error();
}
I believe the problem is special characters send to the IIS server (such as parenths, and / char), which I need to encode the params array before the GET request is sent via the Angular $http() service.
In order to encode the left/right parenths, and the / char, I'm trying:
request.params.myData = request.params.myData.replace(/\(/g, '%28').replace(/\)/g, '%29').replace(/\//g, '%2F')
However, my server is still rejecting the request.
And I doing something wrong?
You're getting a Forbidden error. Sure you don't need to authenticate or pass a token?
Before getting too manual, I'd suggest trying JSON.stringify(obj) and as a second pass, encodeURIComponent(obj)
If you're posting a valid JSON object, you might want to consider POST instead of GET, if you have backend access.

Getting a parseerror from jQuery $.ajax post return using jsonp from Trakt.tv api

Working with the Trakt.tv API. It looks like I'm sending valid json as I'm able to authenticate but the return I receive is a parse error.
Resource interpreted as Script but transferred with MIME type text/html:
http://api.trakt.tv/recommendations/shows/myApiKeyCompleteNumbers?callback=jQuery111000155555475132972_1397674204444&{%22username%22:%22userName%22,%22password%22:%22mySha1PassComplete%22}&_=1397674207093
Uncaught SyntaxError: Unexpected identifier
The return says:
Disallowed Key Characters.
I'm using:
jQuery 1.11.0
Thanks in advance for any help or guidance
$(document).ready(function () {
function success(data) {
alert('data: ' + data);
}
var traktUser = 'myUserName';
var traktHash = 'mySha1Password';
var traktApi = 'myApiKey';
var data = {
'username': traktUser,
'password': traktHash
};
var postData = JSON.stringify(data);
var apiUrl = 'http://api.trakt.tv/recommendations/shows/' + traktApi;
$.ajax({
type: 'POST',
url: apiUrl,
data: postData,
contentType: 'application/json',
dataType: 'jsonp',
}).
done(success);
}); //document ready
You can't make a POST request using JSONP, jQuery is ignoring the POST instruction and making a GET request.
Your data is being placed in the query string and is not properly URL Encoded.
The server is responding with an HTML document containing an error message instead of a JavaScript script formatted according to JSONP rules.
It looks like the API you are trying to use does not support JSONP at all. Since you are passing your own user credentials in the request, this makes sense. JSONP is a hack to work around the Same Origin Policy that is implemented by browsers (these days we can use CORS instead) and there is no point in using it unless you want end user browsers to access the API directly. Since end user browsers couldn't access it without being given your username and password, it doesn't seem likely to be intended to be used that way.
Process the data from the API on your server instead.

AppState with Google Client API. Update not possible?

We are integrating Google App State into our web based game using Google's API discovery service. Our test code looks like this:
gapi.client.load('appstate','v1',function(response) {
var request = gapi.client.appstate.states.update({ stateKey: 0 });
request.execute(function(result) { console.log(result); });
});
I always get back an HTTP 400 with the message "The update request does not contain any data.". This makes sense since I'm not passing any data, the problem is that I have no idea how to pass the data (aka state) and I can't find any documentation on how to do it. I tried passing some data along with the stateKey
{ stateKey: 0 , data: <some-base64-encoded-data> }
But this still fails with the same message.
After inspecting the library's minified code I'm starting to suspect this feature is still unimplemented.
UPDATE (after doing some more experiments)
I also tried using gapi.client.request like this:
var request = gapi.client.request({
'path': '/appstate/v1/states',
'params': { 'stateKey': '0' },
'method': 'PUT',
'body': {
'kind': 'appstate#updateRequest',
'data': 'ZGF0YQ=='
}
});
But I get a 404 and I think it's because this method passes request parameters in the &stateKey=0 style. On the other hand if I use 'path': '/appstate/v1/states/0' I get a conflict response which is what I was expecting.
Correct me if I'm wrong but I'm starting to believe that the problem is due to inconsistencies between Google's different web APIs which they are now trying to coalesce into a single interface.
In your initial Discovery-based method, you need to pass the data in a parameter named 'resource.'
Here is the Discovery document for appstate/v1: https://www.googleapis.com/discovery/v1/apis/appstate/v1/rpc

How to send data in request body with a GET when using jQuery $.ajax()

The service API I am consuming has a given GET method that requires the data be sent in the body of the request.
The data required in the body is a list of id's separated by hypen and could potentially be very large and thus it must be sent in the body otherwise it will likely foobar somewhere in the browsers/proxies/webservers etc chain. Note I don't have control over the service or API so please don't make suggestions to change it.
I am using the following jQuery code however observing the request/response in fiddler I can see that the "data" I am sending is ALWAYS converted and appended to the query string despite me setting the "processData" option to false...
$.ajax({
url: "htttp://api.com/entity/list($body)",
type: "GET",
data: "id1-id2-id3",
contentType: "text/plain",
dataType: "json",
processData: false, // avoid the data being parsed to query string params
success: onSuccess,
error: onError
});
Anyone know how I can force the "data" value to be sent in the body of the request?
In general, that's not how systems use GET requests. So, it will be hard to get your libraries to play along. In fact, the spec says that "If the request method is a case-sensitive match for GET or HEAD act as if data is null." So, I think you are out of luck unless the browser you are using doesn't respect that part of the spec.
You can probably setup an endpoint on your own server for a POST ajax request, then redirect that in your server code to a GET request with a body.
If you aren't absolutely tied to GET requests with the body being the data, you have two options.
POST with data: This is probably what you want. If you are passing data along, that probably means you are modifying some model or performing some action on the server. These types of actions are typically done with POST requests.
GET with query string data: You can convert your data to query string parameters and pass them along to the server that way.
url: 'somesite.com/models/thing?ids=1,2,3'
we all know generally that for sending the data according to the http standards we generally use POST request.
But if you really want to use Get for sending the data in your scenario
I would suggest you to use the query-string or query-parameters.
1.GET use of Query string as.
{{url}}admin/recordings/some_id
here the some_id is mendatory parameter to send and can be used and req.params.some_id at server side.
2.GET use of query string as{{url}}admin/recordings?durationExact=34&isFavourite=true
here the durationExact ,isFavourite is optional strings to send and can be used and req.query.durationExact and req.query.isFavourite at server side.
3.GET Sending arrays
{{url}}admin/recordings/sessions/?os["Windows","Linux","Macintosh"]
and you can access those array values at server side like this
let osValues = JSON.parse(req.query.os);
if(osValues.length > 0)
{
for (let i=0; i<osValues.length; i++)
{
console.log(osValues[i])
//do whatever you want to do here
}
}
Just in case somebody ist still coming along this question:
There is a body query object in any request. You do not need to parse it yourself.
E.g. if you want to send an accessToken from a client with GET, you could do it like this:
const request = require('superagent');
request.get(`http://localhost:3000/download?accessToken=${accessToken}`).end((err, res) => {
if (err) throw new Error(err);
console.log(res);
});
The server request object then looks like {request: { ... query: { accessToken: abcfed } ... } }
You know, I have a not so standard way around this. I typically use nextjs. I like to make things restful if at all possible. If I need to make a get request I instead use post and in the body I add a submethod parameter which is GET. At which point my server side handles it. I know it's still a post method technically but this makes the intention clear and I don't need to add any query parameters. Then the get method handles a get request using the data provided in the post method. Hopefully this helps. It's a bit of a side step around proper protocol but it does mean there's no crazy work around and the code on the server side can handle it without any problems. The first thing present in the server side is if(subMethod === "GET"){|DO WHATEVER YOU NEED|}

Categories

Resources