doGet
, doPost
をContent-Type:x-www-form-urlencoded
でリクエストすればいい。
前回
今回
CORSエラーを回避する。
情報源
- https://knmts.com/become-engineer-31/
- https://qiita.com/masakielastic/items/70516e074eadf2ce09dd
- https://qiita.com/att55/items/04e8080d1c441837ad42
CORSエラーの原因はContent-Type:application/json
のせいだった。これはGASサーバの仕様と思われる。それに合わせてx-www-form-urlencoded
でリクエストする。GAS側もそれにあわせて引数を受け取るように書く。
工程
- Googleアカウントを作成する
- Google Driveでスプレッドシートを作成する
- スプレッドシートのメニュー→
拡張機能
→Apps Script
をクリックする - 後述するGASコードを入力する
- デプロイする
- デプロイしたIDやURLを使ってリクエストする
GAS コード
doGet
GETリクエストされたときの処理。スプレッドシートに登録されたデータをJSONで返す。
もし引数にaddress
があれば、そのアドレスのプロフィール情報だけを返す。もし引数にaddress
がなければ、全件返す。
function doGet(e) {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName('list');
const sheetData = sheet.getRange('A2:D' + sheet.getLastRow()).getValues();
const address = e.parameter.address;
if (address) { // 引数にアドレスが存在するなら
const addrs = sheet.getRange('A2:A' + sheet.getLastRow()).getValues();
const targetIdRow = addrs.findIndex(addr=>addr[0]==address);
if (0 <= targetIdRow) { // 一致するアドレスが既存ならそのレコードだけ返す
const i = targetIdRow + 2
const r = sheet.getRange(`A${i}:D${i}`).getValues()[0];
return ContentService.createTextOutput(JSON.stringify({address:r[0], profile:r[1], created:r[2], updated:r[3]})).setMimeType(ContentService.MimeType.JSON);
} else { return ContentService.createTextOutput(JSON.stringify({error:'指定したアドレスの情報は存在しません。'})).setMimeType(ContentService.MimeType.JSON); }
}
const responseList = [];
sheetData.map(function(d) {
responseList.push({address:d[0], profile:d[1], created:d[2], updated:d[3]});
});
return ContentService.createTextOutput(JSON.stringify(responseList)).setMimeType(ContentService.MimeType.JSON);
}
doPost
POSTリクエストされたときの処理。渡されたアドレスとプロフィール情報をスプレッドシートに登録する。すでに存在するなら上書きする。
function doPost(e) {
console.log(e);
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName('list');
requiredParameterError(e, 'address')
requiredParameterError(e, 'profile')
const address = e.parameter.address;
const profile = e.parameter.profile;
const addrs = sheet.getRange('A2:A' + sheet.getLastRow()).getValues();
let targetIdRow = addrs.findIndex(addr=>addr[0]==address);
const now = new Date().toISOString();
if (0 <= targetIdRow) { // 一致するアドレスが既存なら上書きする
targetIdRow += 2;
sheet.getRange(targetIdRow, 2).setValue(profile);
sheet.getRange(targetIdRow, 4).setValue(now);
} else { // 一致するアドレスが未存なら新規追加する
sheet.appendRow([address, profile, now, now]);
}
return ContentService.createTextOutput(JSON.stringify({address:address, profile:profile})).setMimeType(ContentService.MimeType.JSON);
}
function requiredParameterError(e, name) {
if (!e.parameter[name]) {
return ContentService.createTextOutput(JSON.stringify({error:`必須パラメータ${name}が存在しません。`})).setMimeType(ContentService.MimeType.JSON);
}
}
リクエストする
GASをリクエストする方法も書いておく。
リクエストする(curl編)
実際にリクエストされると好き勝手に改変できてしまうため、ID値などを伏せておく。コードの中には書いてあるので意味はないかもしれないが。
こうすればcurl
でリクエストできる、という技術情報メモとして書き残しておく。
GET
ID=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
URL="https://script.google.com/macros/s/${ID}/exec"
curl -L "$URL"
POST
ID=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
URL="https://script.google.com/macros/s/${ID}/exec"
ADDRESS=MEHCqJbgiNERCH3bRAtNSSD9uxPViEX1nu
PROFILE='{\"name\":\"ytyaru\",\"url\":\"https://ytyaru.github.io/\",\"avatar\":\"https://s3.arkjp.net/misskey/thumbnail-4820c61e-3194-48a6-b3a7-7d7319a67966.png\",\"mastodon\":{\"mstdn.jp\":[{\"id\":\"233143\",\"username\":\"ytyaru\"}]},\"misskey\":{\"misskey.io\":[{\"id\":\"918va5mxda\",\"username\":\"ytyaru\"}]}}'
JSON='{"address":"'"$ADDRESS"'","profile":"'"$PROFILE"'"}'
curl -H 'Accept: application/json' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d "address=$ADDRESS" \
-d "profile=$JSON" \
-L "$URL"
ポイントは2つ。
Content-Type: application/x-www-form-urlencoded
(application/json
でなく)-d
で引数を渡す(-F
でなく)
リクエストする(JavaScript編)
GET
async get(url, headers) {
const data = {
method: 'GET',
headers: this.getJsonHeaders(headers),
}
const res = await fetch(url, data)
const json = await res.json()
return json
}
getDefaultJsonHeaders() { return {
'Accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded',
}}
POST
async post(address, profile) {
const data = {}
data.method = 'POST'
data.headers = {
'Accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded',
}
const params = new URLSearchParams();
params.append('address', address)
params.append('profile', JSON.stringify(profile))
data.body = params;
const res = await fetch(this.#getUrl(), data).catch(e=>console.error(e))
const json = await res.json()
return json
}
ポイントは2つ。
Content-Type: application/x-www-form-urlencoded
(application/json
でなく)body
に渡す引数はURLSearchParams
型である(address=...&profile=...
のような文字列でなく)