基于参数类型的WCF路由

时间:2018-02-26 19:05:13

标签: c# wcf

我正在努力将旧的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很新。

1 个答案:

答案 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文档进一步详细说明。