AWS Lambda + API Gateway <GET> の使い方【初心者】と
AWS Lambda + API Gateway <POST> の使い方【初心者】の
GCP Cloud Function版です。
Cloud Functions の作成
AWSの場合、API Gateway と Lambda で WEB API を作成しましたが
GCPの場合、トリガーに「HTTP」を選択することにより、Cloud Functions のみで対応が可能です。
ランタイムは、「Go 1.11」を選択しています。
ポイントは、packageに「main」を指定せずに、「main」関数も指定しません。
以下のソースを「インラインエディタ」で記述します。
実行する関数は、「HelloWorld」です。
package p
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strconv"
)
type Calc struct {
Value1 int `json:"Value1"`
Value2 int `json:"Value2"`
}
type ReturnCalc struct {
Calc // 埋め込み(embedded) 継承的な意味合い
Ans int `json:"Ans"`
}
// メソッド定義
func (c Calc) Add() int {
return c.Value1 + c.Value2
}
func (c *ReturnCalc) Add() {
c.Ans = c.Value1 + c.Value2
}
func HelloWorld(w http.ResponseWriter, r *http.Request) {
// クエリパラメータ取得してみる
var err_flg = 0
var data Calc
if r.Method == http.MethodGet {
for key, values := range r.URL.Query() {
for _, v := range values {
switch key {
case "Value1":
i, err := strconv.Atoi(v)
if err != nil {
fmt.Fprintln(w, "Value1が数値じゃない")
err_flg = 1
break
}
data.Value1 = i
case "Value2":
i, err := strconv.Atoi(v)
if err != nil {
fmt.Fprintln(w, "Value2が数値じゃない")
err_flg = 1
break
}
data.Value2 = i
default:
fmt.Fprintln(w, "パラメータエラー")
err_flg = 1
break
}
}
}
if err_flg == 0 {
fmt.Fprintf(w, "%d + %d = %d \n", data.Value1, data.Value2, data.Add())
} else {
w.WriteHeader(http.StatusInternalServerError)
}
} else if r.Method == http.MethodPost {
// CORS対策
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
// Bodyデータを扱う場合には、事前にパースを行う(POSTの場合、必ず実施する)
r.ParseForm()
if r.Header.Get("Content-Type") != "application/json" {
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintln(w, "json形式じゃない")
return
}
//parse json
var data Calc
jsondata, err := ioutil.ReadAll(r.Body)
// 構造体に格納しているが、 data をmap[string]int や map[string]interfase{}にしても格納可能
err = json.Unmarshal(jsondata, &data)
if err != nil {
fmt.Fprintf(w, "json変換できない %v \n", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
//返信用データ作成
var rdata ReturnCalc
rdata.Value1 = data.Value1
rdata.Value2 = data.Value2
rdata.Add()
v, _ := json.Marshal(rdata)
fmt.Fprint(w, string(v))
} else if r.Method == http.MethodOptions {
// CORS対策
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
}
}
GETの作成
r.Method == http.MethodGet
内がGETの処理です。
以下のページを参考に、URLパラメータを取得します。
golangでURLをパース
POSTの作成
OPTIONSの指定とCORS対策
POSTの場合、AWS Lambda + API Gateway <POST> の使い方【初心者】にあったように
OPTIONS
の指定とCORS対策
が必要です。
POSTすると、r.Method == http.MethodOptions
が先に実行され
r.Method == http.MethodPost
が実行されます。
CORS対策は、以下のヘッダー情報を付加することにより対応できます。
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
r.ParseForm()
POST処理の場合、ParseForm()
でパース処理が必要らしいです。
jsonデータ読み取り・書き込み
ioutil.ReadAll(r.Body)
で、リクエストボディを読み取ることが可能です。
Unmarshal
で、jsonデータをGo言語の構造体などに格納することができます。
Marshal
は、構造体などのデータをjsonデータに変換します。
メソッド
func (c *ReturnCalc) Add() {
c.Ans = c.Value1 + c.Value2
}
上記のメソッドで加算した結果を Ansに格納していますが
以下のようにポインタレシーバを値レシーバのメソッドにした場合、正しい結果になりません。
func (c ReturnCalc) Add() {
c.Ans = c.Value1 + c.Value2
}
ポインタレシーバと値レシーバの違いは、構造体の値の変更が、メソッド呼び出し後にも反映されるか否かです。
関数内で構造体の値を変更する場合には、ポインターメソッドで定義する必要があります。
以下が参考になりました。
[Go] 構造体で、値メソッドとポインタメソッドを使い分ける
テスト
Cloud Functions にもテストはありますが、POSTとして実行されるようです。