So-net無料ブログ作成
検索選択

Python CGI で 掲示板みたいなものを作る~Ajax編~ [プログラム三昧]このエントリーを含むはてなブックマーク#

WS000261.png

これまで、 HTML 文書から直接 CGI を呼び出すことで、掲示板みたいなものを実現してきました。 今回は、 JavaScript ファイルを追加して、いわゆる "Ajax" に挑戦してみます。

HTML : visitor_world6.html

今回のメッセージ送信画面は、今までとちょっと違います。 メッセージを記入する "TextBox" と "Submit" "Clear" ボタンが並んでいる所までは同じなのですが、今回は、その下にメッセージのリストが表示されています。

"TextBox" にメッセージを記入して、 "Submit" ボタンが押したら、今までは CGI を直接呼び出していました。 今回は、一旦 "JavaScript" が "Submit" ボタンのクリックを受けとめて、そこから改めて CGI を呼び出して、メッセージの追加とメッセージ一覧の取り寄せを行います。 つまり、 "JavaScript" がサーバにデータを送信し結果を受信します。 画面上のメッセージのリストは、取り寄せたメッセージ一覧を元に動的に書き換えられます。

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
    "http$#58;//www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<!-- $Id: visitor_world6.html,v 1.2 2009/06/11 12:50:27 noritan Exp $ -->
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" >
<head profile="http://www.w3.org/2005/10/profile">
<title>VISITOR WORLD 6</title>
</head>
<body>
<h1>VISITOR WORLD 6</h1>
<form action="#">
<div>
<textarea name="message" id="message" rows="4" cols="40"></textarea>
</div>
<p>
<input type="button" value="Submit" onclick="submit_message();" />
<input type="reset" value="Clear" />
</p>
</form>
<div id="table"> </div>
<script src="xmlhttprequest.js" type="text/javascript"></script>
<script src="visitor_world6.js" type="text/javascript"></script>
</body>
</html>
2009-06-11 21:45
"onClick=" を "onclick=" に修正
「<form>」 を 「<form action="#">」に修正

HTML文書自体は、この程度です。 面倒な処理は、この HTML 文書から呼び出している、二つの "JavaScript" ファイルに押し込めてあります。

JavaScript : xmlhttprequest.js

一つ目のファイル、 "xmlhttprequest.js" は、 "InternetExplorer" で動作させるための仕掛けです。 この記述そのものは、短期集中連載●今こそAjaxに強くなる - ITproから貰ってきました。 したがって、詳しい情報は、こちらの記事をご参照ください。

ちなみに、当方自宅の InternetExplorer では、 ActiveX オブジェクトの生成がなぜかうまくいかず、動作確認が出来ませんでした。 他にも動かないという方がいらっしゃったら、お知らせください。 何とかする気になるかもしれません。

JavaScript : visitor_world6.js

このファイルには、 HTML 記述とサーバ上の CGI との仲介を行うための記述が記載されています。 凝る必要性を感じなかったので、オブジェクト指向を無視した、ベタな記述になっていますので、ご勘弁を。

//
//   BBS using Ajax technique
//
// $Id: visitor_world6.js,v 1.3 2009/06/11 12:41:10 noritan Exp $

//
//   Submit a message to the server
//
function submit_message() {
  var element = document.getElementById('message');
  var query = 'message='+encodeURIComponent(element.value);
  xmlhttp = new XMLHttpRequest();
  xmlhttp.open('POST', 'visitor_world6.cgi', true);
  xmlhttp.onreadystatechange = function() {
    if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
      update_table(xmlhttp.responseXML);
    }
  }
  xmlhttp.setRequestHeader(
    'Content-Type', 'application/x-www-form-urlencoded;charset=utf-8'
  );
  xmlhttp.setRequestHeader("Content-Length", query.length);
  xmlhttp.send(query);
}

//
//   Get a table of messages.
//
function get_table() {
  xmlhttp = new XMLHttpRequest();
  xmlhttp.open('GET', 'visitor_world6.cgi', true);
  xmlhttp.onreadystatechange = function() {
    if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
      update_table(xmlhttp.responseXML);
    }
  }
  xmlhttp.send(null);
}

//
//   Escape a string with entity references
//
function escapeHTML(str) {
  str = str.split("&").join("&amp;");
  str = str.split("<").join("&lt;");
  str = str.split(">").join("&gt;");
  str = str.split('"').join("&quot;");
  str = str.split("{").join("&#123;");
  str = str.split("}").join("&#125;");
  str = str.split("'").join("&#039;");
  return str;
}

//
//   Update a table of message with a received XML
//
function update_table(doc) {
  var str = "";
  var element   = document.getElementById('table');
  var topnode   = doc.getElementsByTagName("visitor-memo")[0];
  var mes_list  = topnode.getElementsByTagName("message");
  
  str += '<dl>\n'
  for (var i = 0; i < mes_list.length; i++) {
    var date     = escapeHTML(mes_list[i].getAttribute("date"));
    var message  = escapeHTML(mes_list[i].firstChild.nodeValue);
    str += "<dt>" + date + "</dt>\n";
    str += "<dd>" + message + "</dd>\n";
  }
  str += "</dl>\n"
  element.innerHTML = str;
}

//
//   Initialize the table visualization
//
get_table()
2009-06-11 08:50
"mes_list[i]textContent" を "mes_list[i].firstChild.nodeValue" に修正

問い合わせは、 "XMLHttpRequest" オブジェクトを利用して "GET" または "POST" を発行しています。 "POST" を送信する具体的な方法については、AJAXでXMLHttpRequestのopenをPOST、sendに内容を送っても送信できない時 - ウィリアムのいたずらの開発日記を参考にさせてもらいました。

問い合わせをサーバの CGI に行った結果は、 XML フォーマットで返ってきます。 このため、返ってきた DOM オブジェクトから必要な情報を取り出す操作を行っています。

XMLに含まれるメッセージを画面上に表示する時には、クロス・サイト・スクリプティング等への警戒から、ある特定の記号を実体参照に変換しています。 文字列を実体参照に変換する関数は、文字の実体参照化からいただきました。

CGI : visitor_world6.cgi

サーバに配置した CGI の機能そのものは、今までとほぼ同じですが、出力が HTML 文書になっているか、 XML データになっているかという違いがあります。 出力は、 W3CMarkup Validation で正しい XML 文書であるとお墨付きをもらうことができる、完全を目指した XML 文書が得られるようになっています。

#!/usr/local/bin/python
# $Id: visitor_world6.cgi,v 1.1 2009/06/10 09:57:59 noritan Exp $

import os
import sys
import cgi
import urllib
import time
import cgitb

# Show error as a page description.
sys.stderr = sys.stdout
cgitb.enable()

# Database releated information
db_file = "visitor.sqlite"
table_name = "visitor"
command = "/usr/local/bin/sqlite3 %s" % (db_file)

# Get a POST data.
form = cgi.FieldStorage()

# Get Current time
now = time.time()

# Get and escape a MESSAGE
message_key = 'message'
if message_key in form:
    message = urllib.quote(cgi.escape(str(form.getvalue(message_key))))
else:
    message = ""

# Compose a SQL
sql = "SELECT time, description FROM %(table)s ORDER BY time DESC;"
if len(message) > 0:
    sql = \
      "INSERT INTO %(table)s VALUES (%(now)f, \"%(message)s\");\n" \
      + sql

# Show HTML header
print """Content-type: text/xml; charset="utf-8"

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE visitor-memo [
<!ELEMENT visitor-memo (message)* >
<!ELEMENT message (#PCDATA) >
<!ATTLIST message date CDATA #REQUIRED >
]>
"""

# Access to the DATABSE
(pipe_out, pipe_in) = os.popen2(command)
pipe_out.write(
  sql % {"table":table_name, "now":now, "message":message}
)
pipe_out.close()

# Show a list of visitor record
print """<visitor-memo>
"""

# Make a list of messages
try:
    for line in pipe_in:
        field = line.split("|")
        asctime = time.strftime(
          "%Y-%m-%d (%A) %H:%M:%S",
          time.localtime(float(field[0]))
        )
        message = urllib.unquote(field[1])
        print "<message date=\"%s\">%s</message>\n" % (asctime, message)
finally:
    pipe_in.close()


# Show footer
print """
</visitor-memo>
"""

2009-06-11 08:50 追記

自宅の Internet Explorer では、あいかわらず、 "Msxml2.XMLHTTP" オブジェクトを生成してくれません。 別のPCで試してみたところ、オブジェクトの生成はしてくれましたが、 XML オブジェクトからメッセージ文字列を取り出す部分に「ブラウザ依存」があったようで、エラーが発生しました。 responseXMLではまった - 文系大学的IT系の悲哀を参考に "mes_list[i]textContent" を "mes_list[i].firstChild.nodeValue" に変更しました。

参考サイト

VISITOR WORLD 6
"FORM"入力ページは、ここにありますので、お試しください。
今からでも遅くない Ajax基本のキ - ITpro
短期集中連載●今こそAjaxに強くなる - ITpro
Web開発の新手法Ajax - ITpro
文字の実体参照化 - SimpleBoxes
Python の場合、ライブラリに変換関数があるので、とうぜん、 JavaScript にも同等関数があるのだろうと思っていました。 ところが、いざ、探してみると、どこにもありません。 どうやら必要な人が個々に関数を記述するという非効率な状態になっているようです。
AJAXでXMLHttpRequestのopenをPOST、sendに内容を送っても送信できない時 - ウィリアムのいたずらの開発日記
"POST" でデータを送るには、 "Content-Type" を書かなくてはならないそうだ。
responseXMLではまった - 文系大学的IT系の悲哀
どうも、すべてのブラウザに対応するのは、並大抵の努力ではすまないようで。

nice!(0)  コメント(0)  トラックバック(0)  このエントリーを含むはてなブックマーク#

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

トラックバック 0

トラックバックの受付は締め切りました

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。