20

I'm trying to POST to a web service that is expecting to get JSON as payload using Google Apps Script. I'm using the following code:

var options =
{
  "method" : "post",
  "contentType" : "application/json",
  "headers" : {
    "Authorization" : "Basic <Base64 of user:password>"  
  },
  "payload" : { "endDate": "2012-06-03" }
};

var response = UrlFetchApp.fetch("http://www.example.com/service/expecting/json", options);

On the server side I'm getting the following error:

WARN [facade.SettingsServlet] 04 Jun 2012 15:30:26 - Unable to parse request body: endDate=2012-06-03
net.liftweb.json.JsonParser$ParseException: unknown token e

I'm assuming that the server is expecting to get

{ "endDate": "2012-06-03" }

instead of

endDate=2012-06-03

but I don't know how to make the UrlFetchApp do it.

Guy
  • 12,388
  • 3
  • 45
  • 67

5 Answers5

21

I do not understand the server side error but the 'payload' parameter must be a string as specified here: https://developers.google.com/apps-script/class_urlfetchapp?hl=fr-FR#fetch.

try:

var options =
{
  "method" : "post",
  "contentType" : "application/json",
  "headers" : {
    "Authorization" : "Basic <Base64 of user:password>"  
  },
  "payload" : '{ "endDate": "2012-06-03" }'
};
  • It is said about the payload in the link: "...It can be a String, a byte array, or a JavaScript key/value map. See example.". When I try your proposal I get Error code 400 from the server. I tried `Utilities.jsonStringify` as well with the same 400 response code. – Guy Jun 05 '12 at 12:28
  • But is the server side error message the same? Try capturing the request to see what the body looks like. – Eric Koleda Jun 05 '12 at 21:18
  • The error message is different as I don't get the request to the server at all (400). I can't capture the request from the client side (Google Apps Script), and I don't see it passing the Tomcat. No track of it in the Tomcat logs. – Guy Jun 06 '12 at 02:03
  • 1
    Yes I was wrong you can send String, byte array, or js key/value map. But in your case I think you need to send a string. Try using this command after your fetch to capture the HTTP request sent by GAS: Logger.log(UrlFetchApp.getRequest("http://www.example.com/service/expecting/json", options).toSource()); – Thierry Chevillard Jun 06 '12 at 07:58
  • Thanks for the tip, but it doesn't show the body of the request. I got when using JsonStrigify: `({headers:{Authorization:"Basic ..."}, oAuthServiceName:"", useIntranet:false, payload:"{\"endDate\":\"2012-06-03\"}", oAuthUseToken:"", method:"post", contentType:"application/json", url:"http://www.example.com/service/expecting/json"})` – Guy Jun 06 '12 at 13:18
  • and `... payload:"dueDate=2012-06-03"...` when using the json (non '' or Stringify) version. – Guy Jun 06 '12 at 13:25
  • 2
    `JSON.stringify` is prefered over `Utilities.jsonStringify`. The later has issues with some ids that the former doesn't. – Henrique G. Abreu Jun 06 '12 at 16:00
  • Yes, this solved the problem. I still think that being able to "debug" the request would be a good idea. In line with the `getRequest` method. I had to use `curl -v -i --user user:password -H "Content-Type: application/json" -X POST -d '{ "endDate": "2012-06-03"}' http://example.com/service/expecting/json` to be able to check the request format and the response content. From the GAS interface I couldn't do it. – Guy Jun 07 '12 at 14:09
4
  • If you set payload as a String, it will be passed directly (as a UTF-8 string).
  • If you set payload as an Object, it will be sent like an HTML form (which means either 'application/x-www-form-urlencoded' if the fields are simple, or 'multipart/form-data' if the Object includes a blob/file).

For your use case (the server is expecting to receive JSON), it sounds like Utilities.jsonStringify() is the way to go.

Steve Lieberman
  • 206
  • 1
  • 2
  • I suspect that there might be an issue with GAS in this scenario as the body of the request in this case is invalid in some way. The server that is usually parsing JSON in the body from other sources, is unable to parse the incoming request from GAS. I tried both versions of Stringing the payload, with '...' and Utilities.jsonStringify(...). – Guy Jun 06 '12 at 15:42
  • 1
    Maybe to compare the requests, try the POST to an echo URL like: http://responseecho.appspot.com/ – Steve Lieberman Jun 06 '12 at 20:00
0

My variant. Thanks to book by Bruce Mcpherson "Going GAS From VBA to Google Apps Script"

var options =
{
  method : "POST",
  contentType : "application/json",
  headers : {
    Authorization : "Basic <Base64 of user:password>"  
  },
  payload : JSON.stringify({"endDate":"2012-06-03"})
};
Style-7
  • 985
  • 12
  • 27
-1

Something like this worked for me in a similar situation:

Instead of creating payload and adding to options, I built the parameters into a string to append to the URL:

var params = "id=2179853&price=62755";

then, I appended params to the url string:

var result = UrlFetchApp.getRequest(url + '?' + params, options);

So, my options was passed containing only the header.

For my project, this worked like a charm.

Steve
  • 31
  • 4
-2

Here goes the code that should work with some important comments:

function testMe() {
    var products_authkey = "------------";
    try {
        var url = "https://app.ecwid.com/api/v1/---------/product?id=----------&secure_auth_key=" + products_authkey;
        //url= "http://requestb.in/----------"; // you can actually debug what you send out with PUTs or POSTs using Requestb.in service
        var payload = {
            id: "21798583", // id is necessary and should be a string, or it might be sent in scientific representation (with E)
            price: 62755
        }; 

        payload = JSON.stringify(payload); // the payload needs to be sent as a string, so we need this
        var options = {
            method: "put",
            contentType: "application/json", // contentType property was mistyped as ContentType - case matters
            payload: payload
        }; 
        var result = UrlFetchApp.getRequest(url, options);
        Logger.log(result) // a better way to debug
        var result = UrlFetchApp.fetch(url, options); // works perfectly in my case
        Logger.log(result)
    } catch (e) {
        Logger.log(e)
    }
}
Jeff Axelrod
  • 27,676
  • 31
  • 147
  • 246