WebService::Simple(v0.16) で POST するときにハマってしまったことがあります。
前提
WebService::Simple は、コンストラクタにパラメータを設定しておけば、後段で get や post を呼び出すときにそのパラメータを自動的に付加してくれる便利機能があります。
例えば、WebService::ReadItLater を WebService::Simple を継承する形で作るときに、コンストラクタで以下のように設定すれば、以後のリクエストにおいて apikey などを明示的に付加する必要はありません。
# in WebService::ReadItLater my $self = $class->SUPER::new( params => { apikey => $apikey, username => $user, password => $pass, }, %$args, );
問題
問題というのは、上記のように設定した項目が、POST をするときは BODY ではなく、HTTP ヘッダにあらわれてしまうことです。下記は HTTP::Request を Dumper でダンプしたときの内容。password や username がヘッダに表われてしまっています。
'_headers' => bless( { 'user-agent' => 'libwww-perl/5.831', 'hash(0x10094acb8)' => undef, 'content-type' => 'application/x-www-form-urlencoded', 'password' => 'password' 'content-length' => 0, 'apikey' => 'hogehoge' 'username' => 'fugafuga' }, 'HTTP::Headers' ),
原因
WebService::Simple は LWP::UserAgent の子クラスの形で実装されていて、POST するときはもちろん LWP::UserAgent::post を呼び出しています。LWP::UserAgent::post には @params をパラメータとして渡していますが、コメントに記載のある通り、@params の最初あたりには「前提」欄で触れたパラメータが設定されます。
# WebService::Simple # default parameters must come *before* @params, so unshift instead # of push unshift @params, %{ $self->basic_params }; my $response = $self->SUPER::post( $uri, @params ); }
ただ、この場合 LWP::UserAgent::post は、HTTP BODY ではなくヘッダに情報を付加してしまいます(実際には HTTP::Request::Common がリクエストを組み立ててる)。
そのため、WebService::Simple の子クラスで
$self->post( 'send' => { read => $json } );
とした場合、{ read => $json } は HTTP BODY ではなく、HTTP ヘッダの方に挿入されてしまう。
解決策
これで良いのかしら。という感じのパッチを書いた。
たぶんこれで、get と同じ形で post が呼べるはず。
--- Simple.pm.orig 2009-06-13 11:15:18.000000000 +0900 +++ Simple.pm 2009-12-13 20:14:45.000000000 +0900 @@ -173,7 +173,19 @@ } sub post { - my ( $self, $url, @params ) = @_; + my $self = shift; + my ( $url, @params ); + + if ( ref $_[0] eq 'HASH' ) { + $url = ""; + @params = %{ shift @_ }; + } + else { + $url = shift @_; + if ( ref $_[0] eq 'HASH' ) { + @params = %{ shift @_ }; + } + } # XXX - do not include params my $uri = $self->request_url( @@ -181,10 +193,11 @@ extra_path => $url ); + my @headers = @_; # default parameters must come *before* @params, so unshift instead # of push unshift @params, %{ $self->basic_params }; - my $response = $self->SUPER::post( $uri, @params ); + my $response = $self->SUPER::post( $uri, {@params}, @headers ); if ( !$response->is_success ) { Carp::croak( "request to $url failed: " . $response->status_line );