連載
» 2003年04月24日 00時00分 公開

Webサービスのキホン(5):WSDL文書が持つ二層構造の前段部

Webサービスの基軸を構成するのは、SOAP、WSDL、UDDIの3つのテクノロジだ。この連載では、Webサービスの基本的な知識を身に付けるために、この3つのテクノロジの背景と仕様、機能などを分かりやすく解説していく。(編集局)

[吉田稔, 青木秀起,株式会社日本ユニテック]

WSDL文書の構造をおさらい

 前回「WSDL:Webサービスのインターフェイス情報」では、Webサービスのインターフェイス情報を記述するWSDL文書の基本的な構造を紹介した。今回は、WSDL文書の中に含まれる抽象的な定義と具体的な定義との関係や、WSDLの要素間の関係に注目しつつ、WSDL文書を構成するそれぞれの要素を解説していく。

 まずは、前回紹介したWSDL文書の構造をもう一度思い出していただこう。

図1 WSDL文書を構成する主要な要素 図中で箱が3重に表現されている要素は、複数回出現可能なことを表している。wsdl:message要素、wsdl:portType要素、wsdl:operation要素、wsdl:binding要素、wsdl:service要素とwsdl:port要素がそれに該当する 図1 WSDL文書を構成する主要な要素
図中で箱が3重に表現されている要素は、複数回出現可能なことを表している。wsdl:message要素、wsdl:portType要素、wsdl:operation要素、wsdl:binding要素、wsdl:service要素とwsdl:port要素がそれに該当する

 WSDLの最上位要素はwsdl:definitions要素であるが、その下位要素としてwsdl:types要素、wsdl:message要素、wsdl:portType要素(以上、抽象的な定義)、wsdl:binding要素、wsdl:service要素(以上、具体的な定義)が記述される。今回は抽象的な定義に該当する要素を中心に解説する。

最上位要素(wsdl:definitions要素)

 wsdl:definitions要素は、WSDL文書の最上位要素だ。Webサービスを定義するすべての要素は、wsdl:definitions要素の子孫要素になる

 wsdl:definitions要素には、name属性とtargetNamespace属性の2つの属性を指定できる。WSDL仕様ではname属性に何を指定すべきなのかを限定していないが、実際のWSDL文書を見ると、そのWSDL文書によってインターフェイスを記述しているWebサービス名を指定することが多いようだ。一方、targetNamespace属性は、WSDL文書の対象名前空間URIを明示するために使用される。WSDL文書の対象名前空間URIとは、そのWSDL文書内で定義される名前(後述するメッセージ名やポートタイプ名など)が属する名前空間を識別するためのURIのことだ。

 wsdl:definitions要素の例をリスト1に示す。このリストでは、Webサービスの名前として「Estimate」が、またこのWSDL文書の対象名前空間URIとして「http://unitec-denki.utj.co.jp/schema/2003/」が指定されている。

<wsdl:definitions
   name="Estimate"
   targetNamespace="http://unitec-denki.utj.co.jp/schema/2003/"
   xmlns=……>
      ……Webサービスのインターフェイス情報の定義……
</wsdl:definitions>
リスト1 wsdl:definitions要素の例

 wsdl:definitions要素では、WSDL文書全体で使用する名前空間を宣言しておく。WSDL文書で使われることの多い名前空間と、本連載で使う接頭辞を併せて以下の表1に示す。

本連載で使用する
名前空間接頭辞
名前空間URI 定義
wsdl http://schemas.xmlsoap.org/wsdl/ WSDL名前空間
wsdlsoap http://schemas.xmlsoap.org/wsdl/soap/ SOAPバインディングのための WSDL拡張性要素のための名前空間
soapenv http://schemas.xmlsoap.org/soap/envelope/ SOAP 1.1で定義されたエンベロープ名前空間
soapenc http://schemas.xmlsoap.org/soap/encoding/ SOAP 1.1で定義されたエンコーディング名前空間
xsd http://www.w3.org/2001/XMLSchema XML Schemaで定義されたスキーマ名前空間
xsi http://www.w3.org/2001/XMLSchema-instance XML Schemaで定義されたインスタンス名前空間
表1 WSDL文書で使われることの多い名前空間

 この記事の解説で使用するサンプルWSDL文書では、表1に加えて、WSDL文書の対象名前空間(名前空間URIは「http://unitec-denki.utj.co.jp/schema/2003/」)を示す名前空間接頭辞として“impl”を使うことにする。

Webサービス・インターフェイスの抽象的な定義

 WSDL文書では、Webサービスのインターフェイスを抽象的に定義しておき、それを具体的なインターフェイスとして定義し直すという、2段構えのインターフェイス記述を行うことを前回解説した。これは、抽象的に定義した部分だけを分離して、通信プロトコルやネットワークアドレスの異なる別のWebサービスの定義に再利用するための工夫だ。

インターフェイスを抽象的に定義する
Webサービスを利用するための出入り口のことをWSDLではポートと呼ぶ。WSDLでは、ポートやポートを通してやりとりされるメッセージのフォーマットを、まず抽象的に定義する。抽象的に定義するとは、ポートやメッセージを、特定の通信プロトコルやネットワークアドレスに依存しない形式で定義することだ。

抽象的なインターフェイスを具体的に定義し直す
続いてWSDLでは、抽象的に定義されたメッセージやポートに具体的な通信プロトコルやネットワークアドレスをバインドすることによって具体的に定義し直す。さらに、関連するポートをまとめたものをサービスとして定義する。

 これらの定義は、具体的にはWSDL文書内の各要素によって行われる。順に見ていこう。

(1)メッセージのフォーマットを定義する(wsdl:message要素)

 Webサービスにおいてやりとりされるメッセージには、サービス要求のためにWebサービスが受信する入力メッセージや、サービス結果を提供するためにWebサービスが送信する出力メッセージや、エラー情報を返すためにWebサービスが送信するフォルトメッセージがある。

 wsdl:message要素は、それらのメッセージのフォーマットを抽象的に定義する。ただし、Webサービスがやりとりするメッセージといっても、wsdl:message要素で定義するメッセージフォーマットとは、SOAPヘッダを含むSOAPメッセージ全体のことではなく、SOAPメッセージのペイロード、すなわちSOAP本体(Body)の内容のことだ。SOAP本体については、本連載の第2回「SOAPという封筒の内部構造」を参照してほしい。

 wsdl:message要素が定義する入出力メッセージやフォルトメッセージは、それぞれ0個以上のパート(part)と呼ぶデータの集まりで構成される。WebサービスによってRPC(Remote Procedure Call)を実現する場合、入力メッセージにはいくつかのパラメータが埋め込まれることになるが、ここで考えているパートとは、個々のパラメータに相当するデータのことだ。

 パートは、wsdl:message要素の子として出現するwsdl:part要素によって記述する。wsdl:part要素は、具体的にはパートの型を定義する。例えば、以下のメソッドをWebサービスとして公開するとしよう。

int getPrice(String Code, int Value)
リスト2 Webサービスとして公開するgetPriceメソッド

 このメソッドを利用するためにWebサービスへ送られる要求メッセージ(すなわちWebサービスの入力メッセージ)をWSDLで記述するとリスト3のようになる。

<wsdl:message name="getPriceRequest">
  <wsdl:part name="Code" type="xsd:string"/>
  <wsdl:part name="Value" type="xsd:int"/>
</wsdl:message> 
リスト3 getPriceメソッドの要求メッセージのWSDL記述

 リスト3を見ると、wsdl:message要素の子として出現するwsdl:part要素が、メソッドのパラメータ(CodeとValue)のデータの型を、XML Schemaのビルトインデータ型を使って定義していることが分かるだろう。

 wsdl:message要素で定義した抽象メッセージは、後で参照できるようにメッセージ名を付けておくことが必要だ。wsdl:message要素のname属性はメッセージ名を指定する属性だ。リスト3の場合、メッセージ名として「getPriceRequest」という名前が指定されている。

(2)型を定義する(wsdl:types要素)

 パートの型として、ユーザーが独自に定義する型を指定したいことがある。その場合、どこか別のところで型を定義して名前を付けておき、その名前をwsdl:part要素で参照する。wsdl:definitions要素の子として出現するwsdl:types要素は、型や要素を定義するために使用する

 WSDL仕様では、相互運用性を確保するため、types要素における型の定義をXML Schemaを使って行うことを推奨しているが、それ以外の型定義システムを使用することも許されている。以下に示すリストは、wsdl:types要素で定義した型を使ってメッセージのフォーマットを定義した例だ。

リスト4 wsdl:types要素の書き方 この例では、wsdl:types要素の中で定義された型「EstimateType」を、wsdl:part要素の中で使用している。型の名前空間接頭辞は「xsd1」 リスト4 wsdl:types要素の書き方
この例では、wsdl:types要素の中で定義された型「EstimateType」を、wsdl:part要素の中で使用している。型の名前空間接頭辞は「xsd1」

 リスト4を見ると、getPriceRequestというメッセージを構成するgetPriceParmというパートがwsdl:types要素で定義された型(xsd1:EstimaeType)を使用していることが分かるだろう。

 ここまで説明してきたwsdl:types要素、wsdl:message要素、wsdl:part要素の関係を整理すると図2のようになる。

図2 wsdl:types要素、wsdl:message要素、wsdl:part要素の関係 図2 wsdl:types要素、wsdl:message要素、wsdl:part要素の関係

メッセージを抽象的に定義する理由

 wsdl:message要素とwsdl:types要素は、あくまでデータフォーマットを抽象的に定義しているだけだ。従って、XML Schemaを使ってXML文書の要素に対してデータ型を定義するとしても、実際にやりとりされるメッセージがXML形式になるとは限らない。実際のデータフォーマットは、最終的にメッセージがどんなプロトコルにバインドされるかによって決まる(WSDLはさまざまなプロトコルに対するインターフェイスを記述できる)。例えばSOAPプロトコルにバインドされればメッセージはSOAPのエンコーディング規則に従ってXML形式にエンコーディングされるが、HTTP GETプロトコルにバインドされればメッセージはURLエンコードデータでWebサービスに送られることになる。

 WSDLではこうしたプロトコルに対する柔軟性を備えている。そのため、メッセージのデータフォーマットを抽象的に定義しておくことでメッセージの定義をさまざまなプロトコルにバインドでき、メッセージのフォーマット定義の再利用が可能になる。

 要素の説明を続けよう。

(3)抽象的なポートを定義する(wsdl:portType要素)

 WSDLは、メソッドに相当するものを操作(operation)と呼ぶ。wsdl:portType要素の子として出現するwsdl:operation要素は、抽象的な操作を記述するために使用される。操作は、その操作を呼び出すための入力メッセージと、処理結果を返すための出力メッセージ、エラー情報を通知するためのフォルトメッセージという3種類のメッセージで構成される

 wsdl:operation要素の子として出現するwsdl:input要素とwsdl:output要素とwsdl:fault要素は、それぞれのメッセージを記述するために使用する。それぞれのメッセージがどんなフォーマットになるかは、wsdl:message要素で定義したフォーマットを参照することによって指定する。

 関連する抽象的な操作の集合をポートタイプと呼ぶ。wsdl:portType要素は、ポートタイプを定義する要素だ。ポートタイプは、Java言語におけるインターフェイスに相当するもので、インターフェイスがいくつかのメソッドを持つようにポートタイプは0個以上の操作を持つことができる。

 ここまで説明してきたwsdl:portType要素やwsdl:operation要素の関係を整理すると図3のようになる。

図3 wsdl:portType要素の構成 wsdl:message要素の中で定義されたwsdl:part要素は、wsdl:operation要素の中のwsdl:input要素、wsdl:output要素、wsdl:fault要素の定義に利用される 図3 wsdl:portType要素の構成
wsdl:message要素の中で定義されたwsdl:part要素は、wsdl:operation要素の中のwsdl:input要素、wsdl:output要素、wsdl:fault要素の定義に利用される

子要素の組み合わせによってさまざまなパターンの操作を表現する

 wsdl:operation要素において、wsdl:input要素やwsdl:output要素の出現の仕方によって、さまざまなパターンの操作を表現できる。

 例えば各支店から本部サーバに月間報告書を送りつけるだけで、何の応答も求めない場合のように、クライアントからWebサービスにメッセージを一方的に送信する「一方向操作」を考えてみよう(図4)。

図4 一方向操作 クライアントからWebサービスに、メッセージを一方的に送信することを想定する 図4 一方向操作
クライアントからWebサービスに、メッセージを一方的に送信することを想定する

 この場合、クライアントからの入力メッセージを受信するだけの操作となるので、wsdl:operation要素の子としてwsdl:input要素が1個だけ出現するリスト5のような記述になる。

<wsdl:operation name="MonthlyReport">
  <wsdl:input message="impl:MonthlyReport"/>
</wsdl:operation>
リスト5 wsdl:operation要素での一方向操作の記述

 wsdl:input要素のmessage属性には、特定のメッセージ名(前述したwsdl:message要素のname属性値のこと)を指定してメッセージのフォーマットを参照する。リスト5では、「impl:MonthlyReport」というメッセージ名を持つ抽象的なメッセージフォーマットを指定している。

 次に、商品コードと商品数を取引先サーバに渡すと見積価格が返される場合のように、Webサービスがクライアントからのメッセージを受信し、対応する応答メッセージを送信する「要求/応答操作」を考えてみよう(図5)。

図5 要求/応答操作 Webサービスがクライアントからのメッセージを受信し、対応する応答メッセージを送信することを想定する 図5 要求/応答操作
Webサービスがクライアントからのメッセージを受信し、対応する応答メッセージを送信することを想定する

 応答/要求操作のためのwsdl:operation要素の構造は、一方向操作の場合よりも少し複雑になる。wsdl:operation要素の子要素を整理すると以下のようになる。

(1)Webサービスがクライアントから受け取る入力メッセージを記述するwsdl:input要素

(2)Webサービスがクライアントへ送信する出力メッセージを記述するwsdl:output要素

(3)必須ではないがWebサービスでエラーが発生したときにエラー情報をクライアントへ送信するフォルトメッセージを記述する、0個以上のwsdl:fault要素

 wsdl:operation要素はリスト6に示す記述になるだろう。

<wsdl:portType name="Estimate">
  <wsdl:operation name="getPrice" parameterOrder="Code Value">
    <wsdl:input message="impl:getPriceRequest" />
    <wsdl:output message="impl:getPriceResponse" />
    <wsdl:fault message=……>
      ……
  </wsdl:operation>
</wsdl:portType>
リスト6 要求/応答操作の記述

 リスト6において、wsdl:input要素は「getPriceRequest」というメッセージ名を持つメッセージフォーマットを、wsdl:output要素は「getPriceeResponse」というメッセージ名を持つメッセージフォーマットを参照している。

パラメータの出現順序を明示する

 ところで、リスト6を見るとwsdl:operation要素にparameterOrder属性が付与されていることが分かる。parameterOrder属性は、wsdl:operation要素がRPCの操作を記述する場合にパラメータの順序を示すための属性だ。parameterOrder属性は、1個の空白で区切ったパート名(パート名はwsdl:part要素のname属性で指定されている)をパラメータの順序どおりに並べて指定する。リスト6の場合、wsdl:operation要素が記述するRPC関数のパラメータは、「Code」と「Value」の順番に並ぶことを示している。

操作を抽象的に定義するとは

 ここまで述べた操作は抽象的に定義された操作だ。従って、メッセージの実際の通信方法は、どんなプロトコルにバインドするかに依存する。例えば、要求/応答操作であっても、HTTP要求/応答を使って単一の通信で送受信を実現する方法もあるだろうし、2つのSMTPメッセージを使って独立した通信で送受信を実現する方法もあるだろう。WSDLでは、具体的なプロトコルを別の場所で定義し、それを抽象的な定義にバインドするという仕組みをとっている。


 今回はWSDLにおける抽象的な定義の部分を中心に見てきた。次回は、ここまでの定義を、Webサービスで利用する具体的なインターフェイスに再定義する方法を解説しよう。


Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。