我对Tim的答案的实现细节
我需要为目前所在公司的客户写一份文档,所以我也考虑在这里发布它。希望它能帮助到某些人。我创建了一个示例客户端和服务端,并用它们来尝试一些想法。我已经清理干净并将其添加到github上。你可以在这里下载它。
要使用 WCF 满足我的需求,需要实现以下功能:
- 不要让 WCF 期望 SOAP 消息
- 能够按照要求格式化请求和响应消息
- 所有消息都应该被考虑处理
- 传入的消息应该被路由到正确的操作以进行处理
1.配置 WCF 不期望 SOAP 消息
第一步是通过 TextMessageEncoder 获取传入的消息。这可以通过使用具有 textMessageEncoding 元素上 MessageVersion.None 设置的自定义绑定来实现。
<customBinding>
<binding name="poxMessageBinding">
<textMessageEncoding messageVersion="None" />
<httpTransport />
</binding>
</customBinding>
2. 格式化消息
为了让现有的XML格式化程序能够正确反序列化输入消息,需要使用消息格式化程序,并在消息契约上添加额外的属性。这通常不是问题,但运行客户端的ebXML模式通过XSD.exe生成了一个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对数据进行序列化和反序列化。对我来说,很有趣的一部分是使用自定义的BodyWriter将对象序列化到Message对象中。以下是服务序列化器的实现的一部分。客户端实现相反。
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();
}
4. 选择正确的操作
为了实现这一点,创建了一个实现IDispatchOperationSelector接口的类。在SelectOperation方法中,我检查传入的消息。这里我需要查找两件事情:
1. 根元素的命名空间与服务契约的命名空间相同的合理性检查
2. 根元素的名称(请注意使用LocalName删除任何命名空间前缀)
根元素的名称是返回值,它将映射到具有匹配名称或操作属性具有匹配值的契约上的任何操作。
public string SelectOperation(ref Message message)
{
var messageBuffer = message.CreateBufferedCopy(16384);
using (var copyMessage = messageBuffer.CreateMessage())
using (var reader = copyMessage.GetReaderAtBodyContents())
{
reader.MoveToContent();
if (reader.NamespaceURI != _namespace)
throw new InvalidOperationException(
"The namespace of the incoming message does not match the namespace of the endpoint contract.");
var action = reader.LocalName;
message = messageBuffer.CreateMessage();
return action;
}
}
总结
为了更容易部署,我创建了一个端点行为来处理消息格式化器、协定过滤器和操作选择器的配置。我也可以创建一个绑定来包装自定义绑定配置,但我认为那一部分不太难记住。
对我来说,有趣的发现之一是,端点行为可以设置所有操作的消息格式化器,以使用自定义消息格式化器。这样可以避免单独配置它们的需要。我从其中一个Microsoft示例中学到了这个技巧。
有用的文档链接
到目前为止,我发现最好的参考资料是Service Station MSDN杂志文章(Google搜索“site:msdn.microsoft.com service station WCF”)。
深入WCF绑定 - 非常有用的关于配置绑定的信息
使用自定义行为扩展WCF - 是我迄今找到的关于调度程序集成点的最佳信息来源,它们包含一些真正有用的图表,说明所有的整合点以及它们在处理顺序中出现的位置。
Microsoft WCF示例 - 这里有很多其他地方没有很好记录的内容。我发现阅读其中一些源代码非常有教益。