LoginSignup
14
13

More than 5 years have passed since last update.

Git.io 短縮 URL を golang コードで取得してみる

Last updated at Posted at 2015-08-08

Git.io で短縮 URL が取得できるらしい

これを読んでたら後ろの方に Git.io の話が出ていた。このサイトで短縮 URL を生成できるらしい。

API が curl で掲載されていて,例えば私の https://github.com/spiegel-im-spiegel なら

$ curl -i "http://git.io" -F "url=https://github.com/spiegel-im-spiegel"
HTTP/1.1 201 Created
Server: Cowboy
Connection: keep-alive
Date: Sat, 08 Aug 2015 02:42:16 GMT
Status: 201 Created
Content-Type: text/html;charset=utf-8
Location: http://git.io/vOj52
Content-Length: 37
X-Xss-Protection: 1; mode=block
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-Runtime: 0.210952
X-Node: 871d903e-a8e0-46ff-a96f-ef424385e5ed
X-Revision: b1d9ce07ccb700fc90398edafd397beb8d3bd772
Via: 1.1 vegur

https://github.com/spiegel-im-spiegel

てな感じで,ヘッダの Location 要素に短縮 URL が返ってくる仕組みらしい。

って,これって curl で書けるんなら golang で表現できるんじゃね?

cURL as DSL

cURL as DSL とは curl の構文を任意のコード(今のところは golang, Python, PHP, JavaScript, Java, Objective-C, Vim Script)に変換してくれるもので,どういうことかというと「Web API は curl で表現すればいいんじゃね?」ということらしい。

さっそく curl を golang に変換してみる

では早速,上述の curl コマンドを golang に変換してみる(ちなみに -i は付けない)。こんな感じ。

gitio0.go
package main

import (
    "bytes"
    "io/ioutil"
    "log"
    "mime/multipart"
    "net/http"
)

func main() {
    var buffer bytes.Buffer
    writer := multipart.NewWriter(&buffer)
    writer.WriteField("url", "https://github.com/spiegel-im-spiegel")

    resp, err := http.Post("http://git.io", "multipart/form-data; boundary="+writer.Boundary(), &buffer)
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        log.Fatal(err)
    }
    log.Print(string(body))
}

まずは何も考えずに動かしてみる。

C:>go run gitio0.go
2015/08/08 12:00:00 Invalid url:

あれ? おかしいなぁ。ん~,じゃあちょっと出力をいじって...

gitio1.go
package main

import (
    "bytes"
    "io/ioutil"
    "log"
    "mime/multipart"
    "net/http"
)

func main() {
    var buffer bytes.Buffer
    writer := multipart.NewWriter(&buffer)
    writer.WriteField("url", "https://github.com/spiegel-im-spiegel")

    resp, err := http.Post("http://git.io", "multipart/form-data; boundary="+writer.Boundary(), &buffer)
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        log.Fatal(err)
    }
    log.Println("  Status: ", resp.Header.Get("Status"))
    log.Println("Location: ", resp.Header.Get("Location"))
    log.Println("    Body: ", string(body))
}
C:>go run gitio1.go
2015/08/08 12:00:00   Status: 422 Unprocessable Entity
2015/08/08 12:00:00 Location:
2015/08/08 12:00:00     Body: Invalid url:

ん? あっそうか。 url がちゃんと渡ってないんだ。ひとしきり悩んだ後,以下のようにすればいいことに気がついた。

gitio2.go
package main

import (
    "bytes"
    "io/ioutil"
    "log"
    "mime/multipart"
    "net/http"
)

func main() {
    var buffer bytes.Buffer
    writer := multipart.NewWriter(&buffer)
    writer.WriteField("url", "https://github.com/spiegel-im-spiegel")
    writer.Close() //writer はちゃんと閉じましょう

    resp, err := http.Post("http://git.io", "multipart/form-data; boundary="+writer.Boundary(), &buffer)
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        log.Fatal(err)
    }
    log.Println("  Status: ", resp.Header.Get("Status"))
    log.Println("Location: ", resp.Header.Get("Location"))
    log.Println("    Body: ", string(body))
}

多分 buffer に flush されてなかったんだね(この問題は修正されたようです)。これで

C:>go run gitio2.go
2015/08/08 12:00:00   Status: 201 Created
2015/08/08 12:00:00 Location: http://git.io/vOj52
2015/08/08 12:00:00     Body: https://github.com/spiegel-im-spiegel

となり,めでたく短縮 URL が取得できた。

ちなみに,これには http.PostForm 関数を使った別解がある。最初の curl の式の -F-d にかえればいいらしい。結果は以下の通り

gitio0b.go
package main

import (
    "io/ioutil"
    "log"
    "net/http"
    "net/url"
)

func main() {
    values := url.Values{
        "url": {"https://github.com/spiegel-im-spiegel"},
    }

    resp, err := http.PostForm("http://git.io", values)
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        log.Fatal(err)
    }
    log.Print(string(body))
}

今回に限れば,こちらのほうがコードがスッキリして分かりやすい。

では,楽しい夏休みを!

14
13
1

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
14
13