Understanding Flow of Request to Web Apps Created by Google Apps Script

Gists

Here, I would like to introduce a report for understanding the flow of the request to Web Apps created by Google Apps Script. There might be a case that various applications using the Web Apps are created and the Web Apps are used as the webhook. In that case, it is considered that when you have understood the flow of requests to the Web Apps, your goal might be able to be smoothly achieved. In this report, I would like to introduce the information about it.

Sample situation

As a sample situation, the sample script for the Web Apps is as follows. And, please set a Spreadsheet ID to work_. In this sample, the data from the request is put into the Spreadsheet. And, the event object is directly returned from the Web Apps.

function work_(e, method) {
  e[method] = method;
  const res = JSON.stringify(e);
  SpreadsheetApp.openById("###SpreadsheetId###").appendRow([new Date(), res]);
  return ContentService.createTextOutput(res);
}

const doGet = (e) => work_(e, "GET method");
const doPost = (e) => work_(e, "POST method");

And, please deploy this script as the Web Apps with the following condition.

  • Execute as: Me
  • Who has access to the app: Anyone

Please copy and paste your Web Apps URL like https://script.google.com/macros/s/{deploymentId}/exec. By this, this Web Apps can be accessed without the access token. This is a simple sample Web Apps.

Request to Web Apps

1. Simple curl command by manual operation

Here, in order to understand the flow of requests to the Web Apps, it tests to request the Web Apps using a simple curl command.

When it requests this Web Apps using curl command with the GET and POST methods, those are as follows.

curl -I -XGET "https://script.google.com/macros/s/{deploymentId}/exec"
curl -I -XPOST "https://script.google.com/macros/s/{deploymentId}/exec"

The status code 302 is returned. In this case, it indicates the redirect. And, the response header has the “location” property including the redirect URL. From this result, it is found that in the case of Web Apps, it is required to run the redirect.

When this redirect process is manually run, it becomes as follows.

For doGet

  1. Request with the GET method to the Web Apps URL like https://script.google.com/macros/s/{deploymentId}/exec.
curl -I -XGET "https://script.google.com/macros/s/{deploymentId}/exec"
  1. Retrieve the URL of “location” from the response header of the above request. It’s like https://script.googleusercontent.com/macros/echo?user_content_key=###id1###&lib=###id2###.

  2. Request the redirect URL with the GET method.

curl -XGET "https://script.googleusercontent.com/macros/echo?user_content_key=###id1###&lib=###id2###"

By this flow, the following value is returned from Web Apps.

{
  "contentLength": -1,
  "queryString": "",
  "contextPath": "",
  "parameters": {},
  "parameter": {},
  "method": "GET method"
}

For doPost

  1. Request with POST method to the Web Apps URL like https://script.google.com/macros/s/{deploymentId}/exec.
curl -I -XPOST "https://script.google.com/macros/s/{deploymentId}/exec"
  1. Retrieve the URL of “location” from the response header of the above request. It’s like https://script.googleusercontent.com/macros/echo?user_content_key=###id1###&lib=###id2###.

  2. Request the redirect URL with the POST method.

curl -XPOST "https://script.googleusercontent.com/macros/echo?user_content_key=###id1###&lib=###id2###"
- In this case, an error like "Page Not Found" (status code 405) occurs.
  1. Request the redirect URL with the GET method.
curl -XGET "https://script.googleusercontent.com/macros/echo?user_content_key=###id1###&lib=###id2###"
  • In this case, no error occurs. The correct value is returned from the Web Apps.

By this flow, the following value is returned from Web Apps.

{
  "queryString": "",
  "parameters": {},
  "parameter": {},
  "contextPath": "",
  "contentLength": 0,
  "method": "POST method"
}

From these 2 patterns, it is found that in the case of the Web Apps, it is required to request with the GET method to the redirect URL for both doGet and doPost.

2. Send values using the curl command by manual operation

Here, it tests sending values to the Web Apps using a curl command.

For doGet

  1. Request with the GET method to the Web Apps URL like https://script.google.com/macros/s/{deploymentId}/exec. In this case, the value is included in the query parameter.
curl -I -XGET "https://script.google.com/macros/s/{deploymentId}/exec?key1=value1"
  1. Retrieve the URL of “location” from the response header of the above request. It’s like https://script.googleusercontent.com/macros/echo?user_content_key=###id1###&lib=###id2###.

    • Here, the most important point is that at the 1st request before the redirect process, the value of key1=value1 has already been sent to the Web Apps. So, in this time, the values are put into the sheet with SpreadsheetApp.openById("###").appendRow([new Date(), res]).
  2. Request the redirect URL with the GET method.

curl -XGET "https://script.googleusercontent.com/macros/echo?user_content_key=###id1###&lib=###id2###"

By this flow, the following value is returned from Web Apps. You can see that the returned values include the sending values.

{
  "contextPath": "",
  "queryString": "key1=value1",
  "parameter": { "key1": "value1" },
  "contentLength": -1,
  "parameters": { "key1": ["value1"] },
  "method": "GET method"
}

For doPost

  1. Request with POST method to the Web Apps URL like https://script.google.com/macros/s/{deploymentId}/exec. In this case, the values are included in both the query parameter and the request body.
curl -vso /dev/null -XPOST -d "key2=value" "https://script.google.com/macros/s/{deploymentId}/exec?key1=value1"
  1. Retrieve the URL of “location” from the response header of the above request. It’s like https://script.googleusercontent.com/macros/echo?user_content_key=###id1###&lib=###id2###.

    • Here, the most important point is that at the 1st request before the redirect process, the values of key1=value1 and key2=value have already been sent to the Web Apps. So, this time, the values are put into the sheet with SpreadsheetApp.openById("###").appendRow([new Date(), res]).
  2. Request the redirect URL with the GET method.

curl -XGET "https://script.googleusercontent.com/macros/echo?user_content_key=###id1###&lib=###id2###"

By this flow, the following value is returned from Web Apps. You can see that the returned values include the sending values.

{
  "postData": {
    "contents": "key2=value",
    "length": 10,
    "name": "postData",
    "type": "application/x-www-form-urlencoded"
  },
  "contextPath": "",
  "queryString": "key1=value1",
  "contentLength": 10,
  "parameter": { "key2": "value", "key1": "value1" },
  "parameters": { "key1": ["value1"], "key2": ["value"] },
  "method": "POST method"
}

4. Send values using the curl command by one command

When --location or -L is used with the curl command, the Web Apps can be requested by one command. Ref

For doGet

curl -L -XGET "https://script.google.com/macros/s/{deploymentId}/exec?key1=value1"

For doPost

curl -L -d "key2=value" "https://script.google.com/macros/s/{deploymentId}/exec?key1=value1"
  • Here, when -XPOST and -L are used, the request process is as follows. By this, the response values from Web Apps are not returned while the values are correctly sent to the Web Apps.

    1. Request the Web Apps URL with the POST method.
    2. Request the redirect URL with the POST method.
  • On the other hand, when -d and -L are used, the request process is as follows. By this, the response values from Web Apps are correctly returned, and also the values are correctly sent to the Web Apps.

    1. Request the Web Apps URL with the POST method.
    2. Request the redirect URL with the GET method.
  • The values, which you want to send to the Web Apps, are required to be included in the 1st request to the Web Apps URL. It is not required to include the values in the 2nd request to the redirect URL.

Summary

From the experiment result, it was found the following important result.

  1. In the case of a request to doGet, the following 2 requests are required to be run.

    1. Request the Web Apps URL with the GET method.
    2. Request the redirect URL with the GET method.
  2. In the case of a request to doPost, the following 2 requests are required to be run.

    1. Request the Web Apps URL with the POST method.
    2. Request the redirect URL with the GET method.
  3. For example, when in your situation of the POST method, when the values can be correctly sent to the Web Apps and the returned values are not correctly returned from the Web Apps, the request for the redirect might be the POST method. In that case, please change the request for the redirect URL from the POST method to the GET method. If your script cannot be directly changed, please try to do the above process with a script or a manual operation.

References

Appendix

As an appendix, I would like to introduce the sample scripts of Google Apps Script for the above flow.

for doGet

function requestToDoGet() {
  const webAppsUrl = "https://script.google.com/macros/s/{deploymentId}/exec";

  // 1st request.
  const res1 = UrlFetchApp.fetch(webAppsUrl + "?key=value", {
    method: "get",
    followRedirects: false,
  });
  const headers = res1.getAllHeaders();
  console.log(res1.getContentText()); // Here, HTML data including the redirect URL is returned. And, the query parameter of "key=value" is sent.
  const location = headers["Location"];

  // 2nd request.
  const res2 = UrlFetchApp.fetch(location, {
    method: "get",
    followRedirects: false,
  });
  console.log(res2.getContentText()); // Here, the values are returned from `doPost`.
}

for doPost

function requestToDoPost() {
  const webAppsUrl = "https://script.google.com/macros/s/{deploymentId}/exec";

  // 1st request.
  const res1 = UrlFetchApp.fetch(webAppsUrl, {
    method: "post",
    payload: "sample",
    followRedirects: false,
  });
  const headers = res1.getAllHeaders();
  console.log(res1.getContentText()); // Here, HTML data including the redirect URL is returned. And, the request body of "sample" is sent.
  const location = headers["Location"];

  // 2nd request.
  const res2 = UrlFetchApp.fetch(location, {
    method: "get",
    followRedirects: false,
  });
  console.log(res2.getContentText()); // Here, the values are returned from `doPost`.
}

 Share!