F#+WCFでRESTfulなサービスをこしらえる・MessageContractについて想いをめぐらす
今日はメッセージについて考えます
- 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を定義する必要がないので楽。