プロデルでメッセンジャーアプリを作る(Webアプリ編)


今日は、プロデルWebサーバを使ってメッセンジャーアプリを作ってみます。メッセンジャーアプリとは特定の相手と文章をやりとりするためのソフトです。代表的な物には、LINEのチャットやTwitterのDM、Skypeや、かつてのMSNメッセンジャーなどがあります。

今回と次回の全2回で、メッセージをやりとりするためのWebアプリ(サーバ側)とデスクトップアプリ(クライアント側)の両方をプロデルで作ってみようと思います。

19/5/23加筆: 記事投稿当時から1.6.957までで開発版よりも古いバージョンの簡易Webサーバが配布されており、掲載しているプログラムがそのままでは動作しない状態だったことがわかりました。問題は1.6.958で修正済みです。失礼致しました。

構成を考える

メッセンジャーアプリは、Webアプリ(サーバ側)とデスクトップアプリ(クライアント側)に分けて作ることにします。やりとりするメッセージは、すべてサーバ側のデータベースに格納します。クライアント側では自分に届いたメッセージを表示したり、指定した相手にメッセージを送ったりします。

プロデル簡易Webサーバ

プロデル簡易Webサーバは、プロデルのプログラムが実行できる簡易的なWebサーバです。HTMLやCSS、画像を送ることに加えて、プロデルのプログラムを開くと実行された結果を送れます。簡易Webサーバを使うと、プロデルでWebアプリを作ることができます。

なお、Fast CGI版を使うことで、ApacheやNginxでもプロデルを動かせます。CentOSなどのLinuxでも動作しますのでVPSなどで公開することもできます。

プロデルでWebアプリを作るには

では早速、プロデル簡易Webサーバを使ったWebアプリを作ってみましょう。簡易Webサーバは、スタートボタンから起動できます。

起動すると、管理画面が表示されます。管理画面で、公開フォルダをマイドキュメントなどのプロデルのプログラムを置くフォルダに変えておきます。

まずは、簡単なWebアプリを作ってましょう。設定した公開フォルダに「post.rdr」という名前で、次のプログラムを保存します。

※ウェブアプリ
値は、[要求から「message」というフィールド値を得たもの]

応答として「<html>
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
<form method="POST">
	<input type="text" name="message">
	<input type="submit" value="送信">
</form>
<hr>
[値]
</body>
</html>
」を送る

保存した上で、Webブラウザのアドレスバーに、http://localhost/post.rdr と入力してページを開きます。ページを開くと、このプログラムがWebサーバ上で実行されて、結果がページとして表示されます。

注意: 簡易Webサーバではアクセスするプロデルプログラムの文字コードがHTMLと一致している必要があります。このブログではHTMLで文字コードとしてUTF-8を指定していますので、.rdrファイルもUTF-8形式で保存して下さい。プロデルデザイナでは、ステータスバーの右下で切り替えることができます。

このページでは、テキストボックスと送信ボタンが表示されます。テキストボックスに文字を入力して送信ボタンをクリックすると、入力した文字が表示されます。

※Edgeではセキュリティ強化のために、自分のPCにサーバがある場合、正しいアドレスを指定してもページが表示できない設定になっています。表示されない場合には、アドレスバーに「about:flags」と入力して設定ページには「localhostループバックを許可する」にチェックしてください。

Webアプリを作るために必要な機能

プロデルでWebアプリを作る時には、「要求」種類「応答」種類を使います。プロデルWebサーバでプログラムを実行すると、その時に限りプログラムの中で「要求」変数と「応答」変数が使えるようになります。

「要求」変数には、Webブラウザ(クライアント側)から送られてきたリクエストの内容が格納されています。要求の内容には、フォームに入力したフィールドの内容やクエリ引数の情報、アクセスしたWebブラウザの情報(ヘッダ)が格納されています。

今回使う「要求」種類の手順は、次の2つです。
フィールド値はフォームに入力された内容で、クエリ引数はアドレスに付加された内容です。

  • 要求から【フィールド名】というフィールド値を得たもの
  • 要求から【引数名】というクエリ引数を得たもの

一方「応答」変数には、Webブラウザへ送るレスポンスの内容を設定します。表示するページのHTMLやクッキー情報などのヘッダを設定できます。

今回使う「応答」種類の手順は、次のとおりです。

  • 応答として【内容】を送る

「送る」手順の【内容】には、Webブラウザに送るHTMLを指定します。

「post.rdr」では、「要求」種類の「フィールド値を得る」手順で入力された文字列を取得して、「応答」種類の「送る」手順でHTMLと共に、入力された文字列をWebブラウザへ送信します。

SQLiteを利用する

メッセンジャーWebアプリでは、メッセージをデータベースで管理します。プロデルではMySQLやSQL ServerなどODBCやAccessデータベース, SQLiteといった主要なデータベースエンジンへアクセスするための種類が用意されています。
今回は、その中でもSQLiteというデータベースを使います。

SQLiteは、ファイル形式のSQL系データベースです。別途データベースサーバをインストールしたり設定したりする必要が無く、手短にデータベースを扱えます。プロデルでは、「SQLiteデータベース」種類を使ってSQLiteデータベースへアクセスできます。

データベースには「メッセージ」という名前のテーブルにメッセージを格納します。このテーブルには、1件のレコードにつき、次のような4つのフィールドを持たせることにします。

まずは、プロデルでこのテーブルにレコードを挿入して、その内容を取得するプログラムを作ってみます。

すでに簡単なWebページは作りましたが、それはひとまず置いておいて、テーブルを操作するだけのプログラムを別に作っておきましょう。一つのプログラムの中でいろいろな事をすると、何か問題があった時に混乱します。最初はやりたいこと別にプログラムを分けて作ることをおすすめします。

データファイルは、「[今の位置]database.db」
新規は、データファイルというファイルが存在しない

SQLiteというSQLiteデータベースを作る
SQLiteのデータソースをデータファイルに変える
SQLiteへ接続する
もし新規なら
	SQLiteで「「CREATE TABLE メッセージ (
		本文 TEXT,
		相手 TEXT,
		名前 TEXT,
		時刻 TEXT);」」を実行する
もし終わり

//レコードを追加する
クエリは、「INSERT INTO メッセージ (名前, 相手, 本文, 時刻) VALUES (?, ?, ?, ?)」
バインド値は、{「秋山」, 「山中」, 「こんにちは」, [今を「yyyy-MM-dd HH:mm:ss」に整えたもの]}
クエリをバインド値としてSQLiteで実行する

//レコードを取得する
SQLiteから「「SELECT 名前,相手,本文,時刻 FROM メッセージ ORDER BY 時刻 DESC」」をデータ表として取得してデータとする
SQLiteから切断する

データの行一覧すべてを辞書化して報告する

「SQLiteデータベース」種類では主に次の手順を使います。

  • 【SQL文】を【バインド値】としてSQLiteで実行する
  • SQLiteから【SQL文】をデータ表として取得してデータとする

SQLiteでは、データベースの操作にSQL文を使います。SQL文を通してテーブルに対して、行を挿入(INSERT)したり取得(SELECT)したりします。

テーブルのレコード(行)を挿入・変更するには、「実行する」手順を使います。この手順は、結果を伴わないSQL文の時に使います。

格納したレコードの内容を取得したい時には、「データ表として取得する」手順を使います。取得した内容は、「データ表」種類のオブジェクトとして格納されていて、一行ずつ取り出したり、二次元配列で取得したりできます。

なお、データベースは、「database.db」というファイル名で保存することにします。初回などdatabase.dbが存在しないときはCREATE文でテーブルを作るようにしておきます。

バインド変数

「実行する」手順では、クエリ文字列とバインド配列の2つを指定します。上記のプログラムの「//レコードを追加する」部分では、クエリ文字列の「?」とバインド値の要素が対になって指定されています。これをバインド変数といいます。実行する際には、クエリ文字列の「?」の部分が配列の要素の内容に順番に置き換わって実行されます。
つまり、SQLiteで実際に実行されるSQL文は、次のようになります。

INSERT INTO メッセージ (名前, 相手, 本文, 時刻) VALUES ('秋山', '山中', 'こんにちは', '2019-01-01 12:01:05')

少しわかりにくい書き方ですが、このようにバインド変数を使うことでSQL文が意図せず改ざんされるSQLインジェクションを防止することができ、より安全なWebアプリになります。

メッセンジャーWebアプリを作る

では、実際にメッセンジャーWebアプリ(messenger.rdr)を作ってみましょう。このWebアプリで必要なのは次の機能です。

  1. Webブラウザからメッセージが送信された時に、フォームに入力された名前・相手・本文、また現在日時を新しいレコードとしてテーブルに挿入する
  2. SQLiteのデータベースの「メッセージ」テーブルから「名前」または「相手」が「私の名前」のメッセージを取得する

Webブラウザでフォームのボタンをクリックすると、フィールドに入力された値がWebサーバに送信されます。この時のフォーム上のフィールド名は次のように対応しています。

※なお仕組みが簡単になるように[ログイン]ボタンだけGETメソッドしています。このようにすることでURLクエリ引数として扱われるようになります

1.メッセージを挿入する

メッセージが送信されたかどうかは、「要求」の「mode」フィールド値が「write」になっているかどうかで判断します。

【私の名前】は、[要求から「myname」というクエリ引数を得たもの]
処理モードは、[要求から「mode」というフィールド値を得たもの]
もし私の名前が無または私の名前が「」なら
	説明は、「ログインしてください。」
他でもし処理モードが「write」 なら
	//レコードを追加する
	相手は、[要求から「sendto」というフィールド値を得たもの]
	メッセージは、[要求から「message」というフィールド値を得たもの]
	もし相手の文字数が0でないかつメッセージの文字数が0でないなら
		クエリは、「INSERT INTO メッセージ (名前, 相手, 本文, 時刻) VALUES (?, ?, ?, ?)」
		バインド値は、{私の名前, 相手, メッセージ, [今を「yyyy-MM-dd HH:mm:ss」に整えたもの]}
		クエリをバインド値としてSQLiteで実行する
	そうでなければ
		説明は、「相手とメッセージを入れて下さい」
	もし終わり
そうでなければ
	説明は、「相手とメッセージを入れて送信ボタンをクリックして下さい」
もし終わり

先ほどの2つのプログラム例を組み合わせて、「フィールド値を得たもの」で取得したフォームの入力内容を、レコードとしてテーブルへ挿入します。

2.メッセージを取得してWebページとして出力する

次にSQLiteの「取得する」手順でメッセージを取得します。取得したメッセージは、「データ表」の「行一覧」で一行ずつ取り出します。その際に、行の内容を「辞書化」することで、一つ一つのメッセージの送信者と相手、本文を取り出しやすいようにします。

//レコードを取得する
クエリは、「SELECT 名前,相手,本文,時刻 FROM メッセージ WHERE 名前=? OR 相手=? ORDER BY 時刻 DESC」
バインド値は、{私の名前,私の名前}
SQLiteからクエリをバインド値としてデータ表として取得してデータとする
SQLiteから切断する

結果は、{}
データの行一覧すべてを辞書化したものを投稿にそれぞれ繰り返す
	結果へ「[投稿の「時刻」] [投稿の「名前」]さん->[投稿の「相手」]さん [投稿の「本文」]」を加える
繰り返し終わり

Webブラウザに表示するページは、HTMLとして出力します。HTMLの文法も理解しないといけませんので、今回はシンプルなHTMLだけを送信することにします。HTMLはプロデルでは文字列として扱われるため、変数を[ ]で囲うことで、その変数の内容をページに表示させることができます。

応答として「<!DOCTYPE html>
<html lang="ja">
<head>
	<meta charset="UTF-8">
	<title>メッセンジャーWebアプリ</title>
</head>
<body>
<form method="GET">
	<input type="text" name="myname" value="[私の名前]"><input type="submit" value="ログイン">
</form>
<hr>
<p>[説明]</p>
<form method="POST">
	相手:<input type="text" name="sendto" value="[相手]"><br>
	メッセージ:<input type="text" name="message"><br>
	<input type="hidden" name="mode" value="write">
	<input type="submit" value="送信する">
</form>
<hr>
[結果を「<br>」で繋げたもの]
</body>
</html>
」を送る

Webアプリ完成!

これでメッセンジャーWebアプリが出来上がりました。「http://localhost/messenger.rdr」などと入力してWebアプリを開きます。

まず自分の名前を入力して[ログイン]ボタンをクリックします。そして「相手」「メッセージ」欄へ内容を入力して[送信する]ボタンをクリックします。そうすると、ページにメッセージが表示されます。

メッセージを変えたり、ログインする名前や送り先の相手を変えたり、複数のブラウザでアクセスしたりして、投稿したメッセージが表示されることをテストしてみて下さい。

メッセンジャーWebアプリ(messenger.rdr)のプログラム全文は、次の通りです。

※ウェブアプリ
データファイルは、「[今の位置]database.db」

新規は、データファイルというファイルが存在しない
SQLiteというSQLiteデータベースを作る
SQLiteのデータソースをデータファイルに変える
SQLiteへ接続する

もし新規なら
	SQLiteで「「CREATE TABLE メッセージ (
		本文 TEXT,
		相手 TEXT,
		名前 TEXT,
		時刻 TEXT);」」を実行する
もし終わり

説明は、「相手の名前とメッセージを入れて下さい」
【私の名前】は、[要求から「myname」というクエリ引数を得たもの]

//レコードを追加する
処理モードは、[要求から「mode」というフィールド値を得たもの]
もし私の名前が無または私の名前が「」なら
	説明は、「ログインしてください。」
他でもし処理モードが「write」 なら
	メッセージは、[要求から「message」というフィールド値を得たもの]
	相手は、[要求から「sendto」というフィールド値を得たもの]
	もしメッセージの文字数が0でないなら
		クエリは、「INSERT INTO メッセージ (名前, 相手, 本文, 時刻) VALUES (?, ?, ?, ?)」
		バインド値は、{私の名前, 相手, メッセージ, [今を「yyyy-MM-dd HH:mm:ss」に整えたもの]}
		クエリをバインド値としてSQLiteで実行する
	そうでなければ
		説明は、「メッセージを入れて下さい」
	もし終わり
もし終わり

//レコードを取得する
クエリは、「SELECT 名前,相手,本文,時刻 FROM メッセージ WHERE 名前=? OR 相手=? ORDER BY 時刻 DESC」
バインド値は、{私の名前,私の名前}
SQLiteからクエリをバインド値としてデータ表として取得してデータとする
SQLiteから切断する

結果は、{}
データの行一覧すべてを辞書化したものを投稿にそれぞれ繰り返す
	結果へ「[投稿の「時刻」] [投稿の「名前」]さん->[投稿の「相手」]さん [投稿の「本文」]」を加える
繰り返し終わり

応答として「<!DOCTYPE html>
<html lang="ja">
<head>
	<meta charset="UTF-8">
	<title>メッセンジャーWebアプリ</title>
</head>
<body>
<form method="GET">
	<input type="text" name="myname" value="[私の名前]"><input type="submit" value="ログイン">
</form>
<hr>
<p>[説明]</p>
<form method="POST">
	相手:<input type="text" name="sendto" value="[相手]"><br>
	メッセージ:<input type="text" name="message"><br>
	<input type="hidden" name="mode" value="write">
	<input type="submit" value="送信する">
</form>
<hr>
[結果を「<br>」で繋げたもの]
</body>
</html>
」を送る

WebAPI化する

完成したメッセージWebアプリは、Webブラウザを通してメッセージを送信したり閲覧したりできます。

これだけでも十分遊べますが、次回に向けて、デスクトップアプリからも利用できるようにします。WebアプリをWebAPI化して「メッセンジャーAPI」として改良しておきましょう。

WebAPIとはサーバが提供する機能に、クライアントからアクセスするためのインタフェイスのことです。次回のデスクトップアプリでは、APIを通じてメッセージを送信したり受信したりします。

WebAPIでは、サーバとクライアントとの通信に、HTMLの代わりにJSON形式を使います。JSON(JavaScript Object Notation)は、JavaScriptで利用することを想定したデータ形式ですがWebAPIでは標準的な形式の一つになっています。プロデルでは、「JSON形式」種類を利用すると、辞書からJSONデータを生成したり、逆にJSONデータから辞書します。

メッセンジャーAPIでは、次のような機能を用意します。

  1. 指定した日時(クライアントが最後に受信した日時)以降に送信されたメッセージを取得する
  2. ContentTypeを「application/json」に設定して、メッセージ内容をJSON形式で送る

「api.rdr」という名前でメッセンジャーAPIのプログラムを作ります。

※ウェブアプリ
データファイルは、「[今の位置]database.db」

新規は、データファイルというファイルが存在しない
SQLiteというSQLiteデータベースを作る
SQLiteのデータソースをデータファイルに変える
SQLiteへ接続する

もし新規なら
	SQLiteで「「CREATE TABLE メッセージ (
		本文 TEXT,
		相手 TEXT,
		名前 TEXT,
		時刻 TEXT);」」を実行する
もし終わり

【私の名前】は、[要求から「myname」というクエリ引数を得たもの]
処理モードは、[要求から「mode」というフィールド値を得たもの]
もし私の名前が無または私の名前が「」なら
	「ログインしてください。」というエラーを発生させる
他でもし処理モードが「write」 なら
	//レコードを追加する
	相手は、[要求から「sendto」というフィールド値を得たもの]
	メッセージは、[要求から「message」というフィールド値を得たもの]
	もし相手の文字数が0でないかつメッセージの文字数が0でないなら
		クエリは、「INSERT INTO メッセージ (名前, 相手, 本文, 時刻) VALUES (?, ?, ?, ?)」
		バインド値は、{私の名前, 相手, メッセージ, [今を「yyyy-MM-dd HH:mm:ss」に整えたもの]}
		クエリをバインド値としてSQLiteで実行する
	そうでなければ
		「相手とメッセージを入れて下さい」というエラーを発生させる
	もし終わり
もし終わり
// 続く

api.rdrの冒頭は、必要な値がない時にエラーを発生されている点が異なりますが、ほぼmessenger.rdrと同じです。

次の「//レコードを取得する」部分では、updateというフィールドが指定されているときに、指定された日時以降のメッセージだけを取得するようにSQLの「WHERE」句を指定しています。

//レコードを取得する
更新日時は、[要求から「update」というフィールド値を得たもの]
もし更新日時が無でなければ
	条件は、「 AND 時刻>?」
	バインド値は、{私の名前,私の名前,更新日時}
そうでなければ
	条件は、「」
	バインド値は、{私の名前,私の名前}
もし終わり
SQLiteから「SELECT 名前,相手,本文,時刻 FROM メッセージ WHERE (名前=? OR 相手=?)[条件]」をバインド値としてデータ表として取得してデータとする
SQLiteから切断する

応答のContentTypeは、「application/json」
データの行一覧すべてを辞書化してJSON形式として整形して書き出して応答として送る

Firefoxなどの一部のWebブラウザでは、 「 http://localhost/api.rdr?myname=秋山 」などとアクセスすると、JSONの内容をツリー構造で確認できます。

まとめ

今回は、プロデル簡易Webサーバを使って、プロデルでメッセンジャーWebアプリを作ってみました。

Webアプリの作り方やSQLiteの取り扱いなど内容が多岐にわたりましたが、Webアプリで典型的な構成をプロデルで実現しました。プログラミング言語やアプリの種類に寄らず多くのWebアプリがこの構成で作られていますので、これを改良して色々なWebアプリが作れると思います。

SQLやHTMLについては、説明できませんでしたが、ネットで調べるとたくさんの解説記事が載っています。よかったらそちらも参考にしてプログラムをよりカッコよく改良して欲しいと思います。

次回は、クライアント編として、WebAPI化したWebアプリを使ってメッセンジャーデスクトップアプリを作ってみます。お楽しみに!

続き> プロデルでメッセンジャーアプリを作る(デスクトップアプリ編)

  • いいね (1)
  • 続きを読みたい (2)

コメントを残す