Skip to content

Instantly share code, notes, and snippets.

@yukpiz yukpiz/golang_hands_on2.md
Last active Sep 11, 2018

Embed
What would you like to do?

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] 終了

Introduction

今回のテーマは「WebAPIサーバー」とします。
Go言語は様々な用途で活用されます。

その中で皆さんがより親しみやすく、楽しくGoを学べる入り口としてこのテーマを選択しました。

課題を通して皆さんが学べる事

  • Go言語の構文に慣れる事
  • Go言語のWebAPIサーバーの実装方法を知ること

※各課題のコードは次の課題で使う場合があります。課題をクリアしたら消さずに保存しておいてください。

WebAPIサーバーとは?

http(s)://から始まるのURLでアクセスされるリモートに存在するマシンです。
本来Webサーバーと呼ばれるマシンは静的、または動的なWebページを返しますが、 その中でもバックエンドにAPIサーバーを用意して、RESTと呼ばれる設計モデルでJSONデータをやり取りして動的なWebページを表示するページも多く存在します。

今回作るAPIサーバーはまさにそのリクエストに応じてJSONデータを返したりする、WebAPIサーバーです。
コードを書いてコンパイルすると、http://localhost:8080 でアクセスできるようにして開発を進めていきます。

シンプルなWebサーバー

最初に簡単な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, &param)
	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

課題1: FizzBuzz APIを作ろう

  • GET http://localhost:8080/FizzBuzz/:num
  • 1~:numまでの数値で3で割り切れる場合Fizz、5で割り切れる場合Buzz、両者で割り切れる場合はFizzBuzz!と出力してみましょう
  • 整数・正数でないパラメータが渡された場合400エラーにしましょう

課題2: プロフィールを返すAPIを作ろう

  • 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

課題3: プロフィールを保存するAPIを作ろう

  • POST http://localhost:8080/Profile
  • 今回はデータベースを使いません、変数にプロフィールを保持しておきましょう
  • 同じ名前のユーザーは登録してはいけません、400エラーを返しましょう
  • リクエストで受け付けるBodyパラメーターは以下です
  • プロフィールを変数に保持した後、201ステータスコードを返せたらゴールです
name string
age int
gender string
favorite_foods: []string

課題4: 保存したプロフィールを返すAPIを作ろう

  • GET http://localhost:8080/Profile/:name
  • 課題3で保存したプロフィールを見ることができません
  • 課題2と課題3を組み合わせて、保存したプロフィールを取得できるようにしてみましょう

課題5: プロフィールAPIのクライアントを作ろう

  • プロフィール取得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を返してみよう

  • html/templateパッケージを使いましょう
  • 以下のようなページ構成でWebページを返すサーバーを書いてみましょう

Indexページ

Image from Gyazo

Aliceプロフィールページ

Image from Gyazo

Bobプロフィールページ

Image from Gyazo

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.