2016-09-01 2 views
1

初めての質問者です。.NET上のベンダーAPIにインタフェースするPerl SOAP :: Lite

私は、Perlと私たちのベンダーの1つであるLogRhythmが提供するSOAP APIとのインターフェースに取り組んでおり、少しのロードブロックを打ったようです。

私はAPIとのインターフェイスができており、もともと遭遇した認証の問題を克服しました。 SOAP呼び出しにパラメータを渡す必要があるときに、SOAP Envelope Bodyで名前空間のタグ付けに問題が発生しています。

ベンダーが提供するAPIは、.NETとWCFをベースにしています。

私のプロトタイプのコードは、現在、以下のようになります。

SOAPAction: "http://www.logrhythm.com/webservices/LookupService/GetClassificationsByType" 

<?xml version="1.0" encoding="UTF-8"?> 
<SOAP-ENV:Envelope 
    SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" 
    xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" 
    xmlns:msc="http://schemas.microsoft.com/ws/2005/12/wsdl/contract" 
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
    xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" 
    xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" 
    xmlns:tns="http://www.logrhythm.com/webservices" 
    xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" 
    xmlns:wsa10="http://www.w3.org/2005/08/addressing" 
    xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata" 
    xmlns:wsap="http://schemas.xmlsoap.org/ws/2004/08/addressing/policy" 
    xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl" 
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
    xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" 
    xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" 
    xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" 
    xmlns:wsx="http://schemas.xmlsoap.org/ws/2004/09/mex" xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 
    <SOAP-ENV:Header> 
     <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> 
      <wsse:UsernameToken> 
       <wsse:Username>reporting</wsse:Username> 
       <wsse:Password>password</wsse:Password> 
      </wsse:UsernameToken> 
     </wsse:Security> 
    </SOAP-ENV:Header> 
    <SOAP-ENV:Body> 
     <tns:GetClassificationsByType> 
      <classificationType xsi:type="xsd:int">2000</classificationType> 
      <classificationTypeSpecified xsi:type="xsd:boolean">true</classificationTypeSpecified> 
     </tns:GetClassificationsByType> 
    </SOAP-ENV:Body> 
</SOAP-ENV:Envelope> 

結果を返しません:

use strict; 
no strict "refs"; 

use Data::Dumper; 
use IO::Socket::SSL qw (SSL_VERIFY_NONE) ; 
use SOAP::Lite; 
use Tie::IxHash; 

# Username and Password 
my $sUID = "<API UserID>"; 
my $sPWD = "<API Password>"; 

# WSDL definition URI. Using a cached local copy using file:/... also works 
my $LookupService_wsdl = 'https://melcapi01.soc.ipsec.net.au/LogRhythm.API/Services/LookupServiceBasicAuth.svc?singleWsdl'; 
my $lrns = 'http://www.logrhythm.com/webservices'; 

# Ensure that a consistent xmlns:soap value is used, without this we get inconsistent results. 
$SOAP::Constants::PREFIX_ENV = 'SOAP-ENV'; 

# Don't validate SSL Certificate while testing 
IO::Socket::SSL::set_defaults(SSL_verify_mode => "SSL_VERIFY_NONE"); 

# Construct the security header 
my %authHash; 
tie %authHash, "Tie::IxHash"; 
%authHash = (
      Username => SOAP::Data->type('' => $sUID)->prefix('wsse'), 
      Password => SOAP::Data->type('' => $sPWD)->prefix('wsse'), 
    ); 

my $wsse = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"; 
my $securityHeader = SOAP::Header->new(
    name => 'Security', 
    uri => $wsse, 
    prefix => 'wsse', 
    value => \SOAP::Data->new(
     name => 'UsernameToken', 
     prefix => 'wsse', 
     value => \%authHash, 
    ) 
); 

# Error handling 
on_fault => sub { my($soap, $res) = @_; 
    die ref $res ? $res->faultstring : $soap->transport->status; 
}; 

# Define the SOAP Instance 
my $lrapi = SOAP::Lite 
    -> readable (1) 
    -> service($LookupService_wsdl) 
    -> on_action(sub {return $action}); 

# Set the default Namespace 
$lrapi->default_ns($lrns); 

# Actually get data from the LookupService 
my $result; 

# Build up the parameters 
my @classificationType = (SOAP::Data->new(name =>'classificationType', value => 2000)); 

$result = $lrapi->GetClassificationsByType($securityHeader); 

print Dumper $result; 

これは、次のSOAPリクエストになります。 .NET WebService Studioを使用してDoctorとRequest Doctorを行ったところ、問題がSOAP Bodyの要素のxmlns識別子が見つからないことが原因であることが判明しました。

SOAPAction: "http://www.logrhythm.com/webservices/LookupService/GetClassificationsByType" 

<?xml version="1.0" encoding="UTF-8"?> 
<SOAP-ENV:Envelope 
    SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" 
    xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" 
    xmlns:msc="http://schemas.microsoft.com/ws/2005/12/wsdl/contract" 
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
    xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" 
    xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" 
    xmlns:tns="http://www.logrhythm.com/webservices" 
    xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" 
    xmlns:wsa10="http://www.w3.org/2005/08/addressing" 
    xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata" 
    xmlns:wsap="http://schemas.xmlsoap.org/ws/2004/08/addressing/policy" 
    xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl" 
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
    xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" 
    xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" 
    xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" 
    xmlns:wsx="http://schemas.xmlsoap.org/ws/2004/09/mex" xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 
    <SOAP-ENV:Header> 
     <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> 
      <wsse:UsernameToken> 
       <wsse:Username>reporting</wsse:Username> 
       <wsse:Password>password</wsse:Password> 
      </wsse:UsernameToken> 
     </wsse:Security> 
    </SOAP-ENV:Header> 
    <SOAP-ENV:Body> 
     <tns:GetClassificationsByType xmlns="http://www.logrhythm.com/webservices"> 
      <classificationType xsi:type="xsd:int">2000</classificationType> 
      <classificationTypeSpecified xsi:type="xsd:boolean">true</classificationTypeSpecified> 
     </tns:GetClassificationsByType> 
    </SOAP-ENV:Body> 
</SOAP-ENV:Envelope> 

様々なサポートリクエストは、これは、手動でSOAP :: Liteのオブジェクトにdefault_nsを設定することによって解決されるべきであることを示している、しかし、これは私のために働いていません。

SOAPリクエストは、次のようになります。 。

私は何が起こっているかを確認するためにSOAP::Lite(1.20)のソースを掘り行っている、そしてSOAPメソッド呼び出しを行う前に1に設定されている{'_use_default_ns'}プロパティは、処理中に0にリセットされますように見えるようですコールの

また、call()メソッドを使用して目的の結果を得るために、手動でSOAP :: Liteオブジェクトを作成しようとしましたが、私は自動ネームスペースの数に問題があります。現在使用しているservice()のメカニズムの結果として追加されました。 call()メソッドを使用する場合は、service()を使用してオブジェクトを設定できないようです。

私はこの時点で迷っているので、私は誰か他の提案をしていますか?

ご協力いただければ幸いです。

免責事項:私はプログラマー/デベロッパーではなく、プログラミング能力のあるシニアセキュリティエンジニア/コンサルタント/アーキテクトですので、コードがひどく構成されている場合は事前にお詫びします。これはSOAPを手に入れた最初の試みです。

+0

これは顧客のためのものですか?ソリューションの汚れはどれくらい快適でしょうか?あなたのコードは 'use warnings'を持っていませんが、私の評価は非常に乱雑ではないということです。それは単にSOAP :: Liteであり、現実のPerlのようには見えません。 – simbabque

+0

LogRhythm APIのドキュメントはひどいだけではなく、まったく誤解を招くものです。いくつかの関数が、文書化されたものとは全く異なる順序で引数を取ることを期待する。 –

答えて

1

これはちょっとしたハックな解決策です。これは、SOAP :: Liteクライアントのトランスポート層にフックし、各送信リクエストを変更します。

私はグーグルで見つかったランダムなWebサービスを使用しているため、サンプルコードをコピーして貼り付けて、資格情報なしで実行できます。 The serviceは米国国立気象サービスによって運営されています。私の例では、エンドポイントを使用して、米国の郵便番号の緯度/経度座標を返しています。

use strict; 
use warnings 'all'; 
use feature 'say'; 
use SOAP::Lite; 

my $client = SOAP::Lite->new(
    proxy => 'http://graphical.weather.gov/xml/SOAP_server/ndfdXMLserver.php' 
); 
$client->service('http://graphical.weather.gov/xml/DWMLgen/wsdl/ndfdXML.wsdl'); 

# the transport layer is a subclass of LWP::UserAgent 
$client->transport->add_handler(
    request_prepare => sub { 
     my ($request, $ua, $h) = @_; 
     my $content = $request->content; 

     # this is the content before modification 
     say 'before: ' . $content; 

     # modify the content with a simple regex substitution 
     $content =~ s{<LatLonListZipCode>}{<LatLonListZipCode xmlns="http://example.org">}; 
     $request->content($content); 

     # so it doesn't throw a 'Content-Length header value 
     # was wrong, fixed' warning 
     $request->header('Content-Length' => length $content); 

     # this is the content after modification 
     say 'after: ' . $content; 

     # the return value is ignored 
    } 
); 

my $res = $client->LatLonListZipCode('90210'); 
say $res->result; 

説明 SOAP通信、SOAP::Transport::HTTP::Clientこの種のSOAP::Transport層は、LWP::UserAgentのサブクラスであるので、これは動作

。 UserAgentは非常に柔軟性があり、特定のイベントに対して多くのdifferent handlersを提供します。私たちが使用しているものはrequest_prepareです。これにより、HTTP::Requestが有線(または無線など)で送信される前に変更することができます。

ハンドラは要求が送信される前に呼び出され、要求に応じて任意の方法で要求を変更できます。たとえば、特定のヘッダーを特定の要求に追加するために使用できます。私たちが追加されているハンドラで

、我々は要求の内容を取得し、<LatLongListZipCode>タグにのxmlnsを追加するために正規表現の代替を使用します。 parsing XML with regex is not a good ideaのように、XMLのように見えるテキストを変更するのは、その時点で何が入っているのか気にしないためです。

リクエストが既に作成されているため、のコンテンツフィールドのヘッダーフィールドを更新する必要があります。自動的に更新されますが、同時に警告が表示されます。これを単に更新することで省略することができます。

ここでは、プログラムの出力を3つの部分に分割しています。変更前のリクエストの

コンテンツ

<?xml version="1.0" encoding="UTF-8"?><soap:Envelope soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><soap:Body><LatLonListZipCode><c-gensym3 xsi:type="xsd:int">90210</c-gensym3></LatLonListZipCode></soap:Body></soap:Envelope> 

変更

<?xml version="1.0" encoding="UTF-8"?><soap:Envelope soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><soap:Body><LatLonListZipCode xmlns="http://example.org"><c-gensym3 xsi:type="xsd:int">90210</c-gensym3></LatLonListZipCode></soap:Body></soap:Envelope> 

応答の内容

<?xml version='1.0'?><dwml version='1.0' xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xsi:noNamespaceSchemaLocation='http://graphical.weather.gov/xml/DWMLgen/schema/DWML.xsd'><latLonList>34.0995,-118.414</latLonList></dwml> 
後のリクエストの内容

明らかに、変更は機能しましたが、リクエストはまだ処理されていました。私が選んだサービスは、そのxmlns属性を気にしません。 Webサービスのためのあなたのユースケースについては

を採用

、あなたは箱から出して動作しないだけの要求が変化し得ることを確認する必要があります。それらのすべてを変更する必要がある場合は、これと同じくらい簡単かもしれません。クライアントを構築した後は、一度やり直してください。

$lrapi->transport->add_handler(
    request_prepare => sub { 
     my ($request, $ua, $h) = @_; 
     my $content = $request->content; 

     # modify the content with a simple regex substitution 
     $content =~ s{<tns:GetClassificationsByType>}{<tns:GetClassificationsByType xmlns="http://www.logrhythm.com/webservices">}; 
     $request->content($content); 

     # so it doesn't throw a 'Content-Length header value was wrong, fixed' warning 
     $request->header('Content-Length' => length $content); 
    } 
); 

免責

私が言ったように、これは実用的なソリューションです。しかし、SOAPは非常に複雑で、すべての権利を得るクライアントはごくわずかです。また、右よりも多くのことを行う多くのサーバーがあります。時には実践主義が最善の行動序列です。もちろん、サーバーの動作が変わる可能性があります。その場合、これが壊れる可能性があります。しかし、それは私が信じる価値があるリスクです。

+1

このような詳細な回答をまとめていただきありがとうございます。私はあなたが何を示唆しているのかを理解していると思うし、コードを突き刺すことから、それがうまくいくように見える。 –

+0

ようこそ。私の意見では、この種のことについて最も難しい部分は、あなたが見なければならないところを見つけることです。私はおそらく、ある時点で自分自身でここに戻って参考になるだろう。あなたは毎日私が推測する限り、あなたの頭の中でこのような詳細を保つことはありません。 :-) – simbabque

+0

唯一の潜在的な問題は、APIのいくつかのサービスのそれぞれが発行する "メソッド"の数であり、その要求を処理する必要がありますが、それに対処する方法についていくつか考えています。いずれにしても、これで問題が解決された場合は、ここで更新してください。再度、感謝します。 –

1

私のオリジナル投稿と@simbabqueのフォローアップとして、これをテストして、@ simbabqueの提案が機能することを確認できます。

利用可能なメソッドの拡張リスト(6つの異なるサービス定義があり、利用可能な合計59のメソッドがあります。これはAPIの将来のエディションで増加する可能性があります...)を実装しました。 add_handlerは、次のように:

$lrapi->transport->add_handler(
    request_prepare => sub { 
     my ($request, $ua, $h) = @_; 
     my $content = $request->content; 

     # Get the SOAPAction Header from the current request 
     my $soapAction = $request->header('SOAPAction'); 

     # Parse the SOAPAction Header, and determine the method being called 
     $soapAction =~ m/^\"(http:\/\/[\w\.\/]+)\/(\w+)\/(\w+)\"$/; 
     my ($lruri, $lrsvc, $lrmethod) = ($1, $2, $3); 

     # Modify the content with a simple regex substitution 
     $content =~ s{<tns:$lrmethod>}{<tns:$lrmethod xmlns="$lruri">}; 
     $request->content($content); 

     # So it doesn't throw a 'Content-Length header value was wrong, fixed' warning 
     $request->header('Content-Length' => length $content); 
    } 
); 

私はまた、いくつかの値を持っていたことを確認するために$lrapi->transportにプロパティを設定する必要がありました。私は、この目的を果たすためにトランスポートのプロキシプロパティを設定するだけです。何らかの理由により、WSDLサービスを使用してエラーが発生した場合、トランスポートプロパティは同じ方法で構築されません。

私はこれを達成するために、add_handlerを呼び出す前に、次のように似て何かを使用:

$lrapi->proxy ("https://melcapi01.soc.ipsec.net.au/LogRhythm.API/Services/LookupServiceBasicAuth.svc"); 

これはadd_handlerが正しく機能するように、適切に構築されて$lrapi->transport対象になります。

もう一度@simbabqueの解決に感謝します。

この記事が他の人にも役立つことを願っています。

+0

喜んで助けました。あなたはあなたの正規表現で '' 'をエスケープする必要はありません。(' s {} {} 'の中で以下のように)別の区切り文字を使うと、スラッシュの前のバックスラッシュも取り除くことができます'[]'の中に '' 'をエスケープするだけでいいのですが、ドットはメタ文字ではなくドットであるので、' m {^ "(http:// [\ w ./] +)/(\ w +)/(\ w +) "$}"これは読みやすい方法です。 – simbabque

関連する問題