我正在努力将旧的ASMX Web服务转换为WCF服务(我不确定这些是否是正确的术语,旧代码有使用wsdl工具创建的ASMX文件,我正在尝试使用svcutil工具创建相同的服务)。根据代码和实验,看起来旧代码基于该消息的内容路由消息。
之前的代码(简化)如下:
[SoapDocumentService(SoapBindingUse.Literal, SoapParameterStyle.Wrapped, RoutingStyle = SoapServiceRoutingStyle.RequestElement)]
public class Server : WebService, IServer
{
public Action1Response Action1(Action1Request request)
{
//Do stuff here
}
public Action2Response Action2(Action2Request request)
{
//Do stuff here
}
}
新参考代码:
[ServiceContract]
public class Server : WebService, IServer
{
[OperationContract]
public Action1Response Action1(Action1Request request)
{
//Do stuff here
}
[OperationContract]
public Action2Response Action2(Action2Request request)
{
//Do stuff here
}
}
但是,我无法弄清楚如何在WCF中使用类似的东西。我尝试了SoapDocumentService
但是没有用。我能想到的另一件事就是做一些路由/过滤,但我不知道我是如何做到的。我的猜测是做一些事情:
<routing>
<namespaceTable>
<add namespace="http://example.org" prefix="ns"/>
</namespaceTable>
<filters>
<filter name="action1" filterType="XPath" filterData="boolean(//ns:Action1Request)"/>
<filter name="action1" filterType="XPath" filterData="boolean(//ns:Action2Request)"/>
</filters>
<filterTables>
<filterTable name="routingTable">
<add filterName="action1" endpointName="Action1Service" />
<add filterName="action2" endpointName="Action2Service" />
</filterTable>
</filterTables>
</routing>
<client>
<!-- Pretty sure this wouldn't work -->
<endpoint name="Action1Service" address="/Server.svc/Action1" binding="basicHttpBinding" contract="*" />
<endpoint name="Action1Service" address="/Server.svc/Action2" binding="basicHttpBinding" contract="*" />
</client>
我的问题是,如何根据邮件内容建立WCF服务以路由到不同的操作。我无法控制客户端,它只会向/Server.svc
发送消息。如果我在这里使用不正确的术语,我很抱歉,我对WCF很新。
答案 0 :(得分:0)
经过大量阅读后,我发现该服务不基于Action URI调度消息,而是基于SOAP主体中的XML元素,SOAP 1.1支持该元素。解决方案是创建一个自定义类,以根据正文中的元素调度消息。 This Microsoft Document详细解释了所有内容。我使用的基本代码如下:
<强>分派器强>
public class DispatchByBodyOperationSelector : IDispatchOperationSelector
{
public string SelectOperation(ref Message message)
{
XmlDictionaryReader bodyReader = message.GetReaderAtBodyContents();
//By accessing the body the message is now marked as read, thus we need to clone the message so it's left in an unread state
//NOTE: We have to pass in the body reader because if we try to get it again it throws an error
message = CloneMessage(message, bodyReader);
//The element in the body has "Request" appended so we need to remove it to match the method
return bodyReader.LocalName.Replace("Request", "");
}
private Message CloneMessage(Message message, XmlDictionaryReader body)
{
Message toReturn = Message.CreateMessage(message.Version, message.Headers.Action, body);
toReturn.Headers.CopyHeaderFrom(message, 0);
toReturn.Properties.CopyProperties(message.Properties);
return toReturn;
}
}
自定义属性
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface)]
public class DispatchByBodyBehaviorAttribute : Attribute, IContractBehavior
{
public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters){}
public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime){}
public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint){}
public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
{
//We want the operator selector to be this
dispatchRuntime.OperationSelector = new DispatchByBodyOperationSelector();
}
}
<强>服务强>
[ServiceContract, DispatchByBodyBehavior]
public class Server : IServer
{
[OperationContract]
public Action1Response Action1(Action1Request request)
{
//Do stuff here
}
[OperationContract]
public Action2Response Action2(Action2Request request)
{
//Do stuff here
}
}
显然这是非常基本的版本,需要更多验证和边缘案例处理。链接的Microsoft文档进一步详细说明。