今回はHTTPの"POST"メソッドを使って、HTMLのフォームからではなく、Silverlight (C#)で処理したデータをサーバにアップロードし、サーバ側(PHP)で受け取るアプリケーションを作ったのですが、その方法を調べるのにかなり苦労したのでまとめて書いておきます。
0. サーバ側でSilverlightからのアクセスを許可しておく
下のようなXMLファイルを作成し、clientaccesspolicy.xmlという名前で保存して、サーバのアクセスを許可するディレクトリの下に配置します。
<access-policy>
<cross-domain-access>
<policy>
<allow-from>
<domain uri="*"></domain>
</allow-from>
<grant-to>
<resource path="/" include-subpaths="true"></resource>
</grant-to>
</policy>
</cross-domain-access>
</access-policy>
※なお、上のXMLは全てのURLからのアクセスを許可しますので、オンラインで公開されているサーバに設置するときにはアクセス許可を編集するなどして、十分注意して下さい。
1. WebRequestを使ってPOSTリクエストを送信する
Silverlightでは.NET FrameworkのWebRequestクラスを使用できますが、リクエストやレスポンス関連のメソッドは非同期のもののみになっています。同期メソッドは使えません。
下の例は、サーバ側のPHPプログラム"http://testserver/testprogram.php"にPOSTリクエストを送信しています。
private void sendPostRequest() {
HttpWebRequest req = (HttpWebRequest)WebRequest.Create("http://testserver/testprogram.php");
req.Method = "post";
req.ContentType = "application/octet-stream";
req.BeginGetRequestStream(new AsyncCallback(receiveRequestStream), req);
}
ポイントは、リクエストのMethodプロパティを"post"に、ContentTypeプロパティを"application/octet-stream"にすることです。Content-typeヘッダには他に"application/x-www-form-urlencoded"を使う方法もあるようですが、これだとPOSTされたデータをPHP側で受け取ることができませんでした。
最後にリクエストのストリームを要求する非同期メソッドBeginGetRequestStreamを呼びます。
2. リクエストのストリームにバイナリデータを書き込む
上でBeginGetRequestStreamのコールバックとして登録したメソッドで、リクエストのストリームを受け取り、そこに送信したいバイナリデータを書き込みます。
private void receiveRequestStream(IAsyncResult asynchronousResult) {
WebRequest req = (WebRequest)asynchronousResult.AsyncState;
Stream reqStream = req.EndGetRequestStream(asynchronousResult);
byte[] data = Encoding.UTF8.GetBytes("hehehe"); // 送信するデータを作成
reqStream.write(data);
reqStream.Close();
req.BeginGetResponse(new AsyncCallback(receiveResponse), req);
}
この例では"hehehe"という文字列からバイト配列を作成しして送信していますが、実際にはここでバイナリpt>データをストリームに書き込みました。
書き込みの後、レスポンスを受け取るための非同期メソッドBeginGetResponseを呼びます。
3. サーバからのレスポンスを受け取る
サーバからのレスポンスを受け取ります。
private void receiveResponse(IAsyncResult asynchronousResult) {
WebRequest req = (WebRequest)asynchronousResult.AsyncState;
WebResponse res = req.EndGetResponse(asynchronousResult);
Stream resStream = res.GetResponseStream();
StreamReader sr = new StreamReader(resStream);
string res = sr.ReadToEnd();
resStream.Close();
sr.Close();
res.Close();
}
4. サーバ側(PHP)でデータを受信
サーバ側ではPOSTで送られてきたデータを受け取り、一時ファイルに書き込んでいます。一時ファイルの名前をechoしてレスポンスとしています。
<?php
$post_fp = fopen("php://input", "r");
$tmpFilePath = tempnam(sys_get_temp_dir(), 'tmp');
file_put_contents($tmpFilePath, $post_fp);
fclose($post_fp);
echo $tmpFilePath;
?>
"php://input"はファイル名のように考えて扱えるようですが、"php://input"から得られたリソースに対してseekやrewindはできないようです。
※なお、上のサンプルはファイルの中身を一切確認しないでサーバに保存していますが、実際にはファイルの形式を確認するなどしてセキュリティには十分注意しましょう。
その他、HTTPのPOSTに関する基本的な事項は、
http://developers.sun.com/mobility/midp/ttips/HTTPPost/
また、WebRequestクラスを使った非同期通信の方法は、
http://msdn.microsoft.com/ja-jp/library/system.net.webrequest.begingetrequeststream(VS.85).aspx
のサンプルを参考にしました。
ところで、私は基本的に、Linux, Apache, eclipse, PHP, C++などのオープンな開発環境を使うようにしてきたのですが、最近のRIAへの流れからSilverlightを使ってみたくなり、Microsoftの世界にやや引きずり込まれております。。。
とはいえ、RIAの現実的な選択肢はAdobeのFlexかMicrosoftのSilverlightぐらいしかなく、Flexよりも応用の利くSilverlightには結構期待しています。開発もFlexは高価なAdobe Flexを購入しなければならないのに対し、Silverlightの開発はMicrosoft Visual Web Developer 2008 Express Editionで完全に無料でできます。Silverlightは.NETフレームワークの多くを利用でき、Windowsアプリ開発と共通の知識で開発できるのでちょっと気に入っています。
ところがやはりサーバサイドはWindowsサーバをレンタルするのは費用がかかりすぎるのでLinux + PHPを使います。ASP .NETなんて全然興味ありません。。。
結果として今の選択は、サーバサイドはPHP、クライアントサイドはSilverlightというちょっと変わった組み合わせになってしまいました。





