How do i allow a CORS requests in my google script?
Asked Answered
B

14

21

I want to post my contact form to my google script that will send an e-mail to me. I use the following code:

var TO_ADDRESS = "[email protected]"; // where to send form data

function doPost(e) {

  var callback = e.parameter.callback;

  try {
    Logger.log(e); // the Google Script version of console.log
    MailApp.sendEmail(TO_ADDRESS, "Contact Form Submitted",
                      JSON.stringify(e.parameters));
    // return json success results
    return ContentService
          .createTextOutput(callback+
          JSON.stringify({"result":"success",
                          "data": JSON.stringify(e.parameters) }))
          .setMimeType(ContentService.MimeType.JSON);
  } catch(error) { // if error return this
    Logger.log(error);
    return ContentService
          .createTextOutput(callback+JSON.stringify({"result":"error", 
          "error": e}))
          .setMimeType(ContentService.MimeType.JSON);
  }
}

When i try to post to the google script url, i get the following error:

Access to XMLHttpRequest at 'https://script.google.com/macros/s/~~myscriptid~~/exec' from origin 'http://localhost:4200' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

I have no clue how to add the CORS-filter to my google script.

I know the script is working i have tested it with this plugin:

https://chrome.google.com/webstore/detail/allow-control-allow-origi/nlfbmbojpeacfghkpbjhddihlkkiljbi

Bullen answered 22/11, 2018 at 15:17 Comment(6)
where is your script expecting to be run? I believe currently you run it from localhost for development purposes. but how do you plan use it in the future?Stupa
The script runs in the google cloud, my application runs on localhost. It is a school assignment.Bullen
according to error message your script running at localhost is trying to access google cloud. But you say script should be run on google cloud. Am I missing something?Stupa
The script runs on google cloud, and gets called by my angular application.Bullen
sou CORS affects your angular application. where will it be run finally? there are different solutions possible depending on if it will run also at cloud,Stupa
I need it to work when my application is on a different domain then my google scriptBullen
S
0

As far as I understood you have application to be run on custom domain. And it should access script on google cloud.

The bad news: there are no way to skip CORS check on your application side(until request is simple that I believe is not your case).

You should specify Access-Control-Allow-Origin on Google Cloud side:

Cloud Storage allows you to set CORS configuration at the bucket level only. You can set the CORS configuration for a bucket using the gsutil command-line tool, the XML API, or the JSON API. For more information about setting CORS configuration on a bucket, see Configuring Cross-Origin Resource Sharing (CORS). For more information about CORS configuration elements, see Set Bucket CORS.

You can use either of the following XML API request URLs to obtain a response from Cloud Storage that contains the CORS headers:

storage.googleapis.com/[BUCKET_NAME]

[BUCKET_NAME].storage.googleapis.com

If this does not help for any reason you will need to get your own server working as a proxy:

your client application <-> your backend that returns Access-Control-Allow-Origin <-> google cloud

Stupa answered 23/11, 2018 at 9:9 Comment(0)
T
19

After a lot of hard work, the only solution which worked 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",
      },
    })

Note the attribute redirect: "follow" that is very important;

Thickleaf answered 26/8, 2021 at 6:24 Comment(5)
This helped me! Indeed, rediret is critical. Also, setting content-type header to text/plain is critical, even if sending JSON, as "text/plain" does not trigger the browser's preflight requests (which are unsupported by Google here)Oidea
indeed it worked for me !Confiding
The reason this may be required in some situations is explained here: For security reasons, content returned by the Content service isn't served from script.google.com, but instead redirected to a one-time URL at script.googleusercontent.com. This means that if you use the Content service to return data to another application, you must ensure that the HTTP client is configured to follow redirects.Content
I was losing my mind with this console error - thank you!Mouldon
Worked, setting content-type header to text/plain is the key, thank you.Guilt
C
5

Quick answer

You (frontend developer) can't fix cors error from remote server. Only the owner of the remote server (google app script server) could do it.

Workaround 1 (GET)

Use only GET method in app script. Get method will not throw CORS errors, no matter where you consume it from: csr, spa, frontend, react, angular, vue, jquery, pure javascript, etc

Workaround 2 (Backend)

If you are in the backend server (java, php, c#, node, ruby, curl, etc) not in the frontend (browser, react, angular, vue), you could consume any method published on google apps script.

CORS don't affect when the consumption is at the backend layer

So if only use get endpoints are not an option for you, you could use another server language (java, nodejs, php, etc) to consume the Post google app script, and return that information to your web

Explanation

Let's imagine this script with 02 methods deployed as web in google app script

function doGet(e) {
    var response = {
      "code": 200,
      "message": "I'm the get"
    };
    return ContentService.createTextOutput(JSON.stringify(response)).setMimeType(ContentService.MimeType.JSON);
}

function doPost(e) {
    var response = {
      "code": 200,
      "message": "I'm the post"
    };
    return ContentService.createTextOutput(JSON.stringify(response)).setMimeType(ContentService.MimeType.JSON);
}

google app script deploy

and url like this after the deployment:

https://script.google.com/a/utec.edu.pe/macros/s/AKfy\*\*\*\*\*\*eo/exec

In the backend

You could consume the POST and GET methods without any problems with any language: java, nodejs, python, php, c#, go , etc and/or with any http client like postman, insomnia, soapui, curl, etc

enter image description here

In the frontend (js in the browser)

I was not able to consume the POST method. I tried with jsonp and other crazy attempts and the error was the same:

Cross-Origin Request Blocked: The Same Origin Policy disallows 
reading the remote resource at         
https://script.google.com/a/utec.edu.pe/macros/s/AKfy***A4B***eo/exec?foo=bar 
(Reason: CORS header ‘Access-Control-Allow-Origin’ missing).

So for any reason, the google server don't allow us to use POST operations from javascript side (2021)

In the frontend : GET Method

google app script as get method

Only GET method worked for me. I will assume that google configuration at server layer has some CORS permission only for GET method.

The following ways worked for me, from a simple js to an advanced frameworks like react, vue or angular:

axios

const axios = require('axios');
axios.get('https://script.google.com/a/acme.org/macros/s/AKfy***A4B***eo/exec').then(resp => {
    console.log(resp.data);
});

$.getJSON

$.getJSON('https://script.google.com/a/acme.org/macros/s/AKfy***A4B***eo/exec?foo=bar', function(result) {
  console.log(result);
});

XMLHttpRequest

var xmlhttp = new XMLHttpRequest();
var theUrl = "https://script.google.com/a/acme.org/macros/s/AKfy***A4B***eo/exec?foo=bar";
xmlhttp.open("GET", theUrl);
xmlhttp.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
xmlhttp.send();

CORS : Cross-origin resource sharing

A lot of developers don't understand what is CORS. It is not easy to understand. Commonly the developer fix the error at the server layer and don't invest time (or don't let him) to understand what CORS is:

If you don't have time, check my definition, extreme summary bordering on wrong:

CORS is a protection offered by trusted browsers to avoid that a web acme.com can load in the background(ajax/js) an http resource from another domain like hacker-api.com/foo/bar

But if acme.com and hacker-api.com/foo/bar are developed by you and/or hacker-api.com/foo/bar is designed to be consumed by any web of the world, you could fix it at server layer

How to fix CORS errors?

Are very common and simple to control with a few lines in the server if the server belongs to us, but since we don't have control over the server(google), we can not do anything at this layer.

Here some samples of CORS configuration to allow consumption from webs is the backend server belongs to you:

java sample:

//only http://acme.com could consume my api
@CrossOrigin("http://acme.com")
@RequestMapping(method = RequestMethod.GET, "/{id}")
public Account retrieve(@PathVariable Long id)

nodejs sample:

//only http://localhost:8080 could consume my api
var corsOptions = {
    origin: 'http://localhost:8080',
    optionsSuccessStatus: 200 // For legacy browser support
}
app.use(cors(corsOptions));

//any web could consume my api
origin : "*"
Concatenate answered 19/5, 2020 at 19:49 Comment(3)
Would you mind explaining what you did different? I see no difference than a general post request, which doesn't work.Ld
this worked in 2022Hoxsie
@Neeraj, only GET methods work. POST are not allowed by some configs at google server layer.Concatenate
S
5

Late answer, but totally working...

To pass data from appscripts to another website, just use mime type JAVASCRIPT on appscripts side, like so:

doGet(e){   
 return ContentService
  .createTextOutput(e.parameter.callback + "(" + JSON.stringify(YOUR OBJECT DATA HERE)+ ")")
  .setMimeType(ContentService.MimeType.JAVASCRIPT);
}

And on the front end access it as:

<script>
var url = "https://script.google.com/macros/s/AKfy*****ACeR/exec?callback=loadData";
// Make an AJAX call to Google Script
jQuery.ajax({
crossDomain: true,
url: url,
method: "GET",
dataType: "jsonp"
});

 // log the returned data
  function loadData(e) {
  console.log(e);
  }
</script>

This works without any CROB/ CROS headache

Swordbill answered 25/11, 2020 at 12:26 Comment(2)
This didn't work for me when dealing with POST requests & the doPost function.Marquismarquisate
with doGet function work well for you @JelledenBurger ? you can share code example pleaseLocoism
I
5

I ran into the same issue while trying to create an application that logs data and retrieves log sections to/from a google sheet through Google Apps Script using Get and Post requests.

I did find a solution that may or may not be helpful to some people.

From the Google Docs:

There are two types of CORS requests: simple and preflighted. A simple request can be initiated directly. A preflighted request must send a preliminary, "preflight" request to the server to get permission before the primary request can proceed. A request is preflighted if any of the following circumstances are true:

It uses methods other than GET, HEAD or POST. It uses the POST method with a Content-Type other than text/plain, application/x-www-form-urlencoded, or multipart/form-data. It sets custom headers. For example, X-PINGOTHER.

All I did was change the content type of my Get and Post requests

var request = new window.XMLHttpRequest();
request.open(opts.method, opts.url, true);
request.setRequestHeader("Content-Type", "text/plain");

And within the google script, parse to JSON to be used

function doPost(e) {
  const d = JSON.parse(e);
...
Indiscerptible answered 31/10, 2022 at 0:6 Comment(0)
L
2

The CORS error is most probably caused by a fatal error in your Google Apps Web App script. In this case the Google error handling system displays a human-readable HTML page that does not contain CORS headers.

In my case I got the following error page:

GAS error screenshot

Loth answered 30/11, 2021 at 20:3 Comment(0)
S
0

As far as I understood you have application to be run on custom domain. And it should access script on google cloud.

The bad news: there are no way to skip CORS check on your application side(until request is simple that I believe is not your case).

You should specify Access-Control-Allow-Origin on Google Cloud side:

Cloud Storage allows you to set CORS configuration at the bucket level only. You can set the CORS configuration for a bucket using the gsutil command-line tool, the XML API, or the JSON API. For more information about setting CORS configuration on a bucket, see Configuring Cross-Origin Resource Sharing (CORS). For more information about CORS configuration elements, see Set Bucket CORS.

You can use either of the following XML API request URLs to obtain a response from Cloud Storage that contains the CORS headers:

storage.googleapis.com/[BUCKET_NAME]

[BUCKET_NAME].storage.googleapis.com

If this does not help for any reason you will need to get your own server working as a proxy:

your client application <-> your backend that returns Access-Control-Allow-Origin <-> google cloud

Stupa answered 23/11, 2018 at 9:9 Comment(0)
T
0

Well after several attempts, I was able to send the data through a web app form in angular 8. The solution is simple, within "HttpClient.post" you can enter a third parameter to establish an HTTP connection header this for "https://script.google.com" may not be correct and will end with an http connection failed by CORS security.

Just don't add the HTTP connection header as the third parameter of HttpClient.post

const object = {
  title: 'Prices',
  phone: '999999999',
  full_name: 'Jerson Antonio',
  email: '[email protected]',
  message: 'Hello, .......'
};
return this.http.post(this.API_REST_FORM, JSON.stringify(object));
Terms answered 4/3, 2020 at 6:40 Comment(0)
B
0

For me None of the above answers worked. I tried using mode:'no-cors'.it worked finally.

const response = await axios.post(
      'https://script.google.com/macros/s/**************',
      payload,
      {
        headers: {
          'Content-Type': 'text/plain',
        },
        mode: "no-cors"
      }
    );

and in the app script

function doPost(e) {
  try {
    var data = JSON.parse(e.postData.contents);
    var sheet = SpreadsheetApp.getActiveSheet();
    sheet.appendRow(data);
    // return ContentService.createTextOutput("Data appended successfully").setMimeType(ContentService.MimeType.TEXT);
    return ContentService.createTextOutput(JSON.stringify({status: "success", "data": "my-data"})).setMimeType(ContentService.MimeType.JSON);

  } catch (error) {
    return ContentService.createTextOutput("Error: " + error.message).setMimeType(ContentService.MimeType.TEXT);
  }
}

Credit: https://forums.appgyver.com/t/web-app-unable-send-request-to-appscript-due-to-cors/10230/2

Biome answered 18/9, 2023 at 7:33 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Brutal
G
0

This approach is working for me I have deployed my website on netlify and making a post request from the site.

appscript part:-

function doPost(e) {

  let ss=SpreadsheetApp.getActiveSpreadsheet()
  let sheet=ss.getSheetByName('Sheet3');
  let data=JSON.parse(e.postData.contents)
  sheet.getRange("A1").setValue(data.Email)

  return ContentService.createTextOutput(e.postData.contents).setMimeType(ContentService.MimeType.JSON)
}

post request:-

  await fetch(url, {
    method: "POST",
    mode: "no-cors", 
    cache: "no-cache",
    headers: {
      "Content-Type": "application/json",
    },
    redirect: "follow", 
    body: JSON.stringify(formData), 
  });
Gawky answered 16/3 at 16:36 Comment(0)
A
0

I faced this problem when I tried to pass a URL as a GET parameter. I deleted the protocol and domain name and succeeded.

Also, my deployment was 'For All.'

Also, don't forget to set

http.setRequestHeader("Content-Type", "text/plain");

Arciniega answered 29/5 at 13:23 Comment(0)
S
0

I post this solution because no response found in this topic or all related subject have solve the problem for me.

I have find a SOLUTION, set a POST request on Postman and disabled one by one not required header.

You need to indicate three Headers: Content-Type, Content-Length, Host, and one parameter "mode".

  const id = 'YourScriptId'
  const url = `https://script.google.com/macros/s/${id}/exec`

  const body = JSON.stringify({ email: "some data" });
  const requestOptions = {
    method: "POST",
    mode: "no-cors",
    headers: {
      "Content-Type": "application/json",
      "Content-Length": body.length,
      "Host": "script.google.com",
    },
    body: body
  }

  await fetch(url, requestOptions);

I hope this helps those like me who have tried everything for long hours :').

Ps: With this solution, json content is accepted.

Smollett answered 29/5 at 16:40 Comment(0)
P
0

All the answers that are here are completely useless. CORS error generally occurs when there is no Access-Control-Allow-Origin included in the response headers. but Unfortunately google sheet doesn't allow us to set custom headers. So whats the solution?.To create APIs from googlesheet we can go to https://docs.sheetdb.io/ and do as instructed

Prosthesis answered 16/6 at 8:43 Comment(0)
P
0

Another answer after 2 days of research from my side would be wait just for oneday it will take some time for the sheet url endpoint to configure correct response headers

Prosthesis answered 17/6 at 17:25 Comment(0)
K
-1

In App script always use New deployment to deploy the script.

  • Otherwise it will use old script and you will get CORS error
Koel answered 27/9, 2021 at 9:39 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Ceremony

© 2022 - 2024 — McMap. All rights reserved.