14

Delphi XE2 を使用して、かなり大規模な SOAP サービスと通信しています。wsdl のインポートに成功し、すべて正常に動作しています。しかし、私は自分が似たようなコードをたくさん書いていることに気づきました。Web サービスを呼び出す汎用メソッドが必要です。また、呼び出しの種類ごとに非常に多くのコードを書かなければならないため、現在のように自分のコードをマルチスレッド化するのは難しいと感じています。

週末のプログラマーなので、Delphi の内外をマスターするにはほど遠いですが、少なくとも RTTI についてはかなり理解していると思います。

Web サービスには約 700 の異なるメソッドがあり、それがほとんどの問題です。wsdl から生成されたコードには、次のようなメソッドがあります。

function  addPhone(const Params: addPhone): addPhoneResponse; stdcall;
function  updatePhone(const Params: updatePhone): updatePhoneResponse; stdcall;
function  getPhone(const Params: getPhone): getPhoneResponse; stdcall;
function  removePhone(const Params: removePhone): removePhoneResponse; stdcall;
function  listPhone(const Params: listPhone): listPhoneResponse; stdcall;
function  addStuff(const Params: addStuff): addStuffResponse; stdcall;
function  updateStuff(const Params: updateStuff): updateStuffResponse; stdcall;
...
... about 700 more of the above

基本的に、処理できるものは約 700 種類あり、それらすべてに対して add、update、get、remove、および list メソッドがあります。各呼び出しには、SOAP 要求へのパラメーターとして使用される対応するクラスがあります。上記のように、応答に対応するクラスもあります。

クラスは次のようになります (非常に単純化されています)。

addStuff = class
  private
    FStuff: string;
  published
    property stuff: string  Index (IS_UNQL) read FStuff write FStuff;
  end;

したがって、Web サービスを呼び出すときは、たとえば次のようにします。

procedure CreateStuff;
var
    req:    addStuff;
    res:    addStuffResponse;
    soap:   MyWebServicePort;
begin
    // Use the function in the wsdl-generated code to create HTTPRIO
    soap := GetMyWebServicePort(false,'',nil);
    // Create Parameter Object
    req := addPhone.Create;
    req.stuff := 'test';
    // Send the SOAP Request
    res := soap.addStuff(req);
end;

(はい、try..finally とそこに Free もあるはずです :-) )

次に、コードの別の場所で別のメソッドを呼び出す必要があります。

procedure listStuff;
var
    req:    listStuff;
    res:    listStuffResponse;
    soap:   MyWebServicePort;
begin
    // Use the function in the wsdl-generated code to create HTTPRIO
    soap := GetMyWebServicePort(false,'',nil);
    // Create Parameter Object
    req := listPhone.Create;
    req.stuff := 'test2';
    // Send the SOAP Request
    res := soap.listStuff(req);
end;

パラメータは常に呼び出すメソッドと同等の名前を持つクラスであることがわかっているため、呼び出しを動的に呼び出すために、以下のメタコードのようなことを実行できるようにしたいと考えています。RTTIマジックが必要だと思いますが、それを行う方法を見つけることができませんでした:

procedure soapRequest(Param: Something; var Response: Something);
begin
  soap := GetMyWebServicePort(false,'',nil);
  Response := soap.DynamicInvoke(Param.ClassName, Param);
end

次に、次のようなことができます。

soapRequest(VarOfTypeAddStuff,VarOfTypeAddStuffResponse)
soapRequest(VarOfTypeListStuff,VarOfTypeListStuffResponse)
...

Web サービスへの呼び出しを簡素化する方法を知っている人はいますか?

4

1 に答える 1

4

何週間も自分で解決しようとしていた質問を投稿してから数時間後に、突然自分で解決できるのは本当に奇妙です...私はSOを見回して触発され、これを見つけました途中で私を助けてくれました:Delphi - Invoke Record method per name

メソッド自体と同じクラス名を持つパラメーターを使用してメソッドを呼び出しているため、私のシナリオはやや具体的です。また、パブリック Web サービスと通信する単純なバージョンも作成しました。誰かが興味を持っている場合は、そのコードをhttp://www.hook.se/delphi/SoapDynamicInvoke.zipで入手できます。Web サービスにさまざまなメソッドが多数ある場合にのみ、動的メソッド呼び出しを行うことが適切であるため、これは役に立たない例です。それにもかかわらず、それは誰かにとって興味深いかもしれません:-)

以下は、私のWebサービスでこれをどのように解決したかです。前述のように、それは非常に具体的であり、コードをより一般的にすることができますが、これは私にとってはうまくいきます。

TRemotable オブジェクトでこのメソッドが呼び出され、オブジェクトのクラス名と同じ名前のメソッドで Web サービスが呼び出されます。

function soapRequest(Param: TRemotable): TValue;
var
  soap: AXLPort;
  C: TRttiContext;
  T: TRttiType;
  M: TRttiMethod;
  SoapParam: TArray<TValue>;
  TVres: TValue;
  soap: MyWebServicePort;
begin
  // Use the function in the wsdl-generated code to create HTTPRIO
  soap := GetMyWebServicePort(false,'',nil);  C := TRttiContext.Create;
  T := C.FindType('MyWebService.MyWebServicePort');
  M := T.GetMethod(Param.ClassName);
  SetLength(SoapParam,1);
  SoapParam[0] := TValue.From(Param);
  TVres := M.Invoke(TValue.From<IInterface>(soap), SoapParam);
  Result := TVres;
end;

上記の関数を使用するには:

procedure DoSomeSoapCalls(Sender: TObject);
var
  req1: getStuff
  res1: getStuffResponse;
  req2: addStuff;
  res2: addStuffResponse;
  res:  TValue;
begin
  //Request #1
  req1 := getStuff.Create;
  req1.stuffToGet := 'abc';
  try
    res := soapRequest(req1);
    res1 := getStuffResponse(res.AsObject);
  finally
    req1.Free;
  end;
  Writeln(res1.someproperty);
  FreeAndNil(res1);

  //Request #2
  req2 := addStuff.Create;
  req2.StuffToAdd := 'cde';
  try
    res := soapRequest(req2);
    res2 := addStuffResponse(res.AsObject);
  finally
    req2.Free;
  end;
  Writeln(res2.result);
  FreeAndNil(res2);
end;

少しタイプキャストが必要ですが、私の場合はそれでかなり安全だと思います。これに関して他にコメント/提案はありますか?つまり、これは機能しますが、おそらくそれを強化する方法があります。

乾杯、

ダン

于 2012-06-28T22:51:29.210 に答える