wcfwcf-routing

WCF Routing service error - ContractFilter mismatch at the EndpointDispatcher


The situation is as follows: I have an internal server running some WCF services, and I want them to be accessible from the internet at large. To this end, I have written a routing service that is running on our public facing web-server.

This routing service appears to work, however when I attempt to invoke a method, I always get the following error.

The message with Action 'http://tempuri.org/IProcessManagementService/ListProcesses' cannot be processed at the receiver, due to a ContractFilter mismatch at the EndpointDispatcher. This may be because of either a contract mismatch (mismatched Actions between sender and receiver) or a binding/security mismatch between the sender and the receiver. Check that sender and receiver have the same contract and the same binding (including security requirements, e.g. Message, Transport, None).

I have attempted to remove all security requirements from the services, and used both wsHTTP and basicHTTP endpoints. Nothing appears to do the trick. The routing service is properly passing on the mex services, however, so svcutil is able to build client classes.

I am configuring the router though code. The routing service is given a list of service names to provide routing for, and the router and server addresses.

Here is the config for the routing service:

<MES.RoutingService.Properties.Settings>
   <setting name="RouterAddress" serializeAs="String">
    <value>http://localhost:8781/</value>
   </setting>
   <setting name="ServerAddress" serializeAs="String">
    <value>http://10.4.1.117:8781/</value>
   </setting>
   <setting name="Services" serializeAs="Xml">
    <value>
     <ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <string>ProcessManagementService</string>
      <string>TestProcessService</string>
      <string>ProcessDataEntryService</string>
      <string>ProcessReportingService</string>
     </ArrayOfString>
    </value>
   </setting>
  </MES.RoutingService.Properties.Settings>

It calls a function with the following code, providing the router address, server address, and service names from the config file.

var routers = new List<ServiceHost>();
        foreach (var service in _services)
        {
            var routerType = typeof(IRequestReplyRouter);
            var routerContract = ContractDescription.GetContract(routerType);
            var serviceHost = new ServiceHost(typeof (System.ServiceModel.Routing.RoutingService));             
            var serverEndpoints = new List<ServiceEndpoint>();              

            //Configure Mex endpoints
            serviceHost.AddServiceEndpoint(routerType, MetadataExchangeBindings.CreateMexHttpBinding(), _routerAddress + service + "/mex");
            serverEndpoints.Add(new ServiceEndpoint(routerContract, MetadataExchangeBindings.CreateMexHttpBinding(), new EndpointAddress(_serverAddress + service + "/mex")));

            //RAR SECURITY SMASH.
            var binding = new WSHttpBinding(SecurityMode.None);
            binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
            binding.Security.Message.EstablishSecurityContext = false;
            binding.Security.Message.NegotiateServiceCredential = false;
            binding.Security.Message.ClientCredentialType = MessageCredentialType.None;

            //Configure WsHttp endpoints
            serviceHost.AddServiceEndpoint(routerType, binding, _routerAddress + service);              
            serverEndpoints.Add(new ServiceEndpoint(routerContract, binding, new EndpointAddress(_serverAddress + service)));

            var basicBinding = new BasicHttpBinding();
            serviceHost.AddServiceEndpoint(routerType, basicBinding, _routerAddress + service + "/basic");
            serverEndpoints.Add(new ServiceEndpoint(routerContract, basicBinding, new EndpointAddress(_serverAddress + service + "/basic")));

            //Set Routing Tables
            var configuration = new RoutingConfiguration();
            configuration.FilterTable.Add(new MatchAllMessageFilter(), serverEndpoints);
            serviceHost.Description.Behaviors.Add(new RoutingBehavior(configuration));

            routers.Add(serviceHost);
        }
        return routers;

The service calls this function on start, and then opens each of the service hosts returned in the routers list.

The server itself is configured via the following app.config

<system.serviceModel>
  <bindings>
   <wsHttpBinding>
    <binding name="noSecurityBinding">
     <security mode="None">
      <transport clientCredentialType="None" />
      <message establishSecurityContext="false" />
     </security>
    </binding>
   </wsHttpBinding>   
  </bindings>
  <services>
   <service name="MES.ProcessManagerServiceLibrary.ProcessManagementService">
    <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
    <endpoint binding="wsHttpBinding" bindingConfiguration="noSecurityBinding"
     contract="MES.ProcessManagerServiceLibrary.IProcessManagementService" />
    <endpoint address="basic" binding="basicHttpBinding" bindingConfiguration=""
     contract="MES.ProcessManagerServiceLibrary.IProcessManagementService" />
    <host>
     <baseAddresses>
      <add baseAddress="http://localhost:8781/ProcessManagementService/" />
     </baseAddresses>
    </host>
   </service>
   <service name="MES.ProcessManagerServiceLibrary.TestProcessService">
    <endpoint address="" binding="wsHttpBinding" bindingConfiguration="noSecurityBinding"
     contract="MES.ProcessManagerServiceLibrary.ITestProcessService" />
    <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
    <endpoint address="basic" binding="basicHttpBinding" bindingConfiguration=""
     contract="MES.ProcessManagerServiceLibrary.ITestProcessService" />    
    <host>
     <baseAddresses>
      <add baseAddress="http://localhost:8781/TestProcessService/" />
     </baseAddresses>
    </host>
   </service>
   <service name="MES.ProcessManagerServiceLibrary.ProcessDataEntryService">
    <endpoint address="" binding="wsHttpBinding" bindingConfiguration="noSecurityBinding"
     contract="MES.ProcessManagerServiceLibrary.IProcessDataEntryService" />
    <endpoint address="basic" binding="basicHttpBinding" bindingConfiguration=""
    contract="MES.ProcessManagerServiceLibrary.IProcessDataEntryService" />
    <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
    <host>
     <baseAddresses>
      <add baseAddress="http://localhost:8781/ProcessDataEntryService/" />
     </baseAddresses>
    </host>
   </service>
   <service name="MES.ProcessManagerServiceLibrary.ProcessReportingService">
    <endpoint address="" binding="wsHttpBinding" bindingConfiguration="noSecurityBinding"
     contract="MES.ProcessManagerServiceLibrary.IProcessReportingService" />
    <endpoint address="basic" binding="basicHttpBinding" bindingConfiguration=""
    contract="MES.ProcessManagerServiceLibrary.IProcessReportingService" />
    <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
    <host>
     <baseAddresses>
      <add baseAddress="http://localhost:8781/ProcessReportingService/" />
     </baseAddresses>
    </host>
   </service>
  </services>
  <behaviors>
   <serviceBehaviors>
    <behavior>
     <!-- To avoid disclosing metadata information, 
     set the value below to false and remove the metadata endpoint above before deployment -->
     <serviceMetadata httpGetEnabled="True"/>
     <!-- To receive exception details in faults for debugging purposes, 
     set the value below to true. Set to false before deployment 
     to avoid disclosing exception information -->
     <serviceDebug includeExceptionDetailInFaults="True"/>
    </behavior>
   </serviceBehaviors>
  </behaviors>
 </system.serviceModel>

What am I missing?

Edit: I think I found the problem- the routing service was returning this configuration for the services-

<client>
   <endpoint address="http://shco-appsrv1.us.shepherd.ad:8781/ProcessManagementService/"
    binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IProcessManagementService"
    contract="IProcessManagementService" name="WSHttpBinding_IProcessManagementService" />
   <endpoint address="http://shco-appsrv1.us.shepherd.ad:8781/ProcessManagementService/basic"
    binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IProcessManagementService"
    contract="IProcessManagementService" name="BasicHttpBinding_IProcessManagementService" />
 </client>

This points to the internal server, not the external server. No idea if this is standard behavior for a Routing service, or if it is overridable behavior.


Solution

  • Seems like you haven't wired-in the serviceModel client element in your config properly. The RoutingService needs to be configure like a standard WCF service and also expose "intercept" endpoints to the routed services. It then uses the client element endpoints to redirect the service calls.

    Below is a simple config that doesn't rely on code. It contains naming conventions for the various routing values that keep everything straight. You can replace the "YourRoutedService" string in the config with your actual service name but the suffixes should remain to keep everything wired up correctly.

    I would start with getting a file-based config successfully making end-to-end calls (no recompiles needed when tweaking with this approach). Next, base your code on the file config and remove the elements being code configured.

    <system.serviceModel>
        <services
            name="System.ServiceModel.Routing.RoutingService"
            behaviorConfiguration="RoutingBehavior" >
            <endpoint
                name="RouterEndpoint"
                address=""
                binding="wsHttpBinding"
                bindingConfiguration="Http"
                contract="System.ServiceModel.Routing.IRequestReplyRouter" />
    
            <!-- List all endpoints to be routed via EndpointName routing filter -->
            <endpoint
                name="YourRoutedServiceName"
                address="YourRoutedService"
                contract="System.ServiceModel.Routing.IRequestReplyRouter"
                binding="wsHttpBinding"
                bindingConfiguration="Http" />
        </services>
        <routing>
            <filters>
                <!-- Active filters -->
                <filter
                    name="YourRoutedServiceFilter"
                    filterType="EndpointName"
                    filterData="YourRoutedServiceName" />
            </filters>
            <filterTables>
                <filterTable name="WebLayer">
                    <!-- Map to client Endpoints-->
                    <add
                        filterName="YourRoutedServiceFilter"
                        endpointName="YourRoutedServiceNameEndpoint"
                        priority="0" />
                </filterTable>
            </filterTables>
        </routing>
        <behavior name="RoutingBehavior">
            <routing routeOnHeadersOnly="false" filterTableName="WebLayer" />
            <serviceDebug includeExceptionDetailInFaults="true" />
            <serviceMetadata httpsGetEnabled="true"  />
        </behavior>
        <bindings>
            <wsHttpBinding>
                <binding name="Http">
                    <security mode="None" />
                </binding>
            </wsHttpBinding>
        </bindings>
        <client>
            <endpoint
                name="YourRoutedServiceNameEndpoint"
                address="http://somehost/YourRoutedService/Service.svc"
                contract="*"
                binding="wsHttpBinding"
                bindingConfiguration="Http" />
        </client>
    </system.serviceModel>