Dartで非同期通信
毎度お世話になっております。クラスメソッドの稲毛です。
今回はプログラミング言語「Dart」を使って非同期通信を行うWebアプリケーションを作ってみました。
Server side programming
Dart Editorでサーバサイドアプリケーションを作成する場合は「New Application > Command-line application」と指定し作成します。今回は簡単に一定のJSONデータを返すアプリケーションを作成しました。
Httpサーバの起動プロセスは以下のようになります。
- 「HttpServer」オブジェクトを生成。
- 「HttpServer」オブジェクトにリクエストハンドラを追加。
- 「HttpServer」オブジェクトのリスニング(リクエストに対する)開始。
ExampleAjaxServer.dart
#import('dart:io'); #import('dart:json'); bool matcher(HttpRequest request) => request.path == '/hello.json'; void handler(HttpRequest request, HttpResponse response) { response.headers.add('Access-Control-Allow-Origin', '*'); var contentType = new ContentType('application', 'json'); contentType.charset = 'utf-8'; response.headers.contentType = contentType; response.outputStream.writeString(JSON.stringify({'message': 'Hello, dart!'})); response.outputStream.close(); } void main() { var server = new HttpServer(); server.addRequestHandler(matcher, handler); server.listen('127.0.0.1', 8080); }
リクエストハンドラを追加する際に第一引数へ指定するのは真偽値を返すマッチ関数です。この関数は、引数として渡される「HttpRequest」オブジェクトを利用し、そのリクエストがハンドラを起動するのに相応しい(true)か否(false)かを返すように実装します。
bool matcher(HttpRequest request) => request.path == '/hello.json';
今回はリクエストパスが「/hello.json」だったらハンドラを起動するというように実装されています。
リクエストハンドラ内では要求に対する応答内容を構築しています。
Client side programming
Dart Editorでクライアントアプリケーションを作成する場合は「New Application > Command-line application」と指定しアプリケーションを作成します。
非同期通信のサービスリクエストを行うにはAjaxと同様に「XMLHttpRequest」を使用します。Dartなら「Adax」でしょうか。(^^;
ExampleAjaxClient.html
<!DOCTYPE html> <html> <head> <title>ExampleAjaxClient</title> </head> <body> <h1>ExampleAjaxClient</h1> <div> <button id="button">Push</button> </div> <ol id="list"></ol> <script type="application/dart" src="ExampleAjaxClient.dart"></script> <script src="http://dart.googlecode.com/svn/branches/bleeding_edge/dart/client/dart.js"></script> </body> </html>
「XMLHttpRequest」を利用したリクエストプロセスは以下のようになります。
- 「XMLHttpRequest」オブジェクトを生成。
- 「XMLHttpRequest」オブジェクトに「readyStateChange」イベントハンドラを追加。
- 「XMLHttpRequest」オブジェクトの「open」メソッドでリクエストを初期化。
- 「XMLHttpRequest」オブジェクトの「send」メソッドでリクエストを送信。
ExampleAjaxClient.dart
#import('dart:html'); #import('dart:json'); XMLHttpRequest xhr; void clickHandler(event) { xhr.abort(); xhr.open('GET', 'http://127.0.0.1:8080/hello.json', true); xhr.send(); } void readyStateChangeHandler(event) { if (xhr.readyState == XMLHttpRequest.DONE) { if (xhr.status == 200 || xhr.status == 0) { var data = JSON.parse(xhr.responseText); var element = new Element.html('<li>${data["message"]}</li>'); document.query('#list').nodes.add(element); } } } void main() { xhr = new XMLHttpRequest(); xhr.on.readyStateChange.add(readyStateChangeHandler); document.query('#button').on.click.add(clickHandler); }
「abort」メソッドは「XMLHttpRequest」オブジェクトを再利用する為にコールしています。
まとめ
クライアントサイドはほぼ「Ajax」のままですし、サーバサイドは「Node.js」のような雰囲気なので、簡単に非同期通信を行うことができました。まだまだ開発途中の言語ですが、サーバサイドの開発もクライアントサイドの開発も単一のプロダクトで提供しているというのはひとつの魅力だと思います。今後も、ライブラリの拡充などが行われてより便利になって行くことでしょう。(^^)
注意点
この時点(Dart SDK version 8121)でのDart Libraryでは以下の点に注意が必要です。
「ContentType」のプロパティを直接変更できない
「response.headers.contentType」のプロパティを操作しても反映されません。そもそも……
var contentType = new ContentType('application', 'json'); contentType.charset = 'utf-8'; response.headers.contentType = contentType; print(response.headers.contentType == contentType); // false
直前で「ContentType」を代入しているのに比較してみると別インスタンスとなっています。時間があったら調べてみようと思います。
コンストラクタ「XMLHttpRequest.get(url, onSuccess)」による生成では「withCredentials」が「true」となる
「XMLHttpRequest」には生成とリクエストの送出を一度に行う便利コンストラクタ「XMLHttpRequest.get(url, onSuccess)」が用意されているのですが、このコンストラクタで生成した「XMLHttpRequest」オブジェクトは「withCredentials」プロパティが必ず「true」となります。「Access-Control-Allow-Origin」にワイルドカード(*)が指定されたサービスにリクエストを送る際には「withCredentials」プロパティは「false」である必要があります。デフォルト値は「false」なんですけどね。