LoginSignup
3
3

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-06-19

GolangのレスポンスデータをjsのPromiseをリターンするfetch APIで取得する

こちらXMLHttpRequestを利用したPOSTによるデータ受信をPromiseで実現したが、PHPではなく、Golangで作成したWebアプリケーションを用意する。
また、XMLHttpRequestではなく、JS標準のPromiseをリターンするfetch APIを利用してみた。
なお、ここでも、シナリオはPOSTでJSONデータを利用するシナリオ(シナリオ1)と、POSTでCSVを受信するシナリオ(シナリオ2)の2パターンを記載。

サンプルのディレクトリ構成

Golangが動作する環境であること。Golangのインストール方法については、公式サイト等を参照。
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で用意した。ビルド方法については、こちらを参照。

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)でレスポンスするようにした。

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!"))
    }
}

ローダー

こちらと同様にダウンロード中にローダを表示するようにした。

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のサンプルコード

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)が設定される。

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データを受信できる。

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)が設定される。

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タグを作成して、クリックイベントを発生させることでダウンロードできる。

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/scenario1http://localhost:3001/scenario2にアクセス。

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3