Go速習会

この投稿は 2015/10/16に[Wantedly](https://www.wantedly.com/)で行われた[Go速習会](http://wantedly.connpass.com/event/21314/)の内容です。速習会は事前知識ほぼ0を仮定して、あるテーマにおける知見を社内全体+少数の外部の参加者に広めるという公開社内勉強会です。=> 前回の[Sketch速習会の様子](http://engineer.wantedly.com/2015/10/13/learn-sketch.html)
今回は、Goで簡単なAPIサーバーを作ってみるというのをゴールにし、環境セットアップの部分は[Go言語の開発環境セットアップ](http://qiita.com/awakia/items/7bf03fd96a74502073b8)の投稿を読んで事前にやっておいて頂きました。

当日はLive Coding形式で発表しました。各項目をCommitのDiffでみたい方は https://github.com/awakia/go_sokushu を参照ください。多少会話の流れで変わっていますが、だいたいここに書いてある内容と同じです。

## この速習会でできるようになること

Go言語で簡単なAPIサーバーが書けるようになります。

- 静的ファイル配信 (APIから少し脱線するけど)
- JSON API配信
- DBアクセス

など

## 基本

実行の仕方

```
go run server.go
```

もしくは

```
go build server.go
./server
```

## Hello world

```go:server.go
package main

import "fmt"

func main() {
	fmt.Println("hello world")
}
```	

- go runで、`func main()`が実行される
- `package main`にする。
- ファイル名はなんでも良い
- goでインポートできる標準パッケージ一覧: https://golang.org/pkg/

## Hello server

```go:server.go
package main

import (
	"fmt"
	"net/http"
)

func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		fmt.Fprintln(w, "hello world")
	})
	http.ListenAndServe(":8080", nil)
}
```

- https://golang.org/pkg/net/http/
- ポートはどこでもいいけど、Goは`8080`がお好きな模様

## Hello server with servemux

```go:server.go
package main

import (
	"fmt"
	"net/http"
)

func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		fmt.Fprintln(w, "hello world")
	})
	http.ListenAndServe(":8080", mux)
}
```

- DefaultServeMuxとか所々に書いてあるはず
 - https://golang.org/pkg/net/http/#ListenAndServe
- ServeMux = HTTP request multiplexer
 - https://golang.org/pkg/net/http/#SurveMux
- 複数マッチする場合はマッチが長いほうが優先される
 - e.g "/images/thumbnails/", "/images/", "/" という順
 - つまり、"/"には全てがマッチする

試しに、`http://localhost:8080/hoge`にアクセスしてみよう！

## Hello negroni

https://github.com/codegangsta/negroni

```bash
$ go get github.com/codegangsta/negroni
```

```go:server.go
package main

import (
	"fmt"
	"net/http"

	"github.com/codegangsta/negroni"
)

func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		fmt.Fprintln(w, "hello world")
	})
	n := negroni.Classic()
	n.UseHandler(mux)
	n.Run(":8080")
}
```

- Negroni は Martini という人気を集めたライブラリの作者が、MartiniはGoぽくないとかいう批判を受け作ったミドルウェア的な立ち位置
- Rubyで言えば、MartiniがSinatra、NegroniはRackといったところか
- Martiniは、中で何をやっているかわかりづらくGoの割には遅いとか問題があった模様。Goではこういった物のほうが受け入れられやすい
- [Go言語でServer作る時に必要な知識メモ](http://qiita.com/awakia/items/ef8978fc33ffb1d27460) にもう少し詳しくメモっておいた

## Hello static assets

コードはそのまま

https://github.com/codegangsta/negroni

>
##negroni.Classic()
>
negroni.Classic() provides some default middleware that is useful for most applications:
>
- negroni.Recovery - Panic Recovery Middleware.
- negroni.Logging - Request/Response Logging Middleware.
- negroni.Static - Static File serving under the "public" directory.
>
This makes it really easy to get started with some useful features from Negroni.

つまり`public`ディレクトリを作ったら、その構造通りに配布してくれる

```html:public/index.html
<html>
<head>
  <title>hello go</title>
</head>
<body>
  <h1>
    hoge
  </h1>
</body>
</html>
```

- `http://localhost:8080/`で`index.html`の内容がRenderされる
- 今後`HundleFunc`で設定した`/`を確かめたいときは`/`は何にでもマッチするので、`http://localhost:8080/hoge`など適当なURLにアクセス
 - または、この`index.html`を`test.html`などに変えておきましょう！
 - 詳しくはコメント参照

## Hello struct

```go:models/user.go
package main

import "fmt"

type User struct {
	ID   int
	Name string
	Age  int
}

func main() {
	user := User{
		1,
		"Naoyoshi Aikawa",
		29,
	}
	fmt.Println(user)
}
```

```bash
$ go run models/user.go
{1 Naoyoshi Aikawa 29}
```

- とりあえず、`package main`で`func main()`ありで作っておく
- `type <名前> <型>`で新しい方が作る
 - e.g. `type StatusCode int`
- 今回はstruct型にUserという名前をつけたという位置づけになる

## Hello object-oriented

```go:models/user.go
package main

import (
	"fmt"
	"strconv"
)

type User struct {
	ID   int
	Name string
	Age  int
}

func NewUser(name string, age int) *User {
	return &User{
		Name: name,
		Age:  age,
	}
}

func (u *User) String() string {
	return u.Name + "(" + strconv.Itoa(u.Age) + ")"
}

func main() {
	user := NewUser("Naoyoshi Aikawa", 29)
	fmt.Println(user)
}
```

```bash
go run models/user.go
Naoyoshi Aikawa(29)
```

いろいろ盛り込みました

- NewXxxでXxx型のコンストラクタ的なものを作るのがイディオム
- `func (自分の型) (引数) 返値` でインスタンスメソッドが作れる
- こういうstructは一般的にポインタ型で返しておくと良い

## Hello package

```go:models/user.go
package models

import "strconv"

// User defines an user
type User struct {
	ID   int
	Name string
	Age  int
}

// NewUser creates user instance
func NewUser(name string, age int) *User {
	return &User{
		Name: name,
		Age:  age,
	}
}

func (u *User) String() string {
	return u.Name + "(" + strconv.Itoa(u.Age) + ")"
}
```

- こうすることにより自分で作ったpackageを使えるようになる
 - 使い方は以下
- 一応Exportする関数にgodoc用のコメントを付けておく

```go:server.go
package main

import (
	"fmt"
	"net/http"

	"github.com/awakia/go_sokushu/models"  // 自分のパッケージ
	"github.com/codegangsta/negroni"
)

func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		user := models.NewUser("Naoyoshi Aikawa", 29)
		fmt.Fprintln(w, user)
	})
	n := negroni.Classic()
	n.UseHandler(mux)
	n.Run(":8080")
}
```

## Hello JSON server

```
package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"

	"github.com/awakia/go_sokushu/models"
	"github.com/codegangsta/negroni"
)

func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		user := models.NewUser("Naoyoshi Aikawa", 29)
		userStr, err := json.Marshal(user)
		if err != nil {
			log.Println(err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		w.Header().Set("Content-Type", "application/json")
		fmt.Fprintln(w, string(userStr))
	})
	n := negroni.Classic()
	n.UseHandler(mux)
	n.Run(":8080")
}
```


```
{"ID":0,"Name":"Naoyoshi Aikawa","Age":29}
```

という結果が見れたらOK

- [json.Marchal](https://golang.org/pkg/encoding/json/#Marshal)の定義は`func Marshal(v interface{}) ([]byte, error)`
- []byte型なのでstringにキャストする
- Content-Typeを`application/json`にするのを忘れずに!

### Modify JSON Response

```go:models/user.go　の抜粋
// User defines an user
type User struct {
	ID   int    `json:"id"`
	Name string `json:"name"`
	Age  int    `json:"age"`
}
```

結果:

```
{"id":0,"name":"Naoyoshi Aikawa","age":29}
```

- structに`json:`アノテーションをするとjson.Marshalの結果が変わります
 - `json:"-"`で出力しない
 - `json:"age,omitempty"`で空(デフォルト値)だと出力しない等
 - 詳細は https://golang.org/pkg/encoding/json/#Marshal

## Hello unrolled/render

https://github.com/unrolled/render

```
package main

import (
	"net/http"

	"github.com/awakia/go_sokushu/models"
	"github.com/codegangsta/negroni"
	"github.com/unrolled/render"
)

func main() {
	ren := render.New()
	mux := http.NewServeMux()
	mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		user := models.NewUser("Naoyoshi Aikawa", 29)
		ren.JSON(w, http.StatusOK, user)
	})
	n := negroni.Classic()
	n.UseHandler(mux)
	n.Run(":8080")
}

```

- ダルいので簡単にRenderしてくれるライブラリを使いましょう
- go getを忘れずに！

## Hello DB with OR-mapping

生SQLがGo的ですが、今回はいきなりORマッピングライブラリを使っちゃいます。

https://github.com/jinzhu/gorm
https://godoc.org/github.com/jinzhu/gorm


- gormはRails的なORマッパー
- 他は[golangでSQLを叩くライブラリまとめ[基本/クエリビルダ/ORM]](http://qiita.com/umisama/items/c022b16101c48ffdbc6a)がまとまっている


まずはCREATE DATABASEで`go_sokushu`テーブルを作る

```
$ psql postgres
psql (9.4.4)
Type "help" for help.

postgres=> CREATE DATABASE go_sokushu;
CREATE TABLE
postgres=> 
```

```go:db/db.go
package main

import (
	"log"

	"github.com/awakia/go_sokushu/models"
	_ "github.com/go-sql-driver/mysql"
	"github.com/jinzhu/gorm"
	_ "github.com/lib/pq"
	_ "github.com/mattn/go-sqlite3"
)

var instance *gorm.DB

// Get gets opened db instance
func Get() *gorm.DB {
	if instance != nil {
		return instance
	}
	db, err := gorm.Open("postgres", "user=awakia dbname=go_sokushu sslmode=disable")
	if err != nil {
		log.Println(err)
	}
	instance = &db
	return instance
}

func createTables() {
	db := Get()
	db.CreateTable(models.NewUser("Naoyoshi Aikawa", 29))
}

func main() {
	createTables()
}
```

```bash
$ go get ./...
$ go run db/db.go
```

CreateTableできてるかチェック

```bash
$ psql go_sokushu
go_sokushu-# \d
             List of relations
 Schema |     Name     |   Type   | Owner  
--------+--------------+----------+--------
 public | users        | table    | awakia
 public | users_id_seq | sequence | awakia
(2 rows)

go_sokushu-# \d users
                                 Table "public.users"
 Column |          Type          |                     Modifiers                      
--------+------------------------+----------------------------------------------------
 id     | integer                | not null default nextval('users_id_seq'::regclass)
 name   | character varying(255) | 
 age    | integer                | 
Indexes:
    "users_pkey" PRIMARY KEY, btree (id)
```

ここまで出来たら、一般的に使える要素だけを抜き出すように`db.go`を書き換える

```db/db.go
import (
	"log"

	_ "github.com/go-sql-driver/mysql"
	"github.com/jinzhu/gorm"
	_ "github.com/lib/pq"
	_ "github.com/mattn/go-sqlite3"
)

var instance *gorm.DB

// Get gets opened db instance
func Get() *gorm.DB {
	if instance != nil {
		return instance
	}
	db, err := gorm.Open("postgres", "user=awakia dbname=go_sokushu sslmode=disable")
	if err != nil {
		log.Println(err)
	}
	instance = &db
	return instance
}
```

## Hello REST

```models/users.go
package models

import (
	"fmt"
	"strconv"

	"github.com/awakia/go_sokushu/db"
)

// User defines an user
type User struct {
	ID   int    `json:"id"`
	Name string `json:"name"`
	Age  int    `json:"age,omitempty"`
}

// NewUser creates user instance
func NewUser(name string, age int) *User {
	user := &User{
		Name: name,
		Age:  age,
	}
	db.Get().Create(user)
	fmt.Println(*user)
	return user
}

func GetUser(id int) *User {
	var user *User
	db.Get().Where("id = ?", id).First(user)
	return user
}

func AllUsers() []*User {
	var users []*User
	db.Get().Find(&users)
	return users
}

func (u *User) String() string {
	return u.Name + "(" + strconv.Itoa(u.Age) + ")"
}
```

```server.go
package main

import (
	"net/http"

	"github.com/awakia/go_sokushu/models"
	"github.com/codegangsta/negroni"
	"github.com/unrolled/render"
)

func main() {
	ren := render.New()
	mux := http.NewServeMux()
	mux.HandleFunc("/create", func(w http.ResponseWriter, req *http.Request) {
		user := models.NewUser("Naoyoshi Aikawa", 29)
		ren.JSON(w, http.StatusOK, user)
	})

	mux.HandleFunc("/index", func(w http.ResponseWriter, req *http.Request) {
		users := models.AllUsers()
		ren.JSON(w, http.StatusOK, users)
	})

	mux.HandleFunc("/show", func(w http.ResponseWriter, req *http.Request) {
		user := models.GetUser(1)
		ren.JSON(w, http.StatusOK, user)
	})
	n := negroni.Classic()
	n.UseHandler(mux)
	n.Run(":8080")
}
```

- とりあえず、リクエストは全部Getでやってみましょう！

### やってみよう！

PostやShowでIDをとったりは

- http://www.gorillatoolkit.org/pkg/mux
- https://github.com/julienschmidt/httprouter

でできます。

## Goの注意点

- 使わない変数やパッケージがあるとエラー
 - `_`に入れて切り抜けよう！ 
- goは基本camelCaseで、constだと全部大文字みたいなルールは無い
- public(Exportするもの)は大文字始まり、privateは小文字始まり
- ライブラリがうまく見つからない時は`go get -u`それでダメなら`go get ./...`
- interface と struct のみ
- クラスメソッドの概念はない。インスタンスメソッドのみ
- どちらかと言うとclassでまとめるというよりpackageでまとめる
- interface は ダックタイピング
 - アヒルのように歩き、アヒルのように鳴くのなら、アヒルでしょ！
- 継承はなくて埋め込み(embed)
 - 考え方はモジュールに似ている
 - https://golang.org/doc/effective_go.html#embedding
 - コンフリクトした場合、使用したらエラーになるので、どっちを使うか決める変数/関数を新たに作る必要がある (厳密には、ネストの階層を見ているのでならない場合もある)