Google Apps Script Content Service – Cross Domain Puzzle

Doing cross domain requests for JSON data from client jQuery apps are hard work. In the past, you would have needed a data source that had implemented JSONP, but nowadays you can use CORS (cross origin resource sharing). However CORS only works if the server is co-operating – meaning that it is allowing access from the domain hosting the client doing the requesting.

Google Apps Script Content Service

Using GAS webapps, you can handle GETS and POSTS by using the Content Service, and can serve up JSON data from within the Google Apps environment quite well. However, it is of course subject to the cross domain restrictions when the client is a browser (or is it? see later).

It seems that GAS Content Service does not support CORS, and up till now I’ve managed GET requests perfectly fine by returning JSONP. However, you cannot POST to a Google Apps Script webapp unless it is publicly available, but the thing is, I need to POST to webapps that have different permissions, and I don’t really want to use a proxy to do it.

Here’s the summary results of a few tests.

What’s strange about this is why “anyone,even anonymous” can GET and POST  JSON  across domains from a client browser. I’m almost certain it didn’t used to be able to. And if that can work, then what makes the other permissions not work?

Here’s the test google apps script handler.

var prot = {web:"only myself",share:"only me",execute:"as me"};
function doGet(e) {
  return makeContent ( makeResponse(e,"GET"));
}
function doPost(e) {
  return makeContent ( makeResponse(e,"POST"));
}
function makeResponse (e,type) {
  var s  = JSON.stringify({type:type,params:e,prot:prot});
  if (!e.parameter.callback) {
      return {mime:ContentService.MimeType.JSON,  content:s};
  }
  else {
    return {mime:ContentService.MimeType.JAVASCRIPT, content:e.parameter.callback + "(" + s + ");" };
  }
}
function makeContent( content) {
  return ContentService.createTextOutput(content.content)
              .setMimeType(content.mime);
}

and here’s the client tester

(function () {
       google.load("jquery", "1");
       google.setOnLoadCallback(function() {
            var u = "https://script.google.com/a/macros/mcpher.com/s/AKfycbwlo9ZT8M8C368opw9AAL3XvJIzzmtGqeV7N_TUL_0i5U2WDV8/exec";
            ajaxStuff ("GET",u);
            ajaxStuff ("GET",u,null,true);
            ajaxStuff ("POST",u,{data:'somestuff'});
            ajaxStuff ("POST",u,{data:'somestuff'},true);
        });
        
        function ajaxStuff(method,url,postData,p) {
            var p = (p ? true: false);
            url += "?method="+method + "&p=" +p;
            dataType = p ? "jsonp" : "json";
            var elem = $('#'+method+p);
            if (p) url += "&callback=?";
            $.ajax({
              type:method,
              url: url,
              data: postData,
              dataType: dataType
            })
            .done (function (data) {
                elem.text(JSON.stringify(data));
            })
            .fail (function ( jqXHR) {
                elem.text(jqXHR.statusText);
            });
        }
    })();

For both the non JSONP tests, as expected, I get this

XMLHttpRequest cannot load https://script.google.com/a/macros/mcpher.com/s/AKfycbwlo9ZT8M8C368opw9AAL3XvJIzzmtGqeV7N_TUL_0i5U2WDV8/exec?method=GET&p=false. No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘http://vm-0.brucemcpherson.kd.io‘ is therefore not allowed access.

The JSONP GET works fine, but the POST is turned into a GET by jQuery since I am asking for a callback.

When I change the web app protection to
{web:”anyone, even anonymous”,share:”only me”,execute:”as me”};

All 4 work (except that jQuery changes the POST jSONP to a GET). I do notice that the content is served up from a different domain than the request went to, and a successful request includes this is the response header.

access-control-allow-origin: *

I’m implementing a ScriptDB API for jQuery (like this one for VBA) and need to figure out how to POST to a GAS webapp that is not public. I have tried authenticating with oAuth2, but that doesn’t change the results at all.

Any ideas would be most welcome. Keep a look out here for a solution (or a workaround)

About brucemcp 225 Articles
I am a Google Developer Expert and decided to investigate Google Apps Script in my spare time. The more I investigated the more content I created so this site is extremely rich. Now, in 2019, a lot of things have disappeared or don’t work anymore due to Google having retired some stuff. I am however leaving things as is and where I came across some deprecated stuff, I have indicated it. I decided to write a book about it and to also create videos to teach developers who want to learn Google Apps Script. If you find the material contained in this site useful, you can support me by buying my books and or videos.