WCF intercepting a message with MessageInspector

53 Views Asked by At

I am writing a WCF service (.NET Framework) that is supposed to act as a provider for a client that will be sending messages to it (one-way communication only). The services must communicate using the SOAP protocol. One of the requirements for the service provider is to create custom HTTP responses whose body will contain SOAP Faults, as described in the standard by w3c.

I thought I would try to use the MessageInspector class for this purpose, but I can't achieve the intended effect. I tried to implement the necessary methods and configuration based on the documentation (https://learn.microsoft.com/en-us/dotnet/framework/wcf/samples/message-inspectors?redirectedfrom=MSDN), but so far to no avail.

Currently, the service is hosted by a .NET Framework Console App. Below is the implementation of the most important elements and the project structure:


Structure:

MyService
DTO
-- CompositeType.cs
Utils
-- SchemaValidationBehavior.cs
-- SchemaValidationBehaviorExtensionElement.cs
-- SchemaValidationMessageInspector.cs
-- ValidationFault.cs
App.config
IMyService
MyService

Implementation:

    [ServiceContract]
    public interface IMyService
    {
        [OperationContract(IsOneWay = true)]
        void SendData(CompositeType composite);

    }
    public class MyService : IMyService
    {
        public void SendData(CompositeType composite)
        {
            System.Diagnostics.Debugger.Break();
            Console.WriteLine(composite.BoolValue.ToString() + " " + composite.StringValue);
        }
    }
    class SchemaValidationMessageInspector : IClientMessageInspector, IDispatchMessageInspector
    {
        XmlSchemaSet schemaSet;
        bool validateRequest;
        bool validateReply;
        bool isClientSide;
        [ThreadStatic] // Wskazuje, że wartość pola statycznego jest unikalna dla każdego wątku
        bool isRequest;

        public SchemaValidationMessageInspector()
        {
        }

        public SchemaValidationMessageInspector(XmlSchemaSet schemaSet, bool validateRequest, bool validateReply, bool isClientSide)
        {
            this.schemaSet = schemaSet;
            this.validateRequest = validateRequest;
            this.validateReply = validateReply;
            this.isClientSide = isClientSide;
        }

        // IClientMessageInspector Interfaces -- Client message inspector
        public void AfterReceiveReply(ref Message reply, object correlationState)
        {
            Console.WriteLine("I am inside AfterReceiveReply");
        }

        public object BeforeSendRequest(ref Message request, IClientChannel channel)
        {
            Console.WriteLine("I am inside BeforeSendRequest");
            return null;
        }

        // IDispatchMessageInspector Interfaces -- Service message inspector
        public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
        {
            Console.WriteLine("I am inside AfterReceiveRequest");
            return null;
        }

        public void BeforeSendReply(ref Message reply, object correlationState)
        {
            Console.WriteLine("I am inside BeforeSendReply");
        }
    }
    class SchemaValidationBehavior : IEndpointBehavior
    {
        XmlSchemaSet schemaSet;
        bool validateRequest;
        bool validateReply;

        public SchemaValidationBehavior(){}

        public SchemaValidationBehavior(XmlSchemaSet schemaSet, bool inspectRequest, bool inspectReply)
        {
            this.schemaSet = schemaSet;
            this.validateReply = inspectReply;
            this.validateRequest = inspectRequest;
        }

        #region IEndpointBehavior Members

        public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters){}

        public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
        {
            Console.WriteLine("I am inside ApplyClientBehavior");
        }

        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
        {
            Console.WriteLine("I am inside ApplyDispatchBehavior");
            SchemaValidationMessageInspector inspector = new SchemaValidationMessageInspector();
            endpointDispatcher.DispatchRuntime.MessageInspectors.Add(inspector);
        }

        public void Validate(ServiceEndpoint endpoint){}

        #endregion
    }
    public class SchemaValidationBehaviorExtensionElement : BehaviorExtensionElement
    {
        public SchemaValidationBehaviorExtensionElement(){}

        public override Type BehaviorType
        {
            get
            {
                return typeof(SchemaValidationBehavior);
            }
        }

        protected override object CreateBehavior()
        {
            Console.WriteLine("I am inside ApplyClientBehavior");
            return new SchemaValidationBehavior();
        }

        [ConfigurationProperty("validateRequest", DefaultValue = false, IsRequired = false)]
        public bool ValidateRequest
        {
            get { return (bool)base["validateRequest"]; }
            set { base["validateRequest"] = value; }
        }

        [ConfigurationProperty("validateReply", DefaultValue = false, IsRequired = false)]
        public bool ValidateReply
        {
            get { return (bool)base["validateReply"]; }
            set { base["validateReply"] = value; }
        }

    }

Config:

<configuration>
    <appSettings>
        <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
    </appSettings>
    <system.web>
        <compilation debug="true" />
    </system.web>
    
    <system.serviceModel>

<!-- SERVICES -->
        <services>
            <service name="MyService.MyService" behaviorConfiguration="MyServiceBehavior">
                <endpoint address="" binding="basicHttpBinding" contract="MyService.IMyService" behaviorConfiguration="MyEndpointBehavior">
                    <identity>
                        <dns value="localhost" />
                    </identity>
                </endpoint>
                <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
                
                <host>
                    <baseAddresses>
                        <add baseAddress="http://localhost:9000/MyService/" />
                    </baseAddresses>
                </host>
            </service>
        </services>
<!-- SERVICES -->
        
<!-- BEHAVIORS -->      
        <behaviors>
            <endpointBehaviors>
                <behavior name="MyEndpointBehavior">
                    <schemaValidator validateRequest="true" validateReply="true"/>
                </behavior>
            </endpointBehaviors>

            <serviceBehaviors>
                <behavior name="MyServiceBehavior">
                    <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
                    <serviceDebug includeExceptionDetailInFaults="true" />
                    <!-- Add the behavior extension here -->
                </behavior>
            </serviceBehaviors>
        </behaviors>
<!-- BEHAVIORS -->

<!-- EXTENSIONS -->
        <extensions>
            <behaviorExtensions>
                <add name="schemaValidator" type="MyService.Utils.SchemaValidationBehaviorExtensionElement, MyService" />
            </behaviorExtensions>
        </extensions>
<!-- EXTENSIONS -->
        
    </system.serviceModel>
</configuration>

I must be missing some config, or maybe something is configured wrongly, but the message sent from client goes straight to the SendData() method of MyService class.


P.S. I guess that it doesn't matter but here is the code for Host App:

    internal class Program
    {
        static void Main(string[] args)
        {
            ServiceHost host = null;

            try
            {
                host = new ServiceHost(typeof(MyService.MyService));
                host.Open();
                Console.WriteLine($"Host for MyService started @ {DateTime.Now} {Environment.NewLine}Service hosted on http://localhost:9000");

                Console.ReadLine();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
                Console.ReadLine();
            }
            finally
            {
                host?.Close();
            }
        }
    }
1

There are 1 best solutions below

0
On

I noticed that when I Set the Service itself as Startup project, and don't Host it using the Console App - The service is being (I guess) self-hosted and a 'WCF Test Client' window appear. When I tried to invoke a method within this window the message inspector methods were being called properly - before the message reached the destination method.

I tried to add all the necessary configuration inside the Host's App.config file - It worked as a charm - all the Message Inspector methods are being called before the message reaches the destination method!