summaries
- [0:00~] Introduction(1m)
- [0:01~] WebAPIサーバーとは?(1m)
- [0:02~] 最もシンプルなWebサーバー(10m)
- [0:12~] 課題1: FizzBuzz APIを作ろう(説明1m+作業10m)
- [0:23~] 課題2: プロフィールを返すAPIを作ろう(説明1m+作業10m)
- [0:34~] 課題3: プロフィールを保存するAPIを作ろう(説明1m+作業10m)
- [0:45~] 課題4: 保存したプロフィールを返すAPIを作ろう(説明1m+作業10m)
- [0:56~] 課題5: プロフィールAPIのクライアントを作ろう(説明1m+作業10m)
- [~0:67] 終了
今回のテーマは「WebAPIサーバー」とします。
Go言語は様々な用途で活用されます。
その中で皆さんがより親しみやすく、楽しくGoを学べる入り口としてこのテーマを選択しました。
課題を通して皆さんが学べる事
- Go言語の構文に慣れる事
- Go言語のWebAPIサーバーの実装方法を知ること
※各課題のコードは次の課題で使う場合があります。課題をクリアしたら消さずに保存しておいてください。
http(s)://から始まるのURLでアクセスされるリモートに存在するマシンです。
本来Webサーバーと呼ばれるマシンは静的、または動的なWebページを返しますが、
その中でもバックエンドにAPIサーバーを用意して、RESTと呼ばれる設計モデルでJSONデータをやり取りして動的なWebページを表示するページも多く存在します。
今回作るAPIサーバーはまさにそのリクエストに応じてJSONデータを返したりする、WebAPIサーバーです。
コードを書いてコンパイルすると、http://localhost:8080 でアクセスできるようにして開発を進めていきます。
最初に簡単なWebサーバーの書き方と動かし方を説明します。
Go言語ではパッケージと呼ばれる様々なライブラリを取り込んで使うことのできる機能が備わっています。
パッケージには言語に組み込まれている内部パッケージ、有志によって開発された外部パッケージがあります。
- net/http(内部パッケージ)
- julienschmidt/httprouter(外部パッケージ)
1. 外部パッケージを導入する
$ go get github.com/julienschmidt/httprouter
2. Webサーバーとして動くコードを書く
main.go
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"github.com/julienschmidt/httprouter"
)
// /Hello/:langにハンドルされているHello関数
func Hello(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
lang := p.ByName("lang") // langパラメーターを取得する
fmt.Fprintf(w, lang) // レスポンスに値を書き込む
}
// /ExampleにハンドルされているExample関数
func Example(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
defer r.Body.Close() // Example関数が終了する時に実行されるdeferステートメント
// リクエストボディを読み取る
bodyBytes, err := ioutil.ReadAll(r.Body)
if err != nil {
// リクエストボディの読み取りに失敗した => 400 Bad Requestエラー
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// JSONパラメーターを構造体にする為の定義
type ExampleParameter struct {
ID int `json:"id"`
Name string `json:"name"`
}
var param ExampleParameter
// ExampleParameter構造体に変換
err = json.Unmarshal(bodyBytes, ¶m)
if err != nil {
// JSONパラメーターを構造体への変換に失敗した => 400 Bad Requestエラー
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// 構造体に変換したExampleParameterを文字列にしてレスポンスに書き込む
fmt.Fprintf(w, fmt.Sprintf("%+v\n", param))
}
func main() {
router := httprouter.New() // HTTPルーターを初期化
// /HelloにGETリクエストがあったらHello関数にハンドルする
// :langはパラメーターとして扱われる
router.GET("/Hello/:lang", Hello)
// /ExampleにPOSTリクエストがあったらExample関数にハンドルする
router.POST("/Example", Example)
// Webサーバーを8080ポートで立ち上げる
err := http.ListenAndServe(":8080", router)
if err != nil {
log.Fatal(err)
}
}
2. Webサーバーを起動する
$ go run main.go
起動した時に8080ポートが使われていると出たら、別のポートに変えてみてください。
3. Webサーバーにアクセスする
ブラウザでhttp://localhost:8080/Hello/golang
にアクセスしてみましょう。
POSTリクエストはcurlコマンドで叩いてみましょう。
$ curl -XPOST -d "{\"id\": 1, \"name\": \"yukpiz\"}" http://localhost:8080/Example
- GET http://localhost:8080/FizzBuzz/:num
- 1~:numまでの数値で3で割り切れる場合Fizz、5で割り切れる場合Buzz、両者で割り切れる場合はFizzBuzz!と出力してみましょう
- 整数・正数でないパラメータが渡された場合400エラーにしましょう
- GET http://localhost:8080/Profile/:name
- :nameがBobとAliceならプロフィールをJSON形式で返しましょう、それ以外は404エラーにしましょう
- BobとAliceのプロフィールは以下です
Bob
Name: Bob
Age: 25
Gender: Man
Favorite Foods: Hamburger, Cookie, Chocolate
Alice
Name: Alice
Age: 24
Gender: Woman
Favorite Foods: Apple, Orange, Melon
- POST http://localhost:8080/Profile
- 今回はデータベースを使いません、変数にプロフィールを保持しておきましょう
- 同じ名前のユーザーは登録してはいけません、400エラーを返しましょう
- リクエストで受け付けるBodyパラメーターは以下です
- プロフィールを変数に保持した後、201ステータスコードを返せたらゴールです
name string
age int
gender string
favorite_foods: []string
- GET http://localhost:8080/Profile/:name
- 課題3で保存したプロフィールを見ることができません
- 課題2と課題3を組み合わせて、保存したプロフィールを取得できるようにしてみましょう
- プロフィール取得API(GET)を呼び出すAPIクライアント
- プロフィール保存API(POST)を呼び出すAPIクライアント
- コマンドライン引数を扱うために、
flag
パッケージを使いましょう - 以下のように呼び出せるようにしてみましょう
$ go run main.go -name yukpiz -age 28 -gender Man -favorite-foods "Apple Orange" #プロフィールを保存
$ go run main.go -name yukpiz #プロフィールを取得
- html/templateパッケージを使いましょう
- 以下のようなページ構成でWebページを返すサーバーを書いてみましょう
Indexページ
Aliceプロフィールページ
Bobプロフィールページ