tetu式

ゲームと音楽・作曲の自己満足と悩みどころの多いプログラムのブログ。

Go言語:とりあえずHTTPサーバー立ててAJAXしてみる

続けざまにGo言語。
今回はかるーくサーバーサイド絡みのことします。


いままでサーバー立てるってなったらApacheを主に使ってました。
Apacheを使うと設定ファイルやらなんやらがあってメインの言語以外の知識が必要になったりして
軽くテストしたいだけなのに煩わしいなーと思ってました。

Go言語だと結構簡単にできちゃいます!

server.go

package main

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

// localhost:8080/ でアクセスしてきた時の処理
func handlerTest(w http.ResponseWriter, r *http.Request) {
	// HTMLテンプレートを使用してhtmlファイルを表示
	tmpl := template.Must(template.ParseFiles("./html/test.html"))
	tmpl.Execute(w, nil)
}

// AJAX戻り値のJSON用構造体
type Param struct {
	Value string
}

// localhost:8080/ajax でアクセスしてきた時の処理
func ajaxPostTest(w http.ResponseWriter, r *http.Request) {
	var val1, val2 int
	var ret Param
	val1, _ = strconv.Atoi(r.PostFormValue("val1"))
	val2, _ = strconv.Atoi(r.PostFormValue("val2"))

	// 計算結果を戻り値に入れる
	ret.Value = calc(val1, val2, r.PostFormValue("type"))

	// 構造体をJSON文字列化する
	jsonBytes, _ := json.Marshal(ret)

	// 返す
	fmt.Fprintf(w, string(jsonBytes))
}

// 整数四則演算
func calc(val1, val2 int, s string) string {
	var res interface{}
	switch s {
	case "add":
		res = val1 + val2
	case "sub":
		res = val1 - val2
	case "mul":
		res = val1 * val2
	case "div":
		if val2 == 0 {
			res = "0割り算エラー"
		} else {
			res = float32(val1) / float32(val2)
		}
	}
	return fmt.Sprint(res)
}

func main() {
	// css、scriptフォルダにアクセスできるようにする
	http.Handle("/css/", http.StripPrefix("/css/", http.FileServer(http.Dir("css/"))))
	http.Handle("/script/", http.StripPrefix("/script/", http.FileServer(http.Dir("script/"))))

	// アクセスしてくるアドレスに対するルーティング的な処理
	http.HandleFunc("/", handlerTest)
	http.HandleFunc("/ajax", ajaxPostTest)

	// サーバー起動
	http.ListenAndServe(":8080", nil)
}

html/test.html

<html>
<head>
    <meta charset="UTF-8">
    <title>テスト</title>
	<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
	<link rel="stylesheet" type="text/css" href="../css/test.css">
	<script type="text/javascript" src="../script/test.js"></script>
</head>

<body>
	<div>
		<p class="test">テストページなのじゃ</p>
	</div>
		値1:<input type="text" name="val1" id="val1">
		値2:<input type="text" name="val2" id="val2">
		<button class="btn" name="add">足し算</button>
		<button class="btn" name="sub">引き算</button>
		<button class="btn" name="mul">掛け算</button>
		<button class="btn" name="div">割り算</button>
	</div>
	<div class="result">
	</div>
<body>

css/test.css

.test {
	color: blue;
}

script/test.js

$(document).ready(function(){
	$('.btn').on('click', function(){
		$.ajax({
			type: "POST",
			url: "ajax",
			data: {
				"val1": $('#val1').val(),
				"val2": $('#val2').val(),
				"type": $(this).attr('name')
			},
			dataType: "json",
			success: function(j_data){
				$(".result").append("結果:" + j_data.Value+"<br />");
			}
		});
	});
});

用意するファイルは4種類。
全部一つのフォルダに突っ込んでもいいのですが、あえて分けてます。

内容的には別にサーバーも立てずにjavascriptだけで済ませられるのですがそこはほら、Go言語の勉強ということで・・・
Go言語のWeb用フレームワークとかもあるのでしょうが、今回は素のGoのみです。
server.goファイルはちょっと長々と書いてますがサーバを立ててページを表示するだけであれば

package main

import (
	"html/template"
	"net/http"
)

func handlerTest(w http.ResponseWriter, r *http.Request) {
	tmpl := template.Must(template.ParseFiles("./html/test.html"))
	tmpl.Execute(w, nil)
}

func main() {
	http.HandleFunc("/", handlerTest)
	http.ListenAndServe(":8080", nil)
}

たったこれだけでできてしまいます。
で、あとはページの数だけmain()に http.HandleFunc("URL", 実行ファンクション名) を書いてファンクションを実装するだけです。

さて、実際のserver.goの内容ですが・・・ページ表示1つと整数四則演算API1つという内容です。
ほんとはこういう内容はMVC的に分けたいところなのですが今回は無しで。
/ でアクセスした時と /ajax でアクセスした時の処理を分けて http.HandleFunc に記述しています。

第1引数に入るのがアクセス時のURLなのですが、これは完全一致ではなくて前方一致な点に注意です。
なので実際は /rsodfhiuh とか適当なアドレスでアクセスしてもhandlerTestが呼ばれちゃいます。
こういう仕様なので / だけでTOPページにアクセスできる内容にしてしまうと404エラー対策とかは逆に面倒かも・・・

ボタンをクリックするとtest.jsのjQueryトリガーが発動し、入力値をごにょごにょして /ajax 宛てに非同期POST通信を行い、
server.go の ajaxPostTest が動き出して、入力値とクリックしたボタン名を判断して計算してくれます。贅沢ですね。

server.go の calc 関数でres変数に計算結果を格納するのにinterface{}型を使用しています。
これはすごいズボラな方法で、半ば動的型付け的な変数として使えちゃいます。
実際計算方法や0除算エラーに備えてint型、string型、float32型の3種類のどれかがresに格納されることになります。
で、最終的にはcalc関数の戻り値であるstring型にはfmt.Sprint()を使用してあげると。

構造体retのValueに計算結果を格納したら json.Marshalを通してバイトコード化し、stringキャストして
test.jsのsuccessに戻ります。

なんというか json.Marshal() の時点でstring型として返ってきて欲しいのですが、
実際にjsonBytesの内容を fmt.Println(jsonBytes) などで見ると

[123 34 86 97 108 117 101 34 58 34 45 50 34 125]

こんな感じでバイトコードの羅列が入ってます。
一手間無駄を感じさせますね。
まぁ、第2戻り値にエラーが返ってくるので json.Marshal() に対して直接stringキャストもできず・・・

でtest.jsのsuccessで最終処理をして結果を表示して終わりです。

簡単な内容ですが、全部のコード合わせても100行未満でローカルブラウザ環境でAjax処理を実装できちゃいました。

Go言語の長所であるゴルーチン処理とか全く使用してないのでGo言語を使用してまでやることか?と言われると微妙なところですが・・・
でも0から始めるならXampp使ったりとかJSP/Servlet + Tomcat使ったりするより圧倒的に学習コスト、環境準備の手順は少ないと思います。