21

I have an error reporting beacon I created using Google Apps script and it is published to run as myself and to be accessible to "anyone, even anonymous," which should mean that X-domain requests to GAS are allowed.

However, my browsers are now indicating there is no Access-Control-Allow-Origin header on the response after the code posts to the beacon.

Am I missing something here? This used to work as recently as two months ago. So long as the GAS was published for public access, then it was setting the Access-Control-Allow-Origin header.

In Google Apps Script:

Code.gs
function doPost(data){
  if(data){
        //Do Something
  }
  return ContentService.createTextOutput("{status:'okay'}", ContentService.MimeType.JSON);
}

Client Side:

script.js
$.post(beacon_url, data, null, "json");

9 Answers 9

25

When making calls to a contentservice script I always have sent a callback for JSONP. Since GAS does not support CORS this is the only reliable way to ensure your app doesn't break when x-domain issues arrive.

Making a call in jQuery just add "&callback=?". It will figure everything else out.

 var url = "https://script.google.com/macros/s/{YourProjectId}/exec?offset="+offset+"&baseDate="+baseDate+"&callback=?";
 $.getJSON( url,function( returnValue ){...});

On the server side

function doGet(e){
 var callback = e.parameter.callback;
 //do stuff ...
 return ContentService.createTextOutput(callback+'('+ JSON.stringify(returnValue)+')').setMimeType(ContentService.MimeType.JAVASCRIPT);
}
3
  • 1
    I marked this as the answer, but it is a little off, so you might want to edit. For some reason, setting the content type inside createTextOutput, even with setting it to JavaScript does not fix the problem. However, using the function setContentType and setting it to JavaScript does work. Also, I am using a POST, and not a GET request in this use case. Commented Apr 9, 2015 at 17:20
  • 1
    Ah yea, JSONP will not work with Post. Everything you described sounds correct. Run the app as "me" allow access to "anonymous". Make sure you are making the call to the published address not the developer address. If all that is set you should have: Access-Control-Allow-Origin: * I've tested a small script with hurl.it. I'm seeing all the proper headers coming back from there. Commented Apr 9, 2015 at 18:14
  • Note that if for some reason there's an error in the script, it'll return text/html MIME type instead of JSON and then it'll result in a CORS error. So this may be the cause of your problem as well.
    – bryant1410
    Commented Aug 27, 2019 at 17:41
11

I've lost a couple of hours with the same issue. The solution was trivial.

When you deploy the script as webapp, you get two URLs: the /dev one and the /exec one. You should use /exec one to make cross domain POST requests. The /dev one is always private: it requires to be authorized and doesn't set *Allow-Origin header.

PS.: The /exec one seems to be frozen — it doesn't reflect any changes of code until you manually deploy it with a new version string (dropdown list in deploy dialog). To debug the most recent version of the script with the /dev URL just install an alternative browser and disable it's web-security features (--disable-web-security in GoogleChrome).

3
  • 1
    For future reference, this blog post seems to elaborate on this: Google Apps Script Content Service - Cross Domain Puzzle. Also quite relevant info here: App Script sends 405 response when trying to send a POST request. As it seems, the /exec scope will by pass CORS as expected only in the cases of simple requests.
    – Novack
    Commented Jan 15, 2019 at 20:02
  • 2
    @Novack you saved my day! Changing the request Content-type from application/json to AJAX default application/x-www-form-urlencoded solved my preflight 405 issue! Commented Dec 17, 2019 at 2:22
  • 1
    Thank you SO much for adding that detail about how the code is frozen. I've been looking for hours, confused b/c I'm doing everything to the letter, and it was still giving me the CO error. I appreciate that they don't auto-version it so it doesn't make a billion versions of code for each time you change an alert, but at the same time it would have been nice to include a note on there that says, "Hey, if you're deploying this with new code, you need to specify a new version."
    – Ryan
    Commented Feb 26, 2020 at 14:52
7

Just to make it simpler for those who are only interested in a POST request like me:

function doPost(e){

 //do stuff ...

 var MyResponse = "It Works!";

 return ContentService.createTextOutput(MyResponse).setMimeType(ContentService.MimeType.JAVASCRIPT);

}
3
  • 8
    This does not seem to work for me in 2018. I am still receiving the CORS error.
    – red-o-alf
    Commented Feb 15, 2018 at 2:30
  • 1
    @jj_ They start to work with /exec URL when you deploy your webapp. more details in my answer below.
    – SergeyR
    Commented Jun 25, 2018 at 22:21
  • Thanks for updating with other solutions, yeah this was a long time ago now! Still works for some though.
    – David
    Commented Oct 14, 2020 at 5:00
3

I stumbled upon the same issue:

  • calling /exec-urls from the browser went fine when running a webpage on localhost
  • throws crossorigin-error when called from a https-domain

I was trying to avoid refactoring my POST JSON-clientcode into JSONP (I was skeptical, since things always worked before).

Possible Fix #1

Luckily, after I did one non-CORS request (fetch() in the browser from a https-domain, using mode: no-cors), the usual CORS-requests worked fine again.

last thoughts

A last explanation might be: every new appscript-deployment needs a bit of time/usage before its configuration actually settled down at server-level.

1
  • I will have to try this out. Perhaps much has changed in the 5 years since I first posted this question. Commented Jul 15, 2020 at 17:21
2

Following solution works for me

In Google Apps Script

function doPost(e) {
  return ContentService.createTextOutput(JSON.stringify({status: "success", "data": "my-data"})).setMimeType(ContentService.MimeType.JSON);
}

In JavaScript

fetch(URL, {
      redirect: "follow",
      method: "POST",
      body: JSON.stringify(DATA),
      headers: {
        "Content-Type": "text/plain;charset=utf-8",
      },
    })

Notice the attribute redirect: "follow" which is very very important. Without that, it doesn't work for me.

1

I faced a similar issue of CORS policy error when I tried to integrate the app script application with another Vue application.

Please be careful with the following configurations:

  1. Project version should be NEW for every deployment.
  2. Execute the app as me in case you want to give access to all.
  3. Who has access to the app to anyone, anonymous.

Hope this works for you.

1

in your calling application, just set the content-type to text/plain, and you will be able to parse the returned JSON from GAS as a valid json object.

Here is my JSON object in my google script doPost function

var result = {
  status: 200, 
  error: 'None',
  rowID: rowID
};

ws.appendRow(rowContents);

  return ContentService.createTextOutput(JSON.stringify(result))
  .setMimeType(ContentService.MimeType.JSON);  

and here I am calling my app script API from node js

           const requestOptions = {
                method: 'POST',
                headers: {'Content-Type': 'text/plain'},
                body: JSON.stringify({param1: value, param2:value})
            };
const response = await fetch(server_URL, requestOptions);
            const data = await response.json();
            console.log(data);
            console.log(data.status);
0

My case is different, I'm facing the CORS error in a very weird way.

My code works normally and no CORS errors, only until I added a constant:

const MY_CONST = "...";

It seems that Google Apps Script (GAS) won't allow 'const' keyword, GAS is based on ES3 or before ES5 or that kind of thing. The error on 'const' redirect to an error page URL with no CORS.

Reference:
https://stackoverflow.com/a/54413892/5581893

0

In case this helps all any of those people like me:

I have a .js file which contains all my utility functions, including ones which call a GAS. I keep forgetting to clear my cache when I go to test updates, so I'll often get this kind of error because the cached code is using the /dev link instead of the /exec one.

Not the answer you're looking for? Browse other questions tagged or ask your own question.