124 第 6 章 レシピを駆使した調理例 # 正常終了 exit 0 ■ public-html/index. html ー入力フォーム く ! DOCTYPE html PUBL 工 C "—//W3C//DTD XHTML 1 . 0 Transitiona1//EN" "http ://www.w3.org/TR/xhtm11/DTD/xhtm11-transitiona1.dtd"> く html mlns="http : //www.響3.0 て g / 1999 / xhtml " lang="j a"> く haed> く meta http—equiv="Content—Type " content="text/html ; charset=utf—8" / > く meta http—equiv="Content—Sty1e—Type" content="text/css" / > く style type="text/css"> dd { margin—bottom: 0.5em; } #inqZipc0de1 ,#inqZipcode2 {font—size: large; font—weight : bold; } #addressform { width : 50em; margin : lem 0 ; padding : lem; border : lpx solid く title > 郵便番号→住所検索 Ajax by シェルスクリプトデモく /title> く script type="text/javascript" src="zip2addr. js"> く /script> く meta http—equiv="Content—Script—Type " content="text/j avascript ー く /style> . type—desc {font—size : small; font—weight : bold;} く hl > 郵便番号→住所検索 Ajax by シェルスクリプトデモく / hl > く bOdy> く /haed> く form action="#dummy"> く table border="O" く t て > く td c01span="3"> く dl > く dt > 郵便番号く / dt > く dd> く input id=" inqZipcode1 " type="text' く input id="inqZipcode2" type="text" く /dd> く / dl > く /td> く / t て > id="addressform"> く tr> く td> く dl > く dt > 住所検索く br /> く /dt> く dd> く input id="runt' type="buttonn く dt > 住所 ( 都道府県名 ) く / dt > く dd > name= name= name= "inqZipcode1" s 土 ze = " 3 " max1ength="3" / > 'inqZipcode2" size="4" max1ength="4" / > 'run" value=" 実行 " onc1ick="zip2addr() ; ” /> く /dd> く select id="inqPref" name="inqPref"> く opti 。Ⅱ > ( 選択してください ) く /option>
レシピ 4.6 Ajax で画面更新したい •CLOCK. HTML く html> く he ad> く tit1e>Ajax C10ck く /title> く style type="text/css"> #clock {border: lpx solid;width 20em} く script type="text/javascript" src="CLOCK. JS"> く /script> く /he ad> く body onload="update—clock() " > く h1>Ajax CIOCk く / れ 1 > く div id="clock"> く dl > く dt>Date : く / dt > く dd > 0000 / 00 / 00 く / dd > く dt>Time : く /dt> く dd>00 : 00 : 00 く / dd > く / d1 > く /div> く input type="button" value="update" onclick="update—clock() " > く /body> く /html> 次に Ajax 通信時にサーバー上でレスポンスを返す CGI スクリプト。 Web プラウザーから Ajax として呼ば れた際、現在時刻を取得して前述 HTML のく div id = " cl 。 ck " > ~ く / div > の中身を生成して返すというものだ。 この CGI スクリプトが XML や JSON ではなく、部分的な HTML を返しているという点も JavaScript ライプラリー依存から脱するための重要な工夫だ。 •CLOCK. CGI # ! /bin/sh datetime=$ (date , + %Y/%m/%d %H : %M : %S , ) cat くく HTTP_RESPONSE Content—Type : text/html く dl > く dt>Date : く /dt> く dd>${datetime% *} く /dd> く dt>Time : く /dt> く dd>${datetime#* } く /dd> exit 0 HTTP_RESPONSE く / d1 > そして最後に、 Ajax 通信を行う JavaScript(CLOCK. (S) の中身は次のとおりだ。
78 •CLOCK. 」 S / / 1 ・ Ajax オプジェクト生成関数 第 4 章 POSIX 原理主義テクニックー Web 編 / / ()E 、非 IE 共に XMLHttpRequest オプジェクトを生成するためのラッパー関数 ) function createXMLHttpRequest ( ) { if (window. XMLHttpRequest) {return new XMLHttpRequest ( ) } if (window. ActiveXObj ect) { if (xhr. readyState = の {alert ( ' タイムアウトです。 ) ) ; } var Str , elm ; function update—clock—callback (xhr , (o) { / / ( Aj 通信が正常終了した時に実行したい処理を、この if 文の中に記述する ) / / 3. コールバック関数 xhr. send(null) ; / / POST メソッドの場合は、 send() の引数として CGI 変数文字列を指定 xhr. open( 'GET' , Ⅱて1 + '?dummy=' + (new Date)/1, true) ; / / キャッシュ対策 xhr. onreadystatechange function(){update-clock—callback(xhr,to)}; tO i Ⅱ do . setTimeout(function(){xhr. abort ( ) } , 3000 の ; / / 30 秒でタイムアウト if ( ! xhr) {return;} xhr = createXMLHttpRequest() ; ur 1 , /PATH/TO/THE/CLOCK . CGI' ・ var url,xhr,to; function update-clock() { / / ( この Ajax 通信をしたい時にはこの関数を呼び出す ) / / 2. Ajax 通信関数 return false; try{return new ActiveXObj ect ( "Microsoft . XMLHTTP" ) }catch(e) { } try{return new ActiveXObj ect ( "Msxm12. XMLHTTP. 3.0 ” ) }catch(e) { } try{return new ActiveXObject("Msxm12. XMLHTTP. 6. O")}catch(e){} if (xhr. readyState ! = 4 ) {return; window. c1earTimeout (tO) ; if ( x れて . status = 200 ) { str = xhr. responseText ; elm = document. getE1ementById( ' clock' ) ; e1m. innerHTML = str; } else { alert(' サーバーが不正な応答を返しました。 } / / Ajax 未完了につき無視 このように、コメントを除けば 40 行足らずの JavaScript コードで、 Ajax が実装できてしまう。 者はまずそこから悩むことになる。そんなことに時間を費やすくらいなら前述のような 40 行足らずのコードを ンアップに追加モジュールがある。もうこうなると「一体どれを使えばよいのか ? ? ? 」、 Ajax や JavaScript 初心 している。昔は prototype. js が流行ったが廃れ、トレンドは jQuery へ移りっている。しかし度重なるバージョ 世の中 JavaScript が流行っており、同時に、それを便利に使うためのライプラリーも実に様々なものが登場 解説 更新されるはずだ。 ること ) し、 Web プラウザーで CLOCK. HTML を開いてみるとよい。 update ボタンを押すたびに現在時刻に この 3 つのコードを適宜 Web サーバーにアップロード ( CLOCK. JS 内で指定している URL は適切に記述す
レシピ 4.6 Ajax で画面更新したい 理解し、コピー & ペーストして使う方がよっぽど簡単ではなかろうか。 前述の JavaScript コードは XMLHttpRequest という Ajax のためのオプジェクトを使うためのコードだが、 いくつかのポイントを押さえれば理解は簡単だ。 ポイント 1. XMLHttpRequest オプジェクト生成方法 79 ただし困ったことに IE8 は、く select > タグにはⅲ nerHTML プロバティー代入ができないというバグがあ する ) で紹介している便利な mojihame コマンドを使えば、それほど大変なことにはならないのだ。 なる。そのぶんサーバー側が大変になると思うかもしれないが、レシピ 4.8 ( HT L テープルを簡単綺麗に生成 リー無しの JavaScript 側で、苦労して HTML の DOM ツリーを操作するなどといった面倒な作業が要らなく に代入するだけで済んでしまう。このようにしてサーバー側に部分 HTML の生成を任せてしまえば、ライプラ HTML ごと作ってしまっている。 こうすると JavaScript 側は受け取った文字列を innerHTML プロバティー 時刻データを XML や JSON で表現したものを返しているのではなく、ハメ込まれるく div> タグの中身の部分 といって、最近 XML の代わりに使われるようになってきた JSON も利用していない。サーバー側の CGI は このサンプルコードのもうーっの特徴は、 Ajax でありながら XML をやりとりしていないことだ。だから ポイント 3. 無理に XM L や」 SON を使おうとしない 検索してもらいたい。様々なページで解説されている。 XMLHttpRequest オプジェクトやその各種メソッドやプロバティーの使い方については、その名前で Web つ後の send メソッドの引数として指定することになっているので注意。 数を毎回替えるというこの原始的な方法が最も確実である。尚、 POST メソッドの場合の CGI 変数は、その一 いためのメソッドが用意されているのだが、これまたバージョンによって使い方が微妙に異なるので、 CGI 変 ようにしている。これで IE もキャッシュを使わなくなる。一応 XMLHttpRequest にはキャッシュを使わせな 間 ( のミリ秒単位を求める Date オプジェクトの利用 ) とすることで、アクセスする度、リクエスト内容が変わる シュ対策」とコメントしてある行の記述だ。 URL の後ろに、ダミーの CGI 変数を置き、その値として UNIX 時 実際の Web サーバーへはアクセスをしないという困ったクセがある。これを回避するテクニックが「キャッ これまた IE 対策なのだが、 IE は同じ内容で Ajax 通信を行うと 2 回目以降はキャッシュを見にいってしまい、 ポイント 2. キャッシュ回避テクニック るのかを気にせずオプジェクトが生成できる。 功するまで試みるのが最初の関数 createXMLHttpRequest() である。これを使えばどのプラウザーで動かされ い IE は ActiveX オプジェクトとして生成しなければならない。そこで、オプジェクトの生成を色々な手段で成 トの生成方法が違う ( オプジェクトの使い方は同じ ) 。 IE でも最近のものは他と同様の方法で生成できるが、古 IE (lnternet Explorer) は他のプラウザーと違って少々クセがある。まずは XMLHttpRequest オプジェク https : //developer. mozilla ・ org/j a/docs/Web/APl/XMLHttpRequest * 9 →レシピ 4.8 (HTML テープルを簡単綺麗に生成する ) → MDN (Mozilla Developer Network) サイトの XMLHttpRequest メソッド説明ペー 参照 る。これがやりたい場合は残念ながら XML や JSON を使うしかない。
76 第 4 章 POSIX 原理主義テクニックー Web 編 て、データの中身と重複しないようにしている。 1 つのセクションはヘッダー部とボディー部からなり、セク ション内の最初の空行で仕切られる。従って変数名やファイル名はヘッダー部から取り出し、値はボディー部を そのまま取り出せばよい。 ボディー部分は、基本的に何のエンコードもされないためバイナリーデータである。これを取り出すのは一工 夫必要だ、 AWK は NULL ( < 0X00 > ) を含んでいるとそこを行末とみなして以降の行末までの文字列が取り出 せない * 8 ので、取り出すのには使えないからだ。 ではどうするかというと、目的のデータのボディー部分は何行目から始まって何行目まで終わるのかを先に 数える。そしてその区間を head コマンドと tail コマンドで抽出し、データ終端についている改行文字を消すの だ。この作業をコマンド化したものが、 POSIX の範囲で書き直した mime-read コマンドなのである。 問題 レシピ 4.6 Ajax で画面更新したい →レシピ 1.6 ( 改行無し終端テキストを扱う ) →レシピ 4.5 (Web プラウザーからのファイルアップロード ) →レシピ 4.3 (CGI 変数の取得 (GET メソッド編 ) ) 参照 POSIX 原理主義を貫く意義を省みれば、クライアント上の JavaScript でも W3C で勧告されていない範囲の 回答 プデートを繰り返していて、追いかけるのが大変だし。 ただ、 JavaScript ライプラリーは懲り懲りだ。 prototype. js は下火になってしまったし、 jQuery も頻緊にアッ Web アプリ制作で、画面全体を更新せず、 Ajax を用いて部分更新したい。 ライプラリーを使うべきではない。従ってここでも自力で行う方法を紹介する。 POSIX 原理主義者的 Ajax チュートリアル では、簡単な Ajax 利用 Web アプリを作ってみよう。 HTML フォームのボタンを押すたび Aj でサー に現在時刻を問い合わせ、時刻欄を書き換えるというものだ。リストは 3 つ必要だ。まずは HTML から。 * 8 GNU 版 AWK は取り出せるのだが。 ノヾ
121 果の住所欄への入力を担当する JavaScript だ。そして、受け取った 7 桁の郵便番号から辞書を引き、得られた 住所文字列を返すシェルスクリプトが "zip2addr. ajax. cgi" である。 名前を見ればわかるがこのシェルスクリプトも Ajax として動作するので、レシピ 4.6 ( Ajax で画面更新し たい ) に従って部分 HTML を返してもよいのだが、 こでは敢えて JSON 形式で返すことにした。「もちろん JSON で返すこともできる」ということを示すためだ。 JSON で返せば、例えばクライアント側で何らかの汎用 JavaScript ライプラリーを利用していて、それと繋ぎ込むといったことも可能というわけだ。 ソースコード 概要が掴めたところで、主要なソースコードを記していくことにする。 アプリケーションの中身を、とくと堪能してもらいたい。 尚、これらのソースコードは GitHub でも公開している * 3 。 ■ data/mkzipdic-kenall. sh ー辞書ファイル作成 ( 一般地域名用 ) シェルスクリプトで構成された Web このプログラムは、 Web サイトから ZIP ファイルをダウンロードして展開する都合により、 POSIX 非準拠 の curl コマンドと unzip コマンドを必要とすることを御了承願いたい。 # ! /bin/sh ・成功時には辞書ファイルを更新する。 しの場合は 0 、失敗したら 0 以外 ー作成成功もしくはサイトのタイムスタンプが古いために作成する必要無 ・戻り値 # [ 出力 ] 今ある辞書ファイルより新しくても更新する ・サイトにある csv ファイルのタイプスタンプが、 # Usage : mkzipdic . sh —f # 日本郵便公式の郵便番号住所 csv から、本システム用の辞書を作成 ( 地域名 ) # MKZIPDIC_KENALL . SH # 初期設定 # ーー一変数定義ー cd "$d"; pwd)" # この sh のパス readonly ur1-ZIPCSVZIP=http : //www.post ・ japanpost. jp/zipcode/dl/oogaki/zip/ken-all. zip # 郵便番号辞書ファイルのパス readonly fi1e—ZIPDIC="$dir_MINE/ken_a11. txt" readonly f1g—SUEXECMODE=0 * 3 https ://github.com/She11Shoccar—jpn/zip2addr # 日本郵便郵便番号ー住所 # csv データ (Zip 形式 ) URL # サーバーが suEXEC モードで # 動いているなら 1 を設定
レシピ 4.10 シェルスクリプトおばさんの手づくり Cookie ( 書き込み編 ) ■掲示板で名前とメールアドレスを Cookie に覚えさせる CGI スクリプト (bbs. cgi) # ! /bin/sh # 一時ファイルの元となる名称 # -e + 604800 : 有効期限を 604800 秒後 ( 1 週間後 ) に設定 cookie—str=$ (mkcookie —e + 604800 —p /bbs —s Y ー Y $Tmp—forcookie) # cookie 文字列を作成 FOR_COOKIE email $email name $name cat くく—FOR—COOKIE > $Tmp—forcookie # " 変数名 + スペース 1 文字 + 値 " で表現された元データファイルを作成 # ( 名前とメールアドレスを設定するための何らかの処理 ) 91 # -p /bbs # —s Y # —h Y # HTTP ヘッダーを出力 cat くく—HTTP_HEADER ・サイトの / bbs ディレクトリー以下で有効な cookie とする : secure フラグを付けて、 SSL 接続時以外には読み取れないようにする : httpOn1y フラグを付けて、 JavaScript には拾わせないようにする Content—Type : text/html$cookie—str HTTP _HEADER # ( ここで HTML のボディー部分を出力 ) rm -f $Tmp-* # 用が済んだら一時ファイルを削除 mkcookie コマンドに渡す変数は、 1 変数につき 1 行で 変数名く半角スペース 1 文字 > 値 という書式にして作る。変数名と値の間に置く半角スペースは 1 文字にすること。もし 2 文字にすると 2 文字 目は値としての半角スペースとみなすので注竜 諸ー、 0 mkcookie コマンドのオプションについては、 -help " オプションなどで表示される Usage を参照されたい。 RFC 6265 で定義されている属性に対応しているのですぐわかるだろう。 最後に こで出来上がった C 。。 kie 文字列は、出力しようとしている他の HTTP ヘッダーに付加して送る。 注意すべき点が 1 つある。 mkcookie コマンドは、先頭に改行を付ける仕様になっているので、前述の例のよ うに他のヘッダー ( 例えば Content-Type) の行末に付加し、単独の行とはしないよう気を付けなければならな いということだ。 解説 クライアントに Cookie を送るためにはまず、 C 。 okie 文字列がどんな仕様になっているかを知る必要がある。 そこで具体例を示そう。まず、次のような条件があるとする。 ・有効期限は、現在 ( 2014 / 12 / 30 日 10 : 20 : 30 とする ) から 1 週間後 投稿者のメールアドレス (email) は、 "6g03@examp1e ・ com" 投稿者の名前 (name) は、「 6 号さん」
92 第 4 章 ・サイトの "/bbs" ディレクトリー以下で有効 ・ "example.com ”というドメインでのみ有効 ・ Secure フラグ ( SSL でアクセスしている時のみ ) 有効 ttpOnly フラグ (JavaScript には取得させない ) 有効 この時に生成すべき C 。。 kie 文字列は次のとおりだ。 POSIX 原理主義テクニックー Web 編 Set—C00kie : Ⅱ ame = 6 % E5 % 8F % B7 % E3 % 81 % 95 % E3 % 82 % 93 ; expires=Tue , 06 ー Ja Ⅱー 2015 19 : 20 : 30 GMT; path=/bbs ; domain=example. com ; Secure ; HttpOn1y Set—Cookie : emai1=6g03%40examp1e.com/ expires=Tue , 06 ー Ja れ一 2015 19 : 20 : 30 GMT; path=/bbs ; domain=e xample. com; Secure ; HttpOnly "Set-Cookie: ”という名前の HTTP ヘッダーを用意し、そこに、変数名 = 値 つまり、 ・変数名 = 値 ( 必須 ) ・ expires= 有効期限の日時 (RFC 2616 Sec3.3.1 形式、省略可 ) ・ path = U. カ上で使用を許可するディレクトリー ( 省略可 ) ・ d 。 main = U. 月秀上で使用を許可するドメイン ( 省略可 ) ・ secure ( 省略可 ) ・ Http0n1y ( 省略可 ) という各種プロバティーを、カンマ区切りで付けていく。もし送りたい Cookie 変数が複数ある場合は、 1 つ 1 つに "Set-Cookie:" 行をつけ、 expires 以降のプロバティーは同じものを使えばよい。 こまで紹介してきたレシピのうちの 2 つを活用する。 このような Cookie 文字列を生成するにあたっては、 値を URL 工ンコードするにはレシピ 4.2 (URL 工ンコードする ) 、有効日時の計算にはレシピ 3.3 ( シェルスク リプトで時間計算を一人前にこなす ) だ。有効日時は、“ RFC2616Sec3.3.1 形式”ということになっているが、 その形式を作るには次のコードで可能だ。 TZ=UTC + 0 dat e + %Y%m%d%ff/0M%S ー TZ=UTC + 0 utconv awk ' {print $ 1 + 86400 } ' TZ=UTC + 0 utconv ーて awk ' { ー # UNIX 時間に変換 ー # 有効期限を 1 日としてみた ー # 皿工 x 時間から逆変換 "Wdy , DD-Mon-YYYY HH : MM : SS GMT" 形式に変換 split ( " Jan Feb Mar Apr May Jun Ju1 Aug sep Oct NOV Dec" 'monthname) ; split ( " Sun Mon Tue Wed Thu Fri Sat " , weekname) ; Y = substr($O, 1 , 4 ) * 1 ; M = substr($O, 5 , 2 ) * 1 ; D = substr($O, 7 , 2 ) * 1 ; subst て ( $ 0 , 13 , 2 ) * 1 ; h = substr($0, 9 , 2 ) * 1 ; m = subst て ( $ 0 , 11 , 2 ) * 1 ; s ( M く 3 ) ? M + 12 : M; ( M く 3 ) ? Y-I : Y; M2 Y2 w = ()2 + int(Y2/4)-int(Y2/100) + int ( Y2 / 400 ) + int ( ( M2 * 13 + 8 ) / 5 ) + D ) % 7 ; printf ( "%s , % 02d ー % s ー % 04d % 02d : % 02d : % 02d GMT*n" weekname + 1 ] , D, monthname CM] , 五 , s) ; Cookie を手づくりするのも、本書のレシピをもってすれば十分可能だ。 参照 →レシピ 4.9 ( シェルスクリプトおばさんの手づくり Co 。 kie ( 読み込み編 ) ) →レシピ 3.3 ( シェルスクリプトで時間計算を一人前にこなす ) →レシピ 4.2 (URL 工ンコードする )