WCFを別の(SOAP以外の)形式で処理するにはどうすればよいですか?
-
09-10-2019 - |
質問
私はWCFと協力して、サードパーティの会社とメッセージを交換しています。メッセージを送信して受信する必要があります EBXML仕様. 。理想的には、できるだけ多くのWCFスタックを使用して、 それらをすべて処理する1つの方法 この場合のようにアプローチは、WCFのインフラストラクチャの多くを再び書くことを意味します。
私の最初の調査からわかる限り、これは私自身のカスタムバインディングを書く必要がありますが、MSDNのドキュメントで明確さを見つけるのに苦労しています。
私はこれらのそれぞれの個々の実装で多くの詳細なドキュメントを見つけることができましたが、それをすべてまとめる方法についてはほとんどありません。私が持っている本は、PeirisとMulderの「Pro WCF」でこれについて言及していないことも、これらのトピックについても同様に軽いようです。
私が目指しているのは、次のようなものです。
送信および受信されるメッセージは、最初の要素の名前が実行される操作の名前であり、子要素がリクエストメッセージのペイロードである場合、以下のようにフォーマットする必要があります。
<?xml version="1.0" encoding="UTF-8"?>
<op:DoSomething xmlns:op="http://my.ebXML.schema.com" xmlns:payload="http://payload.company.com">
<op:AnObject>
<payload:ImportantValue>42</payload:ImportantValue>
</op:AnObject>
</op:DoSomething>
そして、応答は次のとおりです。
<?xml version="1.0" encoding="UTF-8"?>
<op:AcknowledgementResponse xmlns:op="http://my.ebXML.schema.com" xmlns:payload="http://payload.company.com">
<op:ResponseObject>
<payload:Ok>True</payload:Ok>
</op:ResponseObject>
</op:AcknowledgementResponse>
メッセージはすべてXMLスキーマで説明されているため、XSD.EXEを使用して、これらを強く型付けされたオブジェクトに変換しました。見る https://gist.github.com/740303 スキーマのために。これらはスキーマの例であることに注意してください。クライアントの機密性契約に違反することなく、実際のスキーマを投稿することはできません(また、それらが巨大であるため、私も望んでいません)。
次のように、サービスの実装を書くことができます。
public class MyEndpoint : IMyEndpoint
{
public AcknowledgementResponse DoSomething(AnObject value)
{
return new AcknowledgementResponse
{
Ok = True;
};
}
}
どんな助けも大歓迎です。
解決
バインディングで何もする必要はないと思います。とにかく、EBXMLフォーマットされたメッセージをHTTPに送信する必要があると思いますか?
@ladislavの答えは1つのアプローチですが、メッセージエンコーダーは、達成しようとしているものよりもはるかに低いレベルで動作するように設計されていると思います。それらは本質的に、基礎となるストリームとの間のメッセージをエンコードするピースです(つまり、メッセージがストリーム上のバイトとして表される方法)。
私はあなたがする必要があることは実装することだと思います カスタムメッセージフォーマッタ. 。特に、あなたはメッセージをサードパーティに送信したいと言っているので、私はそれだけだと思います IClientMessageFormatter
実装する必要があるインターフェイス。他のインターフェイス(IDispatchMessageFormatter
)サーバー側で使用されます。
また、適切なServiceBehaviorとOperationBehaviorを実装してフォーマッタをスタックにインストールする必要がありますが、このコードは最小限になります(コードの大部分は上記のインターフェイスを実装することになります)。
実装されたら、「1つの方法をすべて処理するための1つの方法」アプローチを使用して、フォーマッタをテストおよびデバッグすることができます。受信したメッセージを取り出してコンソールにダンプして、レビューしてからEBXML応答を送信します。同じアプローチを使用して、ユニットテストを構築することもできます。
他のヒント
ティムの答えの実装の詳細
私は現在働いているクライアントのためにこれを書く必要がありましたので、私もここに投稿するかもしれないと思いました。それが誰かを助けることを願っています。これらのアイデアのいくつかを試してみるために使用したサンプルクライアントとサービスを作成しました。私はそれをきれいにして、それをgithubに追加しました。あなたはできる ここからダウンロードしてください.
WCFを必要な方法で使用できるようにするには、次のことを実装する必要があります。
- SOAPメッセージを期待しないWCF
- 必要に応じてリクエストと応答メッセージをフォーマットする機能
- 処理のために考慮されるすべてのメッセージ
- それらを処理するために正しい操作にルーティングされる受信メッセージ
1.石鹸メッセージを期待しないようにWCFを構成します
最初のステップは、TextMessageEncoderを介して着信メッセージを取得することでした。これは、messageversion.None設定でカスタムバインディングを使用して達成されました。
<customBinding>
<binding name="poxMessageBinding">
<textMessageEncoding messageVersion="None" />
<httpTransport />
</binding>
</customBinding>
2.メッセージを正しくフォーマットします
メッセージ契約に追加の属性を追加せずに、既存のXMLフォーマッタによって着信メッセージが廃止されることを拒否されたため、メッセージフォーマッタが必要です。これは通常問題ではありませんが、XSD.EXEを介してクライアントEBXMLスキーマを実行すると、33000行CSファイルが生成され、これを変更する必要はありませんでした。それに加えて、人々は将来属性を再生するのを忘れてしまうので、これによりメンテナンスも容易になることを願っています。
カスタムフォーマッタは、着信メッセージを最初のパラメーターのタイプに変換し、返品タイプを応答メッセージに変換することを期待しています。実装方法を検査して、コンストラクター内の最初のパラメーターと戻り値のタイプを決定します。
public SimpleXmlFormatter(OperationDescription operationDescription)
{
// Get the request message type
var parameters = operationDescription.SyncMethod.GetParameters();
if (parameters.Length != 1)
throw new InvalidDataContractException(
"The SimpleXmlFormatter will only work with a single parameter for an operation which is the type of the incoming message contract.");
_requestMessageType = parameters[0].ParameterType;
// Get the response message type
_responseMessageType = operationDescription.SyncMethod.ReturnType;
}
次に、XmlSerializerを使用してデータをシリアル化して脱isizeするだけです。私にとってこの興味深い部分は、オブジェクトをメッセージオブジェクトにシリアル化するためのカスタムボディライターを使用することでした。以下は、Service Serializerの実装の一部です。クライアントの実装は逆です。
public void DeserializeRequest(Message message, object[] parameters)
{
var serializer = new XmlSerializer(_requestMessageType);
parameters[0] = serializer.Deserialize(message.GetReaderAtBodyContents());
}
public Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result)
{
return Message.CreateMessage(MessageVersion.None, _responseMessageType.Name,
new SerializingBodyWriter(_responseMessageType, result));
}
private class SerializingBodyWriter : BodyWriter
{
private readonly Type _typeToSerialize;
private readonly object _objectToEncode;
public SerializingBodyWriter(Type typeToSerialize, object objectToEncode) : base(false)
{
_typeToSerialize = typeToSerialize;
_objectToEncode = objectToEncode;
}
protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
{
writer.WriteStartDocument();
var serializer = new XmlSerializer(_typeToSerialize);
serializer.Serialize(writer, _objectToEncode);
writer.WriteEndDocument();
}
}
3.すべての着信メッセージを処理します
すべての着信メッセージを処理するようにWCFに指示するために、EndpointDispatcherのContractFilterプロパティをMatchAllMessageFilterのインスタンスに設定する必要がありました。これは、私のエンドポイントの動作構成からこれを示すスニペットです。
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
endpointDispatcher.ContractFilter = new MatchAllMessageFilter();
// Do more config ...
}
4.正しい操作の選択
これは、IdisPatchOperationSelectorを実装するクラスを作成することで達成されました。 Selectoperationメソッドでは、着信メッセージを検査します。ここでは、2つのことを探しています。1。ルート要素の名前空間がサービス契約の名前空間と同じであることを確認します。2。ルート要素の名前(LocalNameの使用には、名前空間プレフィックスを削除することに注意してください。 ))
ルート要素の名前は返品値であり、一致する名前またはアクション属性に一致する値を持つ契約上の任意の操作にマッピングされます。
public string SelectOperation(ref Message message)
{
var messageBuffer = message.CreateBufferedCopy(16384);
// Determine the name of the root node of the message
using (var copyMessage = messageBuffer.CreateMessage())
using (var reader = copyMessage.GetReaderAtBodyContents())
{
// Move to the first element
reader.MoveToContent();
if (reader.NamespaceURI != _namespace)
throw new InvalidOperationException(
"The namespace of the incoming message does not match the namespace of the endpoint contract.");
// The root element name is the operation name
var action = reader.LocalName;
// Reset the message for subsequent processing
message = messageBuffer.CreateMessage();
// Return the name of the action to execute
return action;
}
}
それをすべて包みます
展開を容易にするために、メッセージフォーマッタ、契約フィルター、および操作セレクターの構成を処理するエンドポイント動作を作成しました。また、カスタムバインディング構成をラップするためにバインディングを作成することもできましたが、その部分を覚えにくいとは思いませんでした。
私にとって興味深い発見の1つは、エンドポイントの動作により、エンドポイントのすべての操作のメッセージフォーマッタを設定して、カスタムメッセージフォーマッタを使用できることです。これにより、これらを個別に構成する必要性が節約されます。私はこれを1つから拾い上げました Microsoftサンプル.
役立つドキュメントリンク
私がこれまでに見つけた最高の参考文献は、サービスステーションMSDNマガジン記事(Google "サイト:msdn.microsoft.comサービスステーションWCF")です。
深さのWCFバインディング - バインディングの構成に関する非常に有用な情報
カスタム動作でWCFを拡張します - 私がまだ見つけたディスパッチャーの統合ポイントに関する最良の情報源は、すべての統合ポイントとそれらが処理の順に発生する場所を示すいくつかの本当に便利な図を含んでいます。
Microsoft WCFサンプル - ここには他の場所であまり文書化されていないことがたくさんあります。これらの非常に有益ないくつかのために、ソースコードを読んでいることがわかりました。
カスタムメッセージ形式には、カスタムが必要です MessageEncoder. MSDN カスタムエンコーダーを作成する方法の例を含む。使用する場合 リフレクター いくつかのエンコーダーの実装が見つかるので、それを書く方法を学ぶことができます。
また、messageversion.noneを使用してtextmessageencoderを使用しようとすると、何が起こるかを確認することもできます(試したことはありません)。