LoginSignup
2
3

More than 3 years have passed since last update.

Goでapiを叩く

Posted at

はじめに

最近Nvimを使ってみたんですが、Nvimの設定ファイルであるinit.vimを編集するのに使い慣れているvsCodeVimを使いました。いつかNvimを使いこなしたいですまる
さて、バックエンドの勉強をしたくて最近Go言語に入門してみました。購入したGOの書籍を読み終えて何か書きたくなったところで、JavascriptでやってたことをGoではどうやっているのか確かめてみようと思いました。
ということでGoで簡単なApiを叩いてみました。

書いてあること

  • Unmarshalでjsonを解析する方法
  • encoderで解析する方法

叩くApi

定番のOpen Weather Map です。
まず、どんなJsonが返ってくるか確認します。

curl http://api.openweathermap.org/data/2.5/weather?q=nagano -H "X-API-KEY: YOUR API KEY" | jq

こんなん↓が返ってくると思います。

{
  "coord": {
    "lon": 138.1811,
    "lat": 36.6514
  },
  "weather": [
    {
      "id": 804,
      "main": "Clouds",
      "description": "overcast clouds",
      "icon": "04n"
    }
  ],
  "base": "stations",
  "main": {
    "temp": 276.93,
    "feels_like": 274.22,
    "temp_min": 275.37,
    "temp_max": 278.15,
    "pressure": 1024,
    "humidity": 67
  },
  "visibility": 10000,
  "wind": {
    "speed": 0.69,
    "deg": 182
  },
  "clouds": {
    "all": 100
  },
  "dt": 1613130091,
  "sys": {
    "type": 3,
    "id": 19237,
    "country": "JP",
    "sunrise": 1613079554,
    "sunset": 1613118238
  },
  "timezone": 32400,
  "id": 1856215,
  "name": "Nagano",
  "cod": 200
}

Unmarshalを使う

まずは全体のコードをぱっとみ↓

package main

import (
    "encoding/json"
    "flag"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
)

var (
    Key string
)

// 構造体のタグによりjsonと結びつける
type Data struct {
    Weather []Weather `json:"weather"`
    Main    Main      `json:"main"`
}

type Weather struct {
    ID          int    `json:"id"`
    Main        string `json:"main"`
    Description string `json:"description"`
    ICON        string `json:"icon"`
}

type Main struct {
    Temp      float32 `json:"temp"`
    FeelsLike float32 `json:"feels_like"`
    TempMin   float32 `json:"temp_min"`
    TempMax   float32 `json:"temp_max"`
    Pressure  int     `json:"pressure"`
    Humidity  int     `json:"humidity"`
}

// コマンドラインの引数を取得
func init() {
    flag.StringVar(&Key, "key", "", "Api key")
    flag.Parse()
}

func main() {
    url := fmt.Sprintf("http://api.openweathermap.org/data/2.5/weather?q=nagano&appid=%s", Key)
    resp, err := http.Get(url)
    if err != nil {
        log.Fatal(err)
    }

    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        log.Fatal(err)
    }

    var data Data

    if err := json.Unmarshal(body, &data); err != nil {
        log.Fatal(err)
    }

    fmt.Println(data)
}

全体の流れはほしいjsonデータに対応する構造体をつくり、その構造体にUnmarshalやdecoderで解析した値を入れていきます。
今回はさきほど返ってきたjsonのweatherとmainの値がほしいとします。

{
  "weather": [
    {
      "id": 804,
      "main": "Clouds",
      "description": "overcast clouds",
      "icon": "04n"
    }
  ],
  "main": {
    "temp": 276.93,
    "feels_like": 274.22,
    "temp_min": 275.37,
    "temp_max": 278.15,
    "pressure": 1024,
    "humidity": 67
  },
}

このjsonをGOの構造体で表現しタグで結びつけます。

type Data struct {
    Weather []Weather `json:"weather"`
    Main    Main      `json:"main"`
}

type Weather struct {
    ID          int    `json:"id"`
    Main        string `json:"main"`
    Description string `json:"description"`
    ICON        string `json:"icon"`
}

type Main struct {
    Temp      float32 `json:"temp"`
    FeelsLike float32 `json:"feels_like"`
    TempMin   float32 `json:"temp_min"`
    TempMax   float32 `json:"temp_max"`
    Pressure  int     `json:"pressure"`
    Humidity  int     `json:"humidity"`
}

あとはUnmarshalに渡すだけです。Marshalは構築するみたいな意味があり、その反対のUnmershalはjsonを崩すみたいなイメージを勝手に持っています。jsonのMarshalメソッドは構造体からjsonを構築するときに使います。(余談ですがMarshalという名前の微妙な性能を持った武器がバロラントというゲームに存在していて何か変な親近感を持ってます)

var data Data

if err := json.Unmarshal(body, &data); err != nil {
    log.Fatal(err)
}

コードを実行

go run main.go -key OpenWeatherMapのApiKey

こんなんが返ってくれば成功です。

{[{804 Clouds overcast clouds 04n}] {276.93 274.22 275.37 278.15 1024 67}}

decoderを使う

package main

import (
    "encoding/json"
    "flag"
    "fmt"
    "io"
    "net/http"
)

var (
    Key string
)

type Data struct {
    Weather []Weather `json:"weather"`
    Main    Main      `json:"main"`
}

type Weather struct {
    ID          int    `json:"id"`
    Main        string `json:"main"`
    Description string `json:"description"`
    ICON        string `json:"icon"`
}

type Main struct {
    Temp      float32 `json:"temp"`
    FeelsLike float32 `json:"feels_like"`
    TempMin   float32 `json:"temp_min"`
    TempMax   float32 `json:"temp_max"`
    Pressure  int     `json:"pressure"`
    Humidity  int     `json:"humidity"`
}

func init() {
    flag.StringVar(&Key, "key", "", "Api key")
    flag.Parse()
}

func main() {
    url := fmt.Sprintf("http://api.openweathermap.org/data/2.5/weather?q=nagano&appid=%s", Key)
    resp, err := http.Get(url)
    if err != nil {
        fmt.Println("Error get api:", err)
        return
    }

    defer resp.Body.Close()

       // デコーダ作成
    decoder := json.NewDecoder(resp.Body)
    for {
        var data Data
        err := decoder.Decode(&data)
        if err == io.EOF {
            break
        }
        if err != nil {
            fmt.Println("Error decode json:", err)
            return
        }
        fmt.Println(data)
    }
}

decoderを作成してそれによってjsonを一行ずつ解析しています。
jsonの最後の行が解析できたらdecoderがio.EOFを返すので、それが返されたら無限ループから抜け出しています。

構造体を使ったプログラミングはほぼ初めてだったので新鮮ですごく楽しいと感じました。
また、少しGoを触ってみてjavascriptとCの中間みたいな書きごごちだと感じました。javascriptを触ったことがある方はきっとGoも楽しめると思うのでぜひ!!

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3