F#+WCFでRESTfulなサービスをこしらえる・MessageContractについて想いをめぐらす

今日はメッセージについて考えます

WCFの持つ4つのコントラクト

  • ServiceContract
  • OperationContrace
  • MessageContract
  • DataContract

の中のメッセージコントラクト(MessageContract)について考えます。

メッセージとは「サーバー・クライアント間でやりとりするデータ」を定義したものです。
この説明だけではDataContractと何が違うのか困惑しますが、

  • DataContract はメッセージを構成する要素
  • MessageContract は「サーバーへのリクエスト・レスポンスのデータ」みたいにもう少しざっくりした概念

と考えています。

そしてメッセージには大きく「型付きメッセージ」と「型無しメッセージ」というものがあります。

型付き!

型付き!!!!



「型付きと言えばF#の出番だ!!!!」


↑と思った方、ちょっとはいるんじゃないでしょうか。
ここで残念なお知らせです。
「型付きメッセージ」の「型」はSOAP等のヘッダーを意味するようで、SOAPのようにヘッダー、ボディを意識したメッセージが必要な場合は用いられるそうですが、今回のようにRESTfulなサービスにはあまり出番はないと考えています。
あと僕が型付きメッセージについて良く知らないという事情もあります。
そういうわけで「型付き」という言葉に過剰反応してしまった方は残念でした。僕らの悪い癖です。


ちなみに型付きメッセージを定義するにはこのようにMessageContractAttributeを付与します(C#)。

[MessageContract]
public class MyMessage
{
  [MessageHeader(MustUnderstand=true)]
  public DateTime SetDataTime;
  [MessageBody(Order=0)]
  public MyBody body;
} 

[DataContract]
public class MyBody{
...
}

[ServiceContract]
public interface IMyService
{
  [OperationContract]
  Message MyOperation(Message message);
} 

MessageContractAttribute を付与したMyMessage が型付きメッセージの定義です。
またMyOperationの引数、返り値のMessage 型は、全てのメッセージのメタ概念です。(Object型に相当)


そんな感じで、「型無しメッセージ」について詳しく見ていきましょう

「型無しメッセージ」でメッセージを学ぶ


型無しメッセージは、ヘッダーを持たない、ボディのみで構成されるメッセージです。型無しメッセージを使用する場合は、
型付きメッセージで行ったようなMessageContractを明示的に定義するといったことはなく、オペレーションの引数、返り値に直接データコントラクトを指定することで暗黙的にMessageContractとして扱われます。
ようするに今までやって来たことは型無しメッセージの使用していたのです。

[<ServiceContract>]  
type IGetDataService = interface   
  [<WebGet(UriTemplate="temp/{value}")>]  
  [<OperationContract>]  
  abstract GetData : value:string -> string  

  ...

  [<WebGet(ResponseFormat = WebMessageFormat.Xml, UriTemplate="temp3/{value}")>]  
  [<OperationContract>]  
  abstract GetData3 : value:string -> MyData

  ...


  [<WebGet(ResponseFormat = WebMessageFormat.Json, UriTemplate="temp6/{value}/value/{value2}")>]  
  [<OperationContract>]  
  abstract GetData5 : value:string*value2:string -> string

  [<WebGet(ResponseFormat = WebMessageFormat.Json, UriTemplate="temp5/{value}/value/{value2}")>]  
  [<OperationContract>]  
  abstract GetData6 : value:string -> value2:string -> string
end  

この例では、

  • GetData は引数として「string型のデータvalueを持つ型無しメッセージ」、返り値として「string型のデータを持つ型無しメッセージ」
  • GetData3 は引数として「string型のデータvalueを持つ型無しメッセージ」、返り値として「MyData型のデータを持つ型無しメッセージ」
  • GetData5 は引数として「string型のデータvalueとvalue2とのタプルを持つ型無しメッセージ」、返り値として「string型のデータを持つ型無しメッセージ」

と解釈できます。ここでいう「x型のデータ」とはもちろんDataContractである事が前提です。

解釈に困るのがGetData6で、

  • GetData6 は引数として「string型のデータvalueとvalue2を持つ型無しメッセージ」、返り値として「string型のデータを持つ型無しメッセージ」

と解釈して良いのか否か判断が分かれるところです。
F#は基本的に引数を一つしか取らず、カリー化をすることによって複数個の引数を取っています。
そのため、GetData6のメッセージがどんな風なのか興味が湧くところです。
またクライアント側でサービスを利用する場合は、半適用等が出来るのでしょうか。
クライアントを作成する時に確認したいと思います。

まとめ

「型付きメッセージ」はSOAPで使う。RESTは「型無しメッセージ」だけで大丈夫かも。
型無しメッセージは明示的にMessageContractを定義する必要がないので楽。