Proper format of a Google Drive API multipart/mixe

2019-04-09 10:36发布

问题:

I am using Javascript (in the Google Apps Script Editor) to make HTTP requests to the Google Drive API.

I've successfully made individual calls to the API, so I know my Oauth 2 works, but now I'm working on making a batch request.

I will loop through each file, and make 2 API calls per file.

My request:

var newOwnerEmail = 'archive@test.com';
var newEditorEmail = 'admin@test.com';
var fileIds = ['a', 'b', 'c'];

const boundary = 'testboundaryname';
const delimiter = "\r\n--" + boundary + "\r\n";
const close_delim = "\r\n--" + boundary + "--";

var multipartRequestBody = '';

for (var i = 0; i < fileIds.length; i++) {

  var fileId = fileIds[i];

  var resource1 = { role: "owner", type: "user", value: newOwnerEmail };

  var requestBody = {};
  requestBody.path = '/drive/v2/files/' + fileId + '/permissions';
  requestBody.method = 'POST';
  requestBody.contentType = 'application/json';
  requestBody.payload = JSON.stringify(resource1);
  requestBody.muteHttpExceptions = true;

  multipartRequestBody += delimiter;
  multipartRequestBody += 'Content-Type: application/json\r\n';
  multipartRequestBody += JSON.stringify(requestBody);

  var resource2 = { role: "writer", type: "user", value: newEditorEmail };

  requestBody.payload = JSON.stringify(resource2);

  multipartRequestBody += delimiter;
  multipartRequestBody += 'Content-Type: application/json\r\n';
  multipartRequestBody += JSON.stringify(requestBody);

}

multipartRequestBody += close_delim;

var batchUrl = 'https://www.googleapis.com/batch';

var batchRequestBody = {};
batchRequestBody.headers = { Authorization: 'Bearer ' + service.getAccessToken() };
batchRequestBody.method = 'POST';
batchRequestBody.contentType = 'multipart/mixed; boundary="' + boundary + '"';
batchRequestBody.body = multipartRequestBody;
batchRequestBody.muteHttpExceptions = true;

try {

  var batchResponse = UrlFetchApp.fetch(batchUrl, batchRequestBody);

  Logger.log(batchResponse);

multipartRequestBody ends up being a string that looks like this:

 --testboundary
 Content-Type: application/json
 {"method":"POST","contentType":"application/json","payload":"{\"role\":\"owner\",\"type\":\"user\",\"value\":\"archive@test.com\"}","muteHttpExceptions":false}
 --testboundary
 Content-Type: application/json
 {"method":"POST","contentType":"application/json","payload":"{\"role\":\"writer\",\"type\":\"user\",\"value\":\"admin@test.com\"}","muteHttpExceptions":false}
 --testboundary
 Content-Type: application/json
 {"method":"POST","contentType":"application/json","payload":"{\"role\":\"owner\",\"type\":\"user\",\"value\":\"archive@test.com\"}","muteHttpExceptions":false}
 --testboundary
 Content-Type: application/json
 {"method":"POST","contentType":"application/json","payload":"{\"role\":\"writer\",\"type\":\"user\",\"value\":\"admin@test.com\"}","muteHttpExceptions":false}
 --testboundary
 Content-Type: application/json
 {"method":"POST","contentType":"application/json","payload":"{\"role\":\"owner\",\"type\":\"user\",\"value\":\"archive@test.com\"}","muteHttpExceptions":false}
 --testboundary
 Content-Type: application/json
 {"method":"POST","contentType":"application/json","payload":"{\"role\":\"writer\",\"type\":\"user\",\"value\":\"admin@test.com\"}","muteHttpExceptions":false}
 --testboundary--

I ran UrlFetchApp.getRequest() to see what was being sent to the server, and it's this:

{
  "headers": {
    "Authorization": "Bearer xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "X-Forwarded-For": “(our external IP address)“
  },
  "method": "post",
  "payload": "\r\n--testboundary\r\nContent-Type: application/json\r\n{\"method\":\"POST\",\"contentType\":\"application/json\",\"payload\":\"{\\\"role\\\":\\\"owner\\\",\\\"type\\\":\\\"user\\\",\\\"value\\\":\\\"archive@test.com\\\"}\",\"muteHttpExceptions\":false}\r\n--testboundary\r\nContent-Type: application/json\r\n{\"method\":\"POST\",\"contentType\":\"application/json\",\"payload\":\"{\\\"role\\\":\\\"writer\\\",\\\"type\\\":\\\"user\\\",\\\"value\\\":\\\"admin@test.com\\\"}\",\"muteHttpExceptions\":false}\r\n--testboundary\r\nContent-Type: application/json\r\n{\"method\":\"POST\",\"contentType\":\"application/json\",\"payload\":\"{\\\"role\\\":\\\"owner\\\",\\\"type\\\":\\\"user\\\",\\\"value\\\":\\\"archive@test.com\\\"}\",\"muteHttpExceptions\":false}\r\n--testboundary\r\nContent-Type: application/json\r\n{\"method\":\"POST\",\"contentType\":\"application/json\",\"payload\":\"{\\\"role\\\":\\\"writer\\\",\\\"type\\\":\\\"user\\\",\\\"value\\\":\\\"admin@test.com\\\"}\",\"muteHttpExceptions\":false}\r\n--testboundary\r\nContent-Type: application/json\r\n{\"method\":\"POST\",\"contentType\":\"application/json\",\"payload\":\"{\\\"role\\\":\\\"owner\\\",\\\"type\\\":\\\"user\\\",\\\"value\\\":\\\"archive@test.com\\\"}\",\"muteHttpExceptions\":false}\r\n--testboundary\r\nContent-Type: application/json\r\n{\"method\":\"POST\",\"contentType\":\"application/json\",\"payload\":\"{\\\"role\\\":\\\"writer\\\",\\\"type\\\":\\\"user\\\",\\\"value\\\":\\\"admin@test.com\\\"}\",\"muteHttpExceptions\":false}\r\n--testboundary\r\nContent-Type: application/json\r\n{\"method\":\"POST\",\"contentType\":\"application/json\",\"payload\":\"{\\\"role\\\":\\\"owner\\\",\\\"type\\\":\\\"user\\\",\\\"value\\\":\\\"archive@test.com\\\"}\",\"muteHttpExceptions\":false}\r\n--testboundary\r\nContent-Type: application/json\r\n{\"method\":\"POST\",\"contentType\":\"application/json\",\"payload\":\"{\\\"role\\\":\\\"writer\\\",\\\"type\\\":\\\"user\\\",\\\"value\\\":\\\"admin@test.com\\\"}\",\"muteHttpExceptions\":false}\r\n--testboundary\r\nContent-Type: application/json\r\n{\"method\":\"POST\",\"contentType\":\"application/json\",\"payload\":\"{\\\"role\\\":\\\"owner\\\",\\\"type\\\":\\\"user\\\",\\\"value\\\":\\\"archive@test.com\\\"}\",\"muteHttpExceptions\":false}\r\n--testboundary\r\nContent-Type: application/json\r\n{\"method\":\"POST\",\"contentType\":\"application/json\",\"payload\":\"{\\\"role\\\":\\\"writer\\\",\\\"type\\\":\\\"user\\\",\\\"value\\\":\\\"admin@test.com\\\"}\",\"muteHttpExceptions\":false}\r\n--testboundary--",
  "followRedirects": true,
  "validateHttpsCertificates": true,
  "useIntranet": false,
  "contentType": "multipart/mixed; boundary=\"testboundary\"",
  "url": "https://www.googleapis.com/batch"
}

I get this response:

Unsupported number format: --

There's definitely something wrong with the content of the request, but it's difficult to determine what it is, given that there's such little documentation for creating batch requests using Javascript.

The only documentation I could find is this, but the example is language-independent/hypothetical.

Then I found this example, which seems to use a Google API client library, which I can't use in Apps Script.