記事一覧に戻る

GoでWEB APIを実行する方法

Goについて

Googleによって作られた静的型付け言語です。公式ドキュメントをGoogle翻訳にかけると、「囲碁」と翻訳される箇所があり、AIはまだ進化の過程であることを実感することができます。

2009年に出来た言語とのことで、かなり若いですね。コンパイルが必要ですが、runコマンドでスクリプトのように実行することが可能で、JSとかpythonのようなスクリプト言語に慣れている方でも違和感なく利用できると思います。

DockerがGoで書かれていることは有名かと思います。もともとはモダンなCだかC++を目指して作られたようですが、マイクロサービスの世界で良く採用されるようです。

私とGo

仮想通貨のbotを昨年から作成しています。もともとはpythonで作っていたのですが、botを稼働させておくと、しばしばエラー落ちして損失を被ることがありました。エラーのほとんどが、型不一致のエラーでした。

こういったランタイムエラーを減らしたいという思いから、コンパイルが必要な静的型付け言語の採用を検討しました。後は、何を採用するか、、、もともとJSとpythonしか書けません。Javaはパブリックスタティックボイドメインファイナルフラッシュが面倒そうだし、C御三家は難しそうだし、、、と色々悩みました。どうせなら若い言語にしようと、GoとRustに絞り込み、最終的にはマスコットのGopher君とFerris君どっちがかわいいか?で決めました。

Gopher君

goher

Ferris君

ferris

一目瞭然ですね。しかしRustは最近Linuxのカーネルに採用されるなど活躍目覚ましいようです。

Webリクエストを実行する方法

前置きが長くなりました。pythonならRequestsパッケージ、Node.jsならaxios等、外部パッケージを使うことが多いと思います。Goは標準パッケージだけでいけます。

ヘッダ指定が不要の場合

非情にシンプルです。エラー処理は冗長になるので省いています。

import "net/http"

func main(){
    getRes,_ := http.Get("https://example.com")
    postRes,_:= http.Post("https://example.com")
}

これだけ。 簡単~~。

ヘッダ指定が必要な場合

大抵のリクエストはContent-Typeとか指定されるので、ほとんどがこちらのケースかと思います。ヘッダを指定する場合、少し手数が増えます。

import "net/http"

funct main(){
    // httpのclient
    client := &http.Client{}
    // Requestを生成。第三引数はrequestのbody部。GETではbodyは無いのでnil。
    req, _ := http.NewRequest("GET","https://example.com",nil)
    // ヘッダをセット。
    req.Header.Set("Some-Prop","Some-Value")
    // API実行
    res, _ := client.Do(req)
}
  • httpを実行するための構造体、http.Clientを初期化
  • Requestを生成
  • Requestにヘッダ付与
  • DoメソッドでAPIを実行

という流れになります。

Postの場合も流れは同じです。bodyにはjson形式のデータを入れる場合を考えてみます。

import (
    "encoding/json"
    "net/http"
    "strings"
)

func main(){
    client := &http.Client{}
    // body部分のデータ
    data := map[string]string {"someKey":"someValue"}
    // jsonに変換
    body, _ := json.Marshal(data)
    // 第一引数がPOSTになる
    req, _ := http.NewRequest("POST","https://example.com",strings.NewReader(string(body)))
    // ヘッダをセット。
    req.Header.Set("Content-Type","application/json")
    // API実行
    res,_ := client.Do(req)
}

違いはRequestの第一引数が"POST"になっているのと、第三引数にRequestのbody部分を指定しているくらいです。body部分をjsonにするため、"encoding/json"のMarshal関数を使っています。NewRequestの第三引数はio.Reader型なので、[]byte型のbodyを文字列に変換し、文字列からio.Reader型に変換しています。

APIのレスポンスを使う

APIを実行するだけなら前述のとおりです。ただ、これだけで終わるケースは少ないでしょう。返ってきたレスポンスをプログラムの中で使いたいことがほとんどでしょう。

import (
    "encoding/json"
    "net/http"
    "io/ioutil"
)

func main(){
   // ~~上のPOST例の続きとします。
   // レスポンスのボディはjson形式とします。
   res,_ := client.Do(req)
   // 関数抜けると置きにres.Bodyは閉じる
   defer res.Body.Close()
   // レスポンスのbody部を読み取る。byte型の配列が返ってくる。
   b, _ := ioutil.ReadAll(res.Body)
   // 読み取ったjson文字列をパース。
   // 便宜敵にinterface{}にマッピングしていますが、
   // レスポンスの型に応じた構造体を作り、そのポインタを渡すのが良いと思います。
   var i interface{}
   json.Unmarshal(b,&i)
   fmt.Println(i)
}

上の例では便宜上json形式のレスポンスをinterface{} iにぶち込んでいます。実際には レスポンスの型に応じた構造体を定義し、そのポインタを渡すのが良いかと思います。

type PostRes struct {
    Time    string `json:"time"`
    Weather string `json:"weather"`
}

func main(){
    // ~~~~
    data := &PostRes{}
    json.Unmarshal(b,data)
    // jsonのtime -> data.Time、weather -> data.Weatherに設定され、以下のように利用できます。
    fmt.Println(data.Time,data.weather)
}

最後に

APIを実行するだけならこんな感じです。レスポンスの型を定義していくのは面倒ですが、、、 Goは汎用言語ですが、もっぱらサーバサイドで使われているようで、webのAPIの実行にはあまり使われないのかもしれません。 でも案外手軽に実行できるので、興味を負った方はお試しあれ。

記事一覧に戻る