太郎日記’79J

「やったろうやないの。」な太郎のblog
技術ネタはタロタローグに任せて、こっちはニュースメインで。
<< July 2018 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 >>
 
RECOMMEND
MySQLによるタフなサイトの作り方
MySQLによるタフなサイトの作り方 (JUGEMレビュー »)
佐藤 真人,桑野 章弘,岡田 達典,大黒 圭祐
MySQLだけでなく、DBサーバやアプリの設計など、あらゆる手段を使ってタフなサイトを作る本である
最新記事
あわせて読みたい
あわせて読みたい
なかのひと
ClusterMaps
CM by JUGEM
スポンサーサイト

一定期間更新がないため広告を表示しています

- | 個別記事 | - | -
HTTP::LiteでXMLデータを投げるHTTPクライアントを作る[crossdomain問題(5)]

CGI.pmでPOSTされてきたXMLデータを受け取る方法[crossdomain問題(4)]の次です。
前回、送られてきたウェブサービスのURLとXMLデータを取得できたので、
今回はいよいよウェブサービスに問い合わせを敢行する部分です。
ずーっと前から名前だけは出てきていた「HTTP::Lite」の出番が、
ようやくやってきました。
が、前回も書いたとおりXMLデータをウェブサービスに渡すのは簡単じゃない!!
通常の方法だと、HTTP::Liteは「名前=値」の形しかPOST出来ないのです!
そこで、今回はHTTP::Liteの「set_callback」を使い、
XMLデータを渡していく方法を解説します。

■prepare_postは使えない!?

HTTP::Liteには、POSTを実現するメソッドとして、
prepare_post()」ってのがあります。
が、こいつは、(名前=>値)形式の、ハッシュを受け取るメソッドです。
前回も書いたようにXMLデータには名前はありませんから、
こいつは使えません。
POSTDATA」も、CGI.pmでのみ通用する名前なので、
HTTP::Liteの前では無力です。
なので、別の方法でXMLデータを渡すしかありません。
ここで、「set_callback」を使います。
こいつは、リクエストの要所要所で呼び出すコールバック関数を、
HTTP::Liteに設定すると言うメソッドです。
呼び出されるタイミング(フェーズと呼びます)は、↓
connect
接続成功!HTTPヘッダが始まる直前
content-length
戻り値がContent-lengthとして書き出される
done-headers
HTTPヘッダ終了!
content
戻り値がコンテンツとして書き出される
content-done
コンテンツ終了!
data
戻り値がレスポンスデータとして扱われる
done
リクエスト終了!
で、今回は「content」フェーズで、
XMLデータを返せばいいわけです。後ついでに、
content-length」フェーズでもXMLデータの長さを返しましょう。
で、「data」フェーズは来た値をそのまま返せば、
レスポンスも問題なく処理できますね。

■コールバック関数を定義する(1)

さて、コールバック関数の定義方法なんですが、
とにかく情報が少ない。
関数への参照などと言う難しい概念が理解できない俺には、
情報なしでは到底実装できません。
でも、何とかひとつだけ実装例を見つけました。
Perlモジュール/HTTP::Lite/set_callbackメソッド
これ!
ありがとう先人よ!!!
おかげでコールバック関数が作れそうです。
このサイトからの情報によると、コールバック関数は、
my $funcref = sub {
    my ($self, $phase, $dataref, $handle) = @_;#引数取得
    ・・・
};
のように定義し、
my $http = new HTTP::Lite;
$http->set_callback($funcref, 引数として渡したいデータ);
のように使うようです。

なるほど!

今回は定義の中身を上手く変えてやればいいわけです。
処理内容はこんな感じでしょうか。
connect
undefを返す
content-length
XMLデータの長さを返す
return length($xml);
done-headers
undefを返す
content
XMLデータを返す
return length($xml);
content-done
undefを返す
data
引数として与えられた$datarefを返す
return $dataref;
done
undefを返す

■コールバック関数の失敗

と、言うわけで、早速コールバック関数を定義してみました。
my $funcref = sub {
    my ($self, $phase, $dataref, $funcparam) = @_;
    if($phase eq 'content-length'){
        return length($xml);
    }elsif($phase eq 'content'){
        return $xml;
    }elsif($phase eq "data"){
        return $dataref;
    }
    return undef;
};
my $http = new HTTP::Lite;
$http->set_callback($funcref ,$xml);
XMLデータはコールバック関数に引数で渡してますが、
面倒なのでCGI.pmから取得した値をそのまんま使ってます。
これで出来るだろう・・・

って失敗!?

上のコードだと動きません。
リクエストにすら行かないのね。
どうも、「content」フェーズで無限ループに入ってるみたいです。
HTTP::Liteのソースを見てみると、
while (my $content = &$callback_func($self, "content", undef, @$callback_params)) {
となってます。
恐らく、「戻り値がなくなるまで続ける」と言う処理でしょう。
つまり、
return $xml;
だけだと、XMLデータを繰り返し繰り返し何度も何度も送りつけると言う、
嫌がらせプログラムだと言うことです。
これは危ない。事前にローカル環境でテストしてよかった。

■コールバック関数の完成

と、言うわけでちょっと改造。
my $funcref = sub {
    my ($self, $phase, $dataref, $funcparam) = @_;
    if($phase eq 'content-length'){
        return length($xml);
    }elsif($phase eq 'content'){
        my $ret_xml = $xml;
        undef $xml;
        return $ret_xml;
    }elsif($phase eq "data"){
        return $dataref;
    }
    return undef;
};
一回$xmlを返したら、もう要らないので消しましょう。
少々乱暴ですが、これで2回目以降はundefが返され、
XMLデータが1回だけ、ウェブサービスに送られるようになります。

■レスポンスの表示

さて、これでXMLデータをウェブサービスに渡せるので、
後は気兼ねなくリクエストを送るだけです。
my $req = $http->request($url) || die "request failed.";
こんな感じで。
$reqにはHTTPステータスが入ってくるみたいですね。
後はこれを表示するわけですが、
念のためコンテントタイプも明記しときましょう。
print "Content-type: application/xml¥n¥n";
print $http->body();
こんな感じで。
body()」メソッドで、HTTPのボディを表示するそうです。
HTMLファイルの「BODY」タグ内ではないことに注意だとか。

■まとめ

最終的に、コードはこんな感じ。
my $http = new HTTP::Lite;

my $funcref = sub {
    my ($self, $phase, $dataref, $funcparam) = @_;
    if($phase eq 'content-length'){
        return length($xml);
    }elsif($phase eq 'content'){
        my $ret_xml = $xml;
        undef $xml;
        return $ret_xml;
    }elsif($phase eq "data"){
        return $dataref;
    }
    return undef;
};

if($xml){
    $http->set_callback($funcref ,$xml);
}

$http->{HTTP11} = 1;

my $req = $http->request($url) || die "request failed.";
print "Content-type: application/xml¥n¥n";
print $http->body();
これで完璧。
xml_proxy.cgiは無事完成といっていいでしょう。
あとは、SWFファイルをアップロードして動作確認するのみ。

スポンサーサイト
- | 個別記事 | - | -
ドミノ・ピザでも食べながら、コメントをどうぞ。
コメントする









 
トラックバック
この記事のトラックバックURL(記事と無関係なトラックバックは削除される可能性があります)
※記事が投稿されてから30分の間、トラックバックを行うことが出来ません。
http://tarotaro.jugem.cc/trackback/1543
 

Copyright (C) 2004 paperboy&co. All Rights Reserved.

Powered by "JUGEM"