Xml 如何修改WCF以处理不同(非SOAP)格式的消息?
我正在与WCF合作,与第三方公司交换消息。这些信息需要在一个信封中发送和接收,该信封要与邮件地址相匹配。理想情况下,我希望尽可能多地使用WCF堆栈,避免使用这种方法,因为在本例中,这意味着再次编写WCF的大部分基础结构 从我最初的研究中可以看出,这需要我编写自己的自定义绑定,但我很难在MSDN的文档中找到清晰的内容 我已经找到了很多关于每个实现的详细文档,但是很少有关于如何将它们端到端地放在一起的文档。看来,我的书在这些主题上也同样轻描淡写,在佩里斯和穆德的《支持WCF》中没有提到这一点 我的目标如下 正在发送和接收的消息的格式必须如下所示,其中第一个元素的名称是要执行的操作的名称,子元素是请求消息的有效负载,其形式如下:Xml 如何修改WCF以处理不同(非SOAP)格式的消息?,xml,wcf,wcf-binding,ebxml,Xml,Wcf,Wcf Binding,Ebxml,我正在与WCF合作,与第三方公司交换消息。这些信息需要在一个信封中发送和接收,该信封要与邮件地址相匹配。理想情况下,我希望尽可能多地使用WCF堆栈,避免使用这种方法,因为在本例中,这意味着再次编写WCF的大部分基础结构 从我最初的研究中可以看出,这需要我编写自己的自定义绑定,但我很难在MSDN的文档中找到清晰的内容 我已经找到了很多关于每个实现的详细文档,但是很少有关于如何将它们端到端地放在一起的文档。看来,我的书在这些主题上也同样轻描淡写,在佩里斯和穆德的《支持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>
任何帮助都将不胜感激。对于自定义消息格式,您需要自定义。包含如何创建自定义编码器的示例。如果您使用,您将发现几个编码器实现,以便您可以学习如何编写它
您还可以检查如果尝试将TextMessageEncoder与MessageVersion.None一起使用会发生什么情况(我从未尝试过)。我认为您不需要对绑定执行任何操作。我假设您需要通过HTTP发送ebXML格式的消息 @ladislav的答案是一种方法,但我认为消息编码器的设计水平远低于您试图实现的水平。它们本质上是对底层流中的消息进行编码的片段(即消息如何在流中表示为字节) 我认为你需要做的是实施一个。特别是,既然您说要将消息提交给第三方,那么我认为您只需要实现
IClientMessageFormatter
接口。另一个接口(IDispatchMessageFormatter
)用于服务器端
您还需要实现适当的ServiceBehavior和OperationBehavior来将格式化程序安装到堆栈中,但这方面的代码很少(大部分代码将用于实现上述接口)
一旦实现,您就可以使用“一个方法处理它们”的方法来测试和调试格式化程序。只需将收到的消息转储到控制台供您查看,然后再发送一个ebXML响应。您还可以使用相同的方法来构建单元测试 我执行蒂姆回答的详细信息 我需要为我目前工作的客户写这篇文章,所以我想我也可以把它贴在这里。我希望它能帮助别人。我创建了一个示例客户机和服务,用于尝试其中一些想法。我已将其清理并添加到github。你可以 为了使WCF能够以我所要求的方式使用,需要实现以下事项:
<customBinding>
<binding name="poxMessageBinding">
<textMessageEncoding messageVersion="None" />
<httpTransport />
</binding>
</customBinding>
然后,它简单地使用XmlSerializer对数据进行序列化和反序列化。对我来说,其中一个有趣的部分是使用自定义BodyWriter将对象序列化为消息对象。下面是服务序列化程序实现的一部分。客户端实现则相反
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方法中,我检查传入的消息。在这里,我要寻找两件事:
1.检查根元素的命名空间是否与服务协定的命名空间相同
2.根元素的名称(注意使用LocalName删除任何名称空间前缀)
根元素的名称是返回值,它将映射到具有匹配名称或action属性具有匹配值的协定上的任何操作
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;
}
}
把一切都结束了
为了便于部署,我创建了一个端点行为来处理消息格式化程序、契约过滤器和操作选择器的配置。我还可以创建一个绑定来封装自定义绑定配置,但我不认为这一部分太难记住
对我来说,一个有趣的发现是端点行为可以将端点中所有操作的消息格式化程序设置为使用自定义消息格式化程序。这样就不需要单独配置这些。这是我从一家商店买的
有用的文档链接
到目前为止,我找到的最好的参考资料是《服务站MSDN》杂志文章(谷歌“网站:MSDN.microsoft.com服务站WCF”)
-有关配置bi的非常有用的信息
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();
}
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
endpointDispatcher.ContractFilter = new MatchAllMessageFilter();
// Do more config ...
}
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;
}
}