├── .gitignore ├── 001_net_http ├── README.md └── src │ ├── server1.go │ ├── server2.go │ └── server3.go ├── 002_net_http_inside ├── README.md └── src │ ├── fmt_server.go │ ├── override.go │ └── server1.go ├── 003_encoding_json ├── README.md └── src │ ├── annotated.go │ ├── flex.go │ ├── marshal.go │ ├── stringer.go │ └── unmarshal.go ├── 004_file ├── README.md └── src │ ├── hoge.txt │ ├── read.go │ └── write.go ├── 005_json_api ├── README.md └── src │ └── jsonapi.go └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | -------------------------------------------------------------------------------- /001_net_http/README.md: -------------------------------------------------------------------------------- 1 | net/http 2 | =================== 3 | 4 | Goのhttpネットワークに関する機能を提供する標準パッケージです。 5 | 基本的にhttpリクエストを発行する場合やhttpサーバを起動するときにはこのパッケージを利用します。 6 | 7 | 説明を読むよりも手を動かしていきましょう。 8 | 9 | httpサーバーを建てる 10 | ------------------- 11 | 12 | ```go 13 | package main 14 | 15 | import ( 16 | "fmt" 17 | "net/http" 18 | ) 19 | 20 | func main() { 21 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 22 | fmt.Fprint(w, "Hello!") 23 | }) 24 | http.ListenAndServe(":4000", nil) 25 | } 26 | ``` 27 | 28 | これが一番簡単なHTTPサーバーです。 29 | もう少し説明を追記します。 30 | 31 | ```go 32 | package main 33 | 34 | import ( 35 | "fmt" 36 | "net/http" 37 | ) 38 | 39 | func main() { 40 | // HandleFuncを使ってリクエストとレスポンスを処理します 41 | // ここでは、"/" 以下全てのリクエストをHelloで返します。 42 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 43 | fmt.Fprint(w, "Hello!") 44 | }) 45 | // 4000 portで起動 46 | http.ListenAndServe(":4000", nil) 47 | } 48 | ``` 49 | 50 | じゃあこれをechoサーバにするため、urlのパスを取ってHelloにくっつけるようにしてみましょう。 51 | 52 | ``` 53 | # イメージ 54 | $ curl http://localhost:4000/world 55 | # Hello! world 56 | ``` 57 | 58 | では作っていきましょう 59 | 60 | ```go 61 | 62 | package main 63 | 64 | import ( 65 | "fmt" 66 | "net/http" 67 | "strings" 68 | ) 69 | 70 | func main() { 71 | // HandleFuncを使ってリクエストとレスポンスを処理します 72 | // ここでは、"/" 以下全てのリクエストをHelloで返します。 73 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 74 | // pathを取るにはr.URL.Pathで受け取る 75 | path := r.URL.Path 76 | // stringsパッケージのReplaceで/を空白に変えておく 77 | path = strings.Replace(path, "/", " ", -1) 78 | // fmt.Fprintfに変更する 79 | fmt.Fprintf(w, "Hello!%s", path) 80 | }) 81 | // 4000 portで起動 82 | http.ListenAndServe(":4000", nil) 83 | } 84 | ``` 85 | 86 | 87 | こんな感じに成ります。 88 | 89 | 実際にブラウザもしくはcurlでアクセスしてみてください。 90 | 91 | ``` 92 | $ curl http://localhost:4000/world 93 | # Hello! world 94 | $ curl http://localhost:4000/world/yosuke 95 | # Hello! world yosuke 96 | ``` 97 | 98 | ここから更にエンドポイントを増やしていきましょう 99 | 100 | ```go 101 | package main 102 | 103 | import ( 104 | "fmt" 105 | "net/http" 106 | "strings" 107 | ) 108 | 109 | func main() { 110 | // HandleFuncを使ってリクエストとレスポンスを処理します 111 | // ここでは、"/" 以下全てのリクエストをHelloで返します。 112 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 113 | path := r.URL.Path 114 | path = strings.Replace(path, "/", " ", -1) 115 | fmt.Fprintf(w, "Hello!%s", path) 116 | }) 117 | // 新しくエンドポイントを追加する 118 | http.HandleFunc("/api", func(w http.ResponseWriter, r *http.Request) { 119 | // QueryParameterを取る 120 | // Mapの形式で帰ってくる 121 | queryParam := r.URL.Query() 122 | // Mapにはkey-valueで入っていて、valueにはGetでアクセスする 123 | name := queryParam.Get("name") 124 | age := queryParam.Get("age") 125 | fmt.Fprintf(w, "Hello! %s, your age is %s", name, age) 126 | }) 127 | // 4000 portで起動 128 | http.ListenAndServe(":4000", nil) 129 | } 130 | ``` 131 | 132 | こうすると、QueryParameterを解すようになります。 133 | 134 | ``` 135 | $ curl http://localhost:4000/api?name=yosuke&age=31 136 | # Hello! yosuke, your age is 31 137 | ``` 138 | 139 | URLは以下の様な構造をしています。 140 | 141 | ```go 142 | type URL struct { 143 | Scheme string 144 | Opaque string 145 | User *Userinfo 146 | Host string 147 | Path string 148 | RawQuery string 149 | Fragment string 150 | } 151 | ``` 152 | 153 | それぞれ以下の様なURL構造にマッチしています。 154 | 155 | ```go 156 | scheme://[userinfo@]host/path[?query][#fragment] 157 | ``` 158 | 159 | 詳しくはURLの公式ドキュメントを見るとよいでしょう。 160 | 161 | [http://golang.org/pkg/net/url/](http://golang.org/pkg/net/url/) 162 | 163 | まとめ 164 | ----------------- 165 | 166 | - httpサーバを建てる 167 | - HandleFuncを使ってエンドポイントを増やす 168 | - クエリーパラメータやurlのパスを得るにはURL structを使う 169 | -------------------------------------------------------------------------------- /001_net_http/src/server1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | ) 7 | 8 | func main() { 9 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 10 | fmt.Fprint(w, "Hello!") 11 | }) 12 | http.ListenAndServe(":4000", nil) 13 | } 14 | -------------------------------------------------------------------------------- /001_net_http/src/server2.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "strings" 7 | ) 8 | 9 | func main() { 10 | // HandleFuncを使ってリクエストとレスポンスを処理します 11 | // ここでは、"/" 以下全てのリクエストをHelloで返します。 12 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 13 | path := r.URL.Path 14 | path = strings.Replace(path, "/", " ", -1) 15 | fmt.Fprintf(w, "Hello!%s", path) 16 | }) 17 | // 4000 portで起動 18 | http.ListenAndServe(":4000", nil) 19 | } 20 | -------------------------------------------------------------------------------- /001_net_http/src/server3.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "strings" 7 | ) 8 | 9 | func main() { 10 | // HandleFuncを使ってリクエストとレスポンスを処理します 11 | // ここでは、"/" 以下全てのリクエストをHelloで返します。 12 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 13 | path := r.URL.Path 14 | path = strings.Replace(path, "/", " ", -1) 15 | fmt.Fprintf(w, "Hello!%s", path) 16 | }) 17 | // 新しくエンドポイントを追加する 18 | http.HandleFunc("/api", func(w http.ResponseWriter, r *http.Request) { 19 | // QueryParameterを取る 20 | // Mapの形式で帰ってくる 21 | queryParam := r.URL.Query() 22 | // Mapにはkey-valueで入っていて、valueにはGetでアクセスする 23 | name := queryParam.Get("name") 24 | age := queryParam.Get("age") 25 | fmt.Fprintf(w, "Hello! %s, your age is %s", name, age) 26 | }) 27 | // 4000 portで起動 28 | http.ListenAndServe(":4000", nil) 29 | } 30 | -------------------------------------------------------------------------------- /002_net_http_inside/README.md: -------------------------------------------------------------------------------- 1 | net/http のより深い所 2 | ==================== 3 | 4 | もう少し深いところについて説明していきます。 5 | 一旦一番簡単な実装に戻って、一行ずつ中で何をやってるのか説明していきます。 6 | 7 | ```go 8 | package main 9 | 10 | import ( 11 | "fmt" 12 | "net/http" 13 | ) 14 | 15 | func main() { 16 | // 1. HandleFunc 17 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 18 | // 2. fmt.Fprint 19 | fmt.Fprint(w, "Hello!") 20 | }) 21 | // 3. ListenAndServe 22 | http.ListenAndServe(":4000", nil) 23 | } 24 | 25 | ``` 26 | 27 | 1. HandleFuncの話 28 | -------------------- 29 | 30 | HandleFuncの中の実装を追っていくと、以下のHandleという関数に行き着きます。ここでは何をやっているかというと、基本的にはmapに対してkeyにurl patternをvalueにServeHTTPという関数を登録するだけです。 31 | 32 | ```go 33 | // 与えられたpatternにhandlerを登録します。 34 | // もしhandlerがパターンに既に存在していたら、panicsを呼び出します 35 | (mux *ServeMux) Handle(pattern string, handler Handler) { 36 | // muxのmapは共有リソースなのでロックを取る 37 | mux.mu.Lock() 38 | defer mux.mu.Unlock() 39 | 40 | if pattern == "" { 41 | panic("http: invalid pattern " + pattern) 42 | } 43 | if handler == nil { 44 | panic("http: nil handler") 45 | } 46 | if mux.m[pattern].explicit { 47 | panic("http: multiple registrations for " + pattern) 48 | } 49 | 50 | mux.m[pattern] = muxEntry{explicit: true, h: handler, pattern: pattern} 51 | 52 | if pattern[0] != '/' { 53 | mux.hosts = true 54 | } 55 | 56 | // 便利なふるまい: 57 | // もしpatternが/tree/だったら、/treeにリダイレクトする機能がある 58 | // これはちなみにexplicit registrationによって変更することも可能 59 | n := len(pattern) 60 | if n > 0 && pattern[n-1] == '/' && !mux.m[pattern[0:n-1]].explicit { 61 | path := pattern 62 | if pattern[0] != '/' { 63 | path = pattern[strings.Index(pattern, "/"):] 64 | } 65 | // ここが本処理。 66 | // mux.mというmapにmuxEntry構造体を作って登録する 67 | mux.m[pattern[0:n-1]] = muxEntry{h: RedirectHandler(path, StatusMovedPermanently), pattern: pattern} 68 | } 69 | } 70 | ``` 71 | 72 | とまぁこんな感じにHandlerFuncの中ではmuxEntry構造体を作って登録するだけ。 73 | 74 | ちなみにHandleFuncは中でDefaultMuxと呼ばれる構造体のServeHTTP関数を呼んでるだけです。HandlerというインタフェースにあるServeHTTP関数を実装しています。このHandleFuncはワザワザ構造体を作らなくてもサーバーを建てられるようにするための糖衣構文と言えるでしょう。 75 | 76 | 逆に言えばServeHTTP関数を持つ構造体を作って登録すればDefaultMuxのふるまいを変更させることが可能です。 77 | 78 | ```go 79 | package main 80 | 81 | import ( 82 | "fmt" 83 | "net/http" 84 | ) 85 | 86 | type greeter struct{} 87 | 88 | // ServeHTTPを実装する 89 | // Golangでは、ServeHTTP関数を実装するだけでHandlerインタフェースを実装したことになる 90 | func (g *greeter) ServeHTTP(w http.ResponseWriter, r *http.Request) { 91 | fmt.Fprint(w, "Hello!") 92 | } 93 | 94 | func main() { 95 | // ListenAndServeの第二引数に構造体を作って渡す 96 | http.ListenAndServe(":4000", &greeter{}) 97 | } 98 | ``` 99 | 100 | HandlerFuncのまとめ 101 | ------------------- 102 | 103 | - やってることはDefaultMuxのServeHTTP関数を呼び出してる 104 | - ServeHTTP関数はResponseWriterとRequestを引数に取り、リクエストが来たら実行される関数 105 | - ということはServeHTTP関数さえ実装すればDefaultMuxの動きを変更できる 106 | 107 | 2. fmt.Fprintの話 108 | -------------------- 109 | 110 | fmt.Fprintは第二引数に与えられた文字列を使って第一引数のWriterに対してWrite関数を実行する関数 111 | ということは、Write関数を明示的にこちらから呼べば同じことが実現できる 112 | 113 | ```go 114 | package main 115 | 116 | import ( 117 | "net/http" 118 | ) 119 | 120 | func main() { 121 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 122 | // WriterのWrite関数を読んであげるだけで良い。 123 | // Write関数はbyteの配列を受け付けるのでstringからキャストする必要がある 124 | w.Write([]byte("Hello!!!")) 125 | }) 126 | http.ListenAndServe(":4000", nil) 127 | } 128 | ``` 129 | 130 | fmt.Fprintのまとめ 131 | ------------------- 132 | 133 | - fmt.Fprintは文字列を受け取ってWriterに渡してるだけ 134 | - Write関数を明示的に呼べば同じことが実現できる 135 | 136 | 137 | 3. ListenAndServeの話 138 | -------------------- 139 | 140 | httpリクエストは必ずgoroutineが上がるようになっている。 141 | 142 | ```go 143 | func (srv *Server) Serve(l net.Listener) error { 144 | defer l.Close() 145 | var tempDelay time.Duration // how long to sleep on accept failure 146 | for { 147 | rw, e := l.Accept() 148 | if e != nil { 149 | if ne, ok := e.(net.Error); ok && ne.Temporary() { 150 | if tempDelay == 0 { 151 | tempDelay = 5 * time.Millisecond 152 | } else { 153 | tempDelay *= 2 154 | } 155 | if max := 1 * time.Second; tempDelay > max { 156 | tempDelay = max 157 | } 158 | srv.logf("http: Accept error: %v; retrying in %v", e, tempDelay) 159 | time.Sleep(tempDelay) 160 | continue 161 | } 162 | return e 163 | } 164 | tempDelay = 0 165 | c, err := srv.newConn(rw) 166 | if err != nil { 167 | continue 168 | } 169 | c.setState(c.rwc, StateNew) // before Serve can return 170 | // ここで必ず goroutineが起動する 171 | go c.serve() 172 | } 173 | } 174 | ``` 175 | 176 | これにより、リクエストが来る度にgoroutineが起動するようになっており、mainルーチンをブロックしない作りになっている。 177 | 178 | 179 | -------------------------------------------------------------------------------- /002_net_http_inside/src/fmt_server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/http" 5 | ) 6 | 7 | func main() { 8 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 9 | // WriterのWrite関数を読んであげるだけで良い。 10 | // Write関数はbyteの配列を受け付けるのでstringからキャストする必要がある 11 | w.Write([]byte("Hello!!!")) 12 | }) 13 | http.ListenAndServe(":4000", nil) 14 | } 15 | -------------------------------------------------------------------------------- /002_net_http_inside/src/override.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | ) 7 | 8 | type greeter struct{} 9 | 10 | // ServeHTTPを実装する 11 | // Golangでは、ServeHTTP関数を実装するだけでHandlerインタフェースを実装したことになる 12 | func (g *greeter) ServeHTTP(w http.ResponseWriter, r *http.Request) { 13 | fmt.Fprint(w, "Hello!") 14 | } 15 | 16 | func main() { 17 | // ListenAndServeの第二引数に構造体を作って渡す 18 | http.ListenAndServe(":4000", &greeter{}) 19 | } 20 | -------------------------------------------------------------------------------- /002_net_http_inside/src/server1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "strings" 7 | ) 8 | 9 | func main() { 10 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 11 | path := r.URL.Path 12 | path = strings.Replace(path, "/", " ", -1) 13 | fmt.Fprintf(w, "Hello!%s", path) 14 | }) 15 | // 新しくエンドポイントを追加する 16 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 17 | // QueryParameterを取る 18 | // Mapの形式で帰ってくる 19 | queryParam := r.URL.Query() 20 | // Mapにはkey-valueで入っていて、valueにはGetでアクセスする 21 | name := queryParam.Get("name") 22 | age := queryParam.Get("age") 23 | fmt.Fprintf(w, "Hello! %s, your age is %s", name, age) 24 | }) 25 | // 4000 portで起動 26 | http.ListenAndServe(":4000", nil) 27 | } 28 | -------------------------------------------------------------------------------- /003_encoding_json/README.md: -------------------------------------------------------------------------------- 1 | encoding/json 2 | =================== 3 | 4 | httpサーバは建てられるようになったので、JSON APIを作るために必要なencoding/jsonの使い方を学んでいきましょう。 5 | 6 | encoding/jsonを使うとjsonを生成することやjson形式の文字列からgolangの構造体を作ることができるようになります。 7 | 8 | 基本的には2つの関数さえ覚えておけば使えると思います。 9 | 10 | Marshal関数 11 | ------------------ 12 | 13 | JSONのbyte列を生成する関数です。使ってみましょう。 14 | 15 | ```go 16 | package main 17 | 18 | import ( 19 | "encoding/json" 20 | "fmt" 21 | ) 22 | 23 | type User struct { 24 | Name string 25 | Age int 26 | } 27 | 28 | func main() { 29 | // ここでUserを作る 30 | m := User{"yosuke furukawa", 31} 31 | // Marshal関数でbyteの文字列を作る 32 | b, err := json.Marshal(m) 33 | if err != nil { 34 | fmt.Errorf("%s", err) 35 | } 36 | // byteの文字列を出力する 37 | fmt.Printf("%s\n", string(b)) 38 | } 39 | ``` 40 | 41 | このファイルを go run で実行させると以下の様な結果が出ると思います。 42 | 43 | ``` 44 | $ go run marshal.go 45 | {"Name":"yosuke furukawa","Age":31} 46 | ``` 47 | 48 | Unmarshal関数 49 | ------------------ 50 | 51 | Unmarshalはbyte列を構造体に変換する関数です。 52 | 53 | ```go 54 | package main 55 | 56 | import ( 57 | "encoding/json" 58 | "fmt" 59 | ) 60 | 61 | type User struct { 62 | Name string 63 | Age int 64 | } 65 | 66 | func main() { 67 | var user User 68 | // Unmarshalを使うと構造体にマッピングできる 69 | err := json.Unmarshal([]byte(`{"Name":"yosuke furukawa", "Age":31}`), &user) 70 | if err != nil { 71 | fmt.Errorf("%s", err) 72 | } 73 | //構造体の値を表示する 74 | fmt.Printf("%s\n", user.Name) 75 | fmt.Printf("%d\n", user.Age) 76 | } 77 | ``` 78 | 79 | ``` 80 | $ go run unmarshal.go 81 | yosuke furukawa 82 | 31 83 | ``` 84 | 85 | さて、このままではjsonの文字列が構造体の変数名とマッピングされているので、外部からも見える変数として使いたいけどjsonのkeyは小文字にしたい場合や、jsonのkeyと変数名は違うものにしたい場合に困ります。 86 | 87 | こういう時はstructにアノテーションを書きます。 88 | 89 | ```go 90 | package main 91 | 92 | import ( 93 | "encoding/json" 94 | "fmt" 95 | ) 96 | 97 | type User struct { 98 | // json:から始まるannotationを書く 99 | Name string `json:"name"` 100 | Age int `json:"age"` 101 | } 102 | 103 | func main() { 104 | var user User 105 | // Unmarshalを使うと構造体にマッピングできる 106 | // アノテーションを使うことでname, ageでもName, Ageにマッピングできる 107 | err := json.Unmarshal([]byte(`{"name":"yosuke furukawa", "age":31}`), &user) 108 | if err != nil { 109 | fmt.Errorf("%s", err) 110 | } 111 | //構造体の値を表示する 112 | fmt.Printf("%s\n", user.Name) 113 | fmt.Printf("%d\n", user.Age) 114 | } 115 | ``` 116 | 117 | tips structにはStringerインタフェースを実装させると便利。 118 | ----------------------- 119 | 120 | JavaでいうtoStringのようにgolangにもString関数を実装させると文字列として評価する時に使えます。早速使ってみましょう。 121 | 122 | 123 | ```go 124 | package main 125 | 126 | import ( 127 | "encoding/json" 128 | "fmt" 129 | ) 130 | 131 | type User struct { 132 | Name string `json:"name"` 133 | Age int `json:"age"` 134 | } 135 | 136 | // String関数を実装する 137 | func (u User) String() string { 138 | return fmt.Sprintf("my name is %s, my age is %d", u.Name, u.Age) 139 | } 140 | 141 | func main() { 142 | var user User 143 | // Unmarshalを使うと構造体にマッピングできる 144 | // アノテーションを使うことでname, ageでもName, Ageにマッピングできる 145 | err := json.Unmarshal([]byte(`{"name":"yosuke furukawa", "age":31}`), &user) 146 | if err != nil { 147 | fmt.Errorf("%s", err) 148 | } 149 | //構造体の値を表示する 150 | //my name is yosuke furukawa, my age is 31 151 | fmt.Printf("%s\n", user) 152 | } 153 | ``` 154 | 155 | jsonの型がフレキシブルでよくわからん時 156 | ------------------------- 157 | 158 | これもありますよね。jsonで型作ろうとするけど、フレキシブルでスキーマがよく分からない時。こういう時は`interface{}`を使います。 159 | 160 | ```go 161 | package main 162 | 163 | import ( 164 | "encoding/json" 165 | "fmt" 166 | ) 167 | 168 | func main() { 169 | // userの型がわからないので、ここではinterface型にする 170 | var user interface{} 171 | err := json.Unmarshal([]byte(`{"name":"yosuke furukawa", "age":31}`), &user) 172 | if err != nil { 173 | fmt.Errorf("%s", err) 174 | } 175 | // とりあえず値を表示する 176 | // map[name:yosuke furukawa age:31] 177 | fmt.Printf("%v\n", user) 178 | // interfaceからmapにして、値を取り出す 179 | m := user.(map[string]interface{}) 180 | fmt.Printf("%s\n", m["name"]) 181 | fmt.Printf("%v\n", m["age"]) 182 | } 183 | ``` 184 | 185 | 参考資料 186 | ------------------ 187 | 188 | - [http://golang.org/doc/articles/json_and_go.html](http://golang.org/doc/articles/json_and_go.html) 189 | 190 | -------------------------------------------------------------------------------- /003_encoding_json/src/annotated.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | ) 7 | 8 | type User struct { 9 | Name string `json:"name"` 10 | Age int `json:"age"` 11 | } 12 | 13 | func main() { 14 | var user User 15 | // Unmarshalを使うと構造体にマッピングできる 16 | // アノテーションを使うことでname, ageでもName, Ageにマッピングできる 17 | err := json.Unmarshal([]byte(`{"name":"yosuke furukawa", "age":31}`), &user) 18 | if err != nil { 19 | fmt.Errorf("%s", err) 20 | } 21 | //構造体の値を表示する 22 | fmt.Printf("%s\n", user.Name) 23 | fmt.Printf("%d\n", user.Age) 24 | } 25 | -------------------------------------------------------------------------------- /003_encoding_json/src/flex.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | ) 7 | 8 | func main() { 9 | // userの型がわからないので、ここではinterface型にする 10 | var user interface{} 11 | err := json.Unmarshal([]byte(`{"name":"yosuke furukawa", "age":31}`), &user) 12 | if err != nil { 13 | fmt.Errorf("%s", err) 14 | } 15 | // とりあえず値を表示する 16 | // map[name:yosuke furukawa age:31] 17 | fmt.Printf("%v\n", user) 18 | // interfaceからmapにして、値を取り出す 19 | m := user.(map[string]interface{}) 20 | fmt.Printf("%s\n", m["name"]) 21 | fmt.Printf("%v\n", m["age"]) 22 | } 23 | -------------------------------------------------------------------------------- /003_encoding_json/src/marshal.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | ) 7 | 8 | type User struct { 9 | Name string 10 | Age int 11 | } 12 | 13 | func main() { 14 | // ここでUserを作る 15 | m := User{"yosuke furukawa", 31} 16 | // Marshal関数でbyteの文字列を作る 17 | b, err := json.Marshal(m) 18 | if err != nil { 19 | fmt.Errorf("%s", err) 20 | } 21 | // byteの文字列を出力する 22 | fmt.Printf("%s\n", string(b)) 23 | } 24 | -------------------------------------------------------------------------------- /003_encoding_json/src/stringer.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | ) 7 | 8 | type User struct { 9 | Name string `json:name` 10 | Age int `json:age` 11 | } 12 | 13 | // String関数を実装する 14 | func (u User) String() string { 15 | return fmt.Sprintf("my name is %s, my age is %d", u.Name, u.Age) 16 | } 17 | 18 | func main() { 19 | var user User 20 | // Unmarshalを使うと構造体にマッピングできる 21 | // アノテーションを使うことでname, ageでもName, Ageにマッピングできる 22 | err := json.Unmarshal([]byte(`{"name":"yosuke furukawa", "age":31}`), &user) 23 | if err != nil { 24 | fmt.Errorf("%s", err) 25 | } 26 | //構造体の値を表示する 27 | //my name is yosuke furukawa, my age is 31 28 | fmt.Printf("%s\n", user) 29 | } 30 | -------------------------------------------------------------------------------- /003_encoding_json/src/unmarshal.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | ) 7 | 8 | type User struct { 9 | Name string 10 | Age int 11 | } 12 | 13 | func main() { 14 | var user User 15 | // Unmarshalを使うと構造体にマッピングできる 16 | err := json.Unmarshal([]byte(`{"Name":"yosuke furukawa", "Age":31}`), &user) 17 | if err != nil { 18 | fmt.Errorf("%s", err) 19 | } 20 | //構造体の値を表示する 21 | fmt.Printf("%s\n", user.Name) 22 | fmt.Printf("%d\n", user.Age) 23 | } 24 | -------------------------------------------------------------------------------- /004_file/README.md: -------------------------------------------------------------------------------- 1 | io package 2 | ================= 3 | 4 | さて、ioパッケージを使うとファイルへのRead/Writeを行うことができ、簡易的な永続化を行うことが可能です。 5 | 6 | ioの使い方を説明していきます。 7 | 8 | fileに書く 9 | ----------------- 10 | 11 | fileに書くにはio/ioutilパッケージを使うと簡単に実施することが可能です。 12 | 13 | ```go 14 | package main 15 | 16 | import ( 17 | "fmt" 18 | "io/ioutil" 19 | ) 20 | 21 | func main() { 22 | ioutil.WriteFile("./hoge.txt", []byte("Hello"), 0644) 23 | fmt.Println("DONE.") 24 | } 25 | 26 | ``` 27 | 28 | 書いたら読んでみたいですよね。 29 | 30 | fileを読む 31 | ------------------ 32 | 33 | fileを読むのもioutil.ReadFileを使うと簡単です。 34 | 35 | ```go 36 | package main 37 | 38 | import ( 39 | "fmt" 40 | "io/ioutil" 41 | ) 42 | 43 | func main() { 44 | ioutil.WriteFile("./hoge.txt", []byte("Hello"), 0644) 45 | fmt.Println("Wrote.") 46 | t, err := ioutil.ReadFile("./hoge.txt") 47 | if err != nil { 48 | fmt.Errorf("%s\n", err) 49 | } 50 | fmt.Printf("READ : %s\n", t) 51 | } 52 | ``` 53 | 54 | 55 | 他にもBufferを使ったやり方があるのですが、現時点では割愛します。 56 | そのうち追記します。 57 | -------------------------------------------------------------------------------- /004_file/src/hoge.txt: -------------------------------------------------------------------------------- 1 | Hello -------------------------------------------------------------------------------- /004_file/src/read.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | ) 7 | 8 | func main() { 9 | ioutil.WriteFile("./hoge.txt", []byte("Hello"), 0644) 10 | fmt.Println("Wrote.") 11 | t, err := ioutil.ReadFile("./hoge.txt") 12 | if err != nil { 13 | fmt.Errorf("%s\n", err) 14 | } 15 | fmt.Printf("READ : %s\n", t) 16 | } 17 | -------------------------------------------------------------------------------- /004_file/src/write.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | ) 7 | 8 | func main() { 9 | ioutil.WriteFile("./hoge.txt", []byte("Hello"), 0644) 10 | fmt.Println("DONE.") 11 | } 12 | -------------------------------------------------------------------------------- /005_json_api/README.md: -------------------------------------------------------------------------------- 1 | json apiを作ろう 2 | ================ 3 | 4 | さて、ここまでの話を基に総合してjson の APIを作ってみましょう。 5 | 6 | 7 | 仕様: 8 | /apiから始まるurlの時にquery paramから来た値をファイルに保存します。 9 | /からその値をjsonにして取り出してみましょう。 10 | jsonは属性が決められたもので良いです。 11 | もちろん時間があればフレキシブルなJSONを保持してそれを取り出すようにしても構いません。 12 | 13 | 例: 14 | 15 | 16 | ``` 17 | $ curl http://localhost:4000/api?name=hoge&age=20 18 | { "name" : "hoge", "age" : "20" } 19 | $ curl http://localhost:4000/ 20 | { "name" : "hoge", "age" : "20" } 21 | ``` 22 | -------------------------------------------------------------------------------- /005_json_api/src/jsonapi.go: -------------------------------------------------------------------------------- 1 | // 解答 2 | package main 3 | 4 | import ( 5 | "encoding/json" 6 | "fmt" 7 | "io/ioutil" 8 | "net/http" 9 | "strconv" 10 | ) 11 | 12 | type User struct { 13 | Name string `json:"name"` 14 | Age int `json:"age"` 15 | } 16 | 17 | func (u User) String() string { 18 | return fmt.Sprintf(`{ "name": %s, "age" : %d }`, u.Name, u.Age) 19 | } 20 | 21 | func main() { 22 | http.HandleFunc("/api", func(w http.ResponseWriter, r *http.Request) { 23 | queryParam := r.URL.Query() 24 | name := queryParam.Get("name") 25 | age, _ := strconv.Atoi(queryParam.Get("age")) 26 | user := &User{Name: name, Age: age} 27 | userJson, _ := json.Marshal(user) 28 | ioutil.WriteFile("./user.json", userJson, 0644) 29 | w.Header().Set("Content-type", "application/json") 30 | fmt.Fprint(w, string(userJson)) 31 | }) 32 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 33 | userJson, _ := ioutil.ReadFile("./user.json") 34 | w.Header().Set("Content-type", "application/json") 35 | fmt.Fprint(w, string(userJson)) 36 | }) 37 | // 4000 portで起動 38 | http.ListenAndServe(":4000", nil) 39 | } 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Golang ハンズオン資料 2 | ========================== 3 | 4 | ヒカルのGoのハンズオン資料です。 5 | 6 | ヒカルのGoでハンズ・オンするための資料です。Tour Of Goをやった後にちょっと具体的なものを作ってみようという感じで学んでもらえるとよいかもしれません。 7 | 8 | 作者自身もそこまでgolangに詳しいわけではないので資料に不備がある場合はissueなどで教えてもらえると助かります。 9 | 10 | 目次 11 | -------------------------- 12 | 13 | JSON APIを作ってみる (ヒカルのGo 第二回) 14 | - [/001_net_http](/001_net_http) 15 | - [/002_net_http_inside](/002_net_http_inside) 16 | - [/003_encoding_json](/003_encoding_json) 17 | - [/004_file](/004_file) 18 | - [/005_json_api](/005_json_api) 19 | --------------------------------------------------------------------------------