データサーバとの非同期通信

Ajax

Web ページ内の JavaScript プログラム動作中に、 新たにサーバとの非同期通信を開始して表示中のページを動的に変える方式を Ajax とよぶ。以下の HTML 文書と JavaScript プログラムは、ユーザからの入力をCGI経由と Ajax で呼ぶ2つを比較するためのものである。

db.html

<!DOCTYPE html>
<html lang="ja">
<head>
<title>例: Access DB via CGI</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>

<body>
<h1>DB Access</h1>
<h2>via CGI</h2>
<form method="POST" action="./db.cgi">
<table>
 <tr><td>タグ:</td><td><input id="tag" name="tag"></td></tr>
 <tr><td>数値:</td><td><input id="val" name="value"></td></tr>
</table>
<p><input type="submit" value="CGIで送信"> /
<button id="js-sub" type="button">AJAXで送受信</button></p>
</form>
<p>Result: <span id="result"></span></p>
<p id="all"></p>
<script type="text/javascript"
 src="../src/db-post.js" charset="utf-8"></script>


</body>
</html>

db.cgi

#!/usr/bin/env ruby22
# -*- coding: utf-8 -*-
require 'cgi'
require 'json'
require 'sqlite3'
dbfile = './db/data.sq3'

c = CGI.new({'accept_charset' => 'utf-8'})
#print "Content-type: application/json; charset=UTF-8\n\n"
print "Content-type: text/plain; charset=UTF-8\n\n"

value = c['value'].to_f
tag = c['tag']
now = Time.now.to_s
db = SQLite3::Database.new(dbfile)
db.busy_timeout = 1000
db.execute("CREATE TABLE IF NOT EXISTS foo(tag text, time text, val real);")
db.execute("INSERT INTO foo VALUES(?, ?, ?);", tag, now, value)
val = {'タグ'=>tag}
val['合計'] =
  db.execute("SELECT sum(val) FROM foo WHERE tag=?", tag)[0][0].to_f
val['平均'] =
  db.execute("SELECT avg(val) FROM foo WHERE tag=?", tag)[0][0].to_f.round(2)
res = db.execute("SELECT val FROM foo WHERE tag=?", tag)
val['全件'] = res.collect{|row| row[0]}		# 先頭カラムを集める

puts JSON.pretty_generate(val)

db-post.js

// 例: AJAX的CGI通信
function dbAccess() {
    function respond(str) {		// CGIからの出力応答関数
	if (this.readyState == 4) {	// 4のときデータ受信完了
	    // CGIがJSON文字列を返すのでJSON.parseでJSONオブジェクトに変換
	    var resp = JSON.parse(this.responseText); // 受信文字列取得(JSON)
	    var str = '合計:'+resp['合計'] + ' 平均:'+resp['平均']
	    var all = '全件: ' + resp['全件'].join(', ')
	    document.getElementById('result').textContent = str; //書き込む
	    document.getElementById('all').textContent = all;
	}
    }
    function submit() {		// 「AJAX送信」ボタンを押したときの処理
	var val = document.forms[0].elements['value'];
	var tag = document.getElementById('tag');
	if (val) {
	    var conn = new XMLHttpRequest();
	    conn.open('POST', './db.cgi');	// POSTメソッドでCGIへ送る
	    conn.setRequestHeader(		// 送信ヘッダを定義
		'Content-Type', 'application/x-www-form-urlencoded');
	    // 送信データは 変数1=値1&変数2=値2... で送るが、
	    // 値の部分に&などが入ってもよいよう encodeURIComponent() する
	    conn.send('value='+encodeURIComponent(val.value) + '&' +
		      'tag='+encodeURIComponent(tag.value));
	    conn.onreadystatechange = respond;	// イベントリスナ登録
	}
    }
    document.getElementById('js-sub').addEventListener('click', submit);
}
document.addEventListener('DOMContentLoaded', dbAccess)

db-post.js のうち Ajax的データ送信を行なうのが submit 関数である。

  1. XMLHttpRequest オブジェクトを生成
  2. open() で http でのアクセスメソッド(この場合は POST)とパスを指定
  3. setRequestHeader() でHTTP送信ヘッダを設定
  4. send() で実データを送信
  5. onreadystatechange プロパティにコールバック関数を代入

という流れで行なう。送信するデータはCGIが受け取るためには、

変数1=1&変数2=2

のように代入形式の並びを & 記号で区切って必要個数列挙した書式にする必要がある。ただし、変数や値に =&、空白などの記号が含まれるときに 正しく変数部分と値部分のそれぞれが解釈されるよう、URL エンコード行なう。URL エンコードは、たとえば空白文字なら %20 のように % 記号に続けて16進数の文字コードを書くものである。 このエンコードを行なうのが encodeURIComponent 関数である。

CGIプログラムでは、db/data.sq3 をデータベースファイルとして利用するようになっている。 実際に動かす前には、httpd に書き込み可能となるディレクトリを作成し、 そこにダミーのデータベースファイルを作成する。

mkdir -m 1777 db
touch db/data.sq3
chmod 666 db/data.sq3

db.html

GPSデータのサーバ連系

ユーザID、ユーザ名、東経、北緯、

ゴールとなる位置(LatLng)は、goal.txt ファイルに入れておく。

db-gps.html