Golang(Go)のレスポンスデータをjsのfetch APIで取得する

#GolangのレスポンスデータをjsのPromiseをリターンするfetch APIで取得する
[こちら](http://qiita.com/hiroykam/items/5bcde5037bbf3398b584)で`XMLHttpRequest`を利用したPOSTによるデータ受信をPromiseで実現したが、PHPではなく、Golangで作成したWebアプリケーションを用意する。
また、`XMLHttpRequest`ではなく、JS標準のPromiseをリターンする[`fetch API`](https://developer.mozilla.org/ja/docs/Web/API/Fetch_API/Using_Fetch)を利用してみた。
なお、ここでも、シナリオはPOSTでJSONデータを利用するシナリオ(シナリオ1)と、POSTでCSVを受信するシナリオ(シナリオ2)の2パターンを記載。

##サンプルのディレクトリ構成
Golangが動作する環境であること。Golangのインストール方法については、[公式サイト](http://golang.jp/install)等を参照。
`test.csv`の内容については割愛。

`./demo.go`
`./test.csv`
`./template/scenario1.htm`
`./template/scenario1.htm`
`./static/css/modal.css`
`./static/js/app1.js`
`./static/js/app2.js`
`./static/js/bundle1.js`
`./static/js/bundle2.js`
`./static/js/libs/MyLoader.js`

##jsサンプルコードのビルド方法(bundle1.jsとbundle2.jsの作成方法)について
ここでもes6で用意した。ビルド方法については、[こちら](http://qiita.com/hiroykam/items/135c51310087182b03cb)を参照。

##Golangを使ったWebアプリケーション
シナリオ1、シナリオ2、JSONおよびCSVデータをレスポンスするのに、`http.HandleFunc`を使い、cssやjsは`http.Handle`で応答するように実装した。

###Golangのサンプルコード
`go run demo.go`と実行すると、例えば`http://localhost:3001/json`でアクセスするとJSONデータのレスポンスが確認できる。
`/csv`、`./json`にPOST以外でアクセスしたときは、"Method Not Allowed"(ステータスコード405)でレスポンスするようにした。

```go:demo.go
package main

import (
    "fmt"
    "encoding/json"
    "net/http"
    "html/template"
)

type MyJson struct {
    Status string `json:"status"`
    Value  int    `json:"value"`
}

type MyContent struct {
    Scene int
}

func main() {
    http.HandleFunc("/scenario1", viewScenario(1))
    http.HandleFunc("/scenario2", viewScenario(2))
    http.HandleFunc("/json", responseJson)
    http.HandleFunc("/csv", func (w http.ResponseWriter, r *http.Request) {
        switch r.Method {
        case "POST":
            http.ServeFile(w, r, "./test.csv")
        default:
            w.WriteHeader(405)
            w.Write([]byte("Method Not Allowed!"))
        }
    })
    http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
    http.ListenAndServe(":3001", nil)
}


func viewScenario(scene int) func (w http.ResponseWriter, r *http.Request) {
    return func (w http.ResponseWriter, r *http.Request) {
         mc := MyContent {scene}
         tmpl, err := template.ParseFiles(fmt.Sprintf("template/scenario%d.htm", scene))
         if err != nil {
             http.Error(w, err.Error(), http.StatusInternalServerError)
             return
         }
         if err := tmpl.Execute(w, mc); err != nil {
             http.Error(w, err.Error(), http.StatusInternalServerError)
         }
    }
}

func responseJson(w http.ResponseWriter, r *http.Request) {
    switch r.Method {
    case "POST":
        mj := MyJson {"SUCCESS", 100}
        js, err := json.Marshal(mj)
        if err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
            return
        }
        w.Header().Set("Content-Type", "application/json")
        w.Write(js)
    default:
        w.WriteHeader(405)
        w.Write([]byte("Method Not Allowed!"))
    }
}
```

##ローダー
[こちら](http://qiita.com/hiroykam/items/5bcde5037bbf3398b584#%E3%83%AD%E3%83%BC%E3%83%80%E3%83%BC%E3%82%92%E8%A1%A8%E7%A4%BA%E3%81%99%E3%82%8B%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA)と同様にダウンロード中にローダを表示するようにした。

###JSのサンプルコード
```js:static/js/MyLoader.js
"use strict"

const modal = document.createElement("div");
modal.className = "modal";

export default class MyLoader {

    constructor() {
        this.loading = document.createElement("div");
        this.loading.className = "loader";
    }

    show() {
        modal.appendChild(this.loading);
        document.body.appendChild(modal);
    }

    hide() {
        document.body.removeChild(modal);
        modal.removeChild(this.loading);
    }
}
```

###cssのサンプルコード
```css:static/css/modal.css
.modal {
    width: 100%;
    height: 300px;
}

.loader {
    margin: 150px auto;
    border: 16px solid #f3f3f3;
    border-radius: 50%;
    border-top: 16px solid blue;
    border-right: 16px solid green;
    border-bottom: 16px solid red;
    border-left: 16px solid pink;
    width: 120px;
    height: 120px;
    -webkit-animation: spin 2s linear infinite;
    animation: spin 2s linear infinite;
}

@-webkit-keyframes spin {
    0% { -webkit-transform: rotate(0deg); }
    100% { -webkit-transform: rotate(360deg); }
}

@keyframes spin {
    0% { transform: rotate(0deg); }
    100% { transform: rotate(360deg); }
}
```

##シナリオ1
POSTしてJSONデータを受信し、その内容を描画する。

###テンプレート
Golangのサンプルコード中の処理`template.Execute`で、タイトルタグの`Scene`の値(ここでは`1`)が設定される。

```html:template/scenario1.htm
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>Scenario{{ .Scene }}</title>
    <link rel="stylesheet" type="text/css" href="/static/css/modal.css">
</head>
<body>
<div style="margin: 20px" class="js-json">
    <div style="color: red" class="js-json-message"></div>
    <input type="button" post_url="/json" class="js-json-post" value="JSON">
</div>
<footer>
    <script type="text/javascript" src="/static/js/bundle1.js"></script>
</footer>
</body>
</html>
```

###jsのサンプルコード
`response.json()`とすることで、JSONデータを受信できる。

```js:static/js/app1.js
"use strict"

import MyLoader from "./libs/MyLoader";

class MyApp {
    static exec() {
        const obj = new MyApp();
        const target = document.querySelector(".js-json");
        target && obj.setupJson(target);
    }

    setupJson(target) {
        const url = [
            location.protocol + '//',
            location.host
        ];

        const buttons = target.querySelectorAll(".js-json-post");
        [].forEach.call(buttons, (button) => {
            button.addEventListener("click", (evt) => {
                evt.preventDefault();

                let post_url = url.join('') + button.getAttribute("post_url");
                const loading = new MyLoader();
                loading.show();
                fetch(post_url, {method: 'POST'}).then((response) => {
                    return response.json();
                }).then((json)  => {
                    const msg = target.querySelector(".js-json-message");
                    msg.innerHTML = json.value;
                    loading.hide();
                }).catch(() => {
                    loading.hide();
                    alert('通信に失敗しました');
                });
            });
        });
    }
}

MyApp.exec();
````

##シナリオ2
POSTでCSVデータを受信するケース。

###テンプレート
シナリオ1と同様にGolangのサンプルコード中の処理`template.Execute`で、タイトルタグの`Scene`の値(ここでは`2`)が設定される。

```html:template/scenario2.htm
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>Scenario{{ .Scene }}</title>
    <link rel="stylesheet" type="text/css" href="/static/css/modal.css">
</head>
<body>
<div style="margin: 20px" class="js-csv">
    <input type="button" post_url="/csv" class="js-csv-post" value="csv">
</div>
<footer>
    <script type="text/javascript" src="/static/js/bundle2.js"></script>
</footer>
</body>
</html>
```

###jsのサンプルコード
`response.blob()`でBlobオブジェクトが取得できるので、`a`タグを作成して、クリックイベントを発生させることでダウンロードできる。

```js:static/js/app2.js
"use strict"

import MyLoader from "./libs/MyLoader";

class MyApp {
    static exec() {
        const obj = new MyApp();
        const target = document.querySelector(".js-csv");
        target && obj.setupCsv(target);
    }

    setupCsv(target) {
        const url = [
            location.protocol + '//',
            location.host
        ];
        const buttons = target.querySelectorAll(".js-csv-post");

        [].forEach.call(buttons, (button) => {
            button.addEventListener("click", (evt) => {
                evt.preventDefault();

                let post_url = url.join('') + button.getAttribute("post_url");
                const loading = new MyLoader();
                loading.show();

                fetch(post_url, {method: 'POST'}).then((response) => {
                    return response.blob();
                }).then((blob) => {
                        const URL  = window.URL || window.webkitURL;
                        const bu = URL.createObjectURL(blob);
                        const a  = document.createElement('a');
                        a.download = "test.csv";
                        a.href = bu;
                        a.click();
                        loading.hide();
                }).catch(() => {
                    loading.hide();
                    alert('通信に失敗しました');
                });
            });
        });
    }
}

MyApp.exec();
```

##シナリオの実行方法
`go run demo.go`と実行するWebアプリケーションが起動するので、その状態で、`http://localhost:3001/scenario1`や`http://localhost:3001/scenario2`にアクセス。