如何使用MessageInspector在WCF上实现Orchestration

时间:2011-06-14 14:43:29

标签: wcf idispatchmessageinspector

我有一个WCF服务,我使用MessageInspector检查它(继承IDispatchMessageInspector)

我想在运行服务之前做一些事情,并且结果是“我不想运行服务。”

我想阻止客户端调用,但客户端不会收到异常。

你能帮助我吗

1 个答案:

答案 0 :(得分:0)

此方案类似于MSDN WCF论坛中名为“IDispatchMessageInspector.AfterReceiveRequest - skip operation and manually generate custom response instead”的帖子。如果这是您需要的(当您在检查器中收到消息时,您决定要跳过服务操作,但是向客户端返回消息而客户端不应该看到异常),那么这个答案应该适用于你也是。请注意,您需要以与客户期望的格式相同的格式创建响应消息,否则您将遇到异常。

此代码使用三个(多个)WCF扩展点来实现该场景,一个消息检查器(正如您所提到的那样),消息格式化程序和操作调用程序。我在http://blogs.msdn.com/b/carlosfigueira/archive/2011/03/14/wcf-extensibility.aspx中正在进行的有关WCF可扩展性的系列文章中发表过关于它们的博客。

public class Post_55ef7692_25dc_4ece_9dde_9981c417c94a
{
    [ServiceContract(Name = "ITest", Namespace = "http://tempuri.org/")]
    public interface ITest
    {
        [OperationContract]
        string Echo(string text);
    }
    public class Service : ITest
    {
        public string Echo(string text)
        {
            return text;
        }
    }
    static Binding GetBinding()
    {
        BasicHttpBinding result = new BasicHttpBinding();
        return result;
    }
    public class MyOperationBypasser : IEndpointBehavior, IOperationBehavior
    {
        internal const string SkipServerMessageProperty = "SkipServer";
        public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
        {
        }

        public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
        {
        }

        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
        {
            endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new MyInspector(endpoint));
        }

        public void Validate(ServiceEndpoint endpoint)
        {
        }

        public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
        {
        }

        public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
        {
        }

        public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
        {
            dispatchOperation.Formatter = new MyFormatter(dispatchOperation.Formatter);
            dispatchOperation.Invoker = new MyInvoker(dispatchOperation.Invoker);
        }

        public void Validate(OperationDescription operationDescription)
        {
        }

        class MyInspector : IDispatchMessageInspector
        {
            ServiceEndpoint endpoint;
            public MyInspector(ServiceEndpoint endpoint)
            {
                this.endpoint = endpoint;
            }

            public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
            {
                Message result = null;
                HttpRequestMessageProperty reqProp = null;
                if (request.Properties.ContainsKey(HttpRequestMessageProperty.Name))
                {
                    reqProp = request.Properties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty;
                }

                if (reqProp != null)
                {
                    string bypassServer = reqProp.Headers["X-BypassServer"];
                    if (!string.IsNullOrEmpty(bypassServer))
                    {
                        result = Message.CreateMessage(request.Version, this.FindReplyAction(request.Headers.Action), new OverrideBodyWriter(bypassServer));
                    }
                }

                return result;
            }

            public void BeforeSendReply(ref Message reply, object correlationState)
            {
                Message newResult = correlationState as Message;
                if (newResult != null)
                {
                    reply = newResult;
                }
            }

            private string FindReplyAction(string requestAction)
            {
                foreach (var operation in this.endpoint.Contract.Operations)
                {
                    if (operation.Messages[0].Action == requestAction)
                    {
                        return operation.Messages[1].Action;
                    }
                }

                return null;
            }

            class OverrideBodyWriter : BodyWriter
            {
                string bypassServerHeader;
                public OverrideBodyWriter(string bypassServerHeader)
                    : base(true)
                {
                    this.bypassServerHeader = bypassServerHeader;
                }

                protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
                {
                    writer.WriteStartElement("EchoResponse", "http://tempuri.org/");
                    writer.WriteStartElement("EchoResult");
                    writer.WriteString(this.bypassServerHeader);
                    writer.WriteEndElement();
                    writer.WriteEndElement();
                }
            }
        }

        class MyFormatter : IDispatchMessageFormatter
        {
            IDispatchMessageFormatter originalFormatter;
            public MyFormatter(IDispatchMessageFormatter originalFormatter)
            {
                this.originalFormatter = originalFormatter;
            }

            public void DeserializeRequest(Message message, object[] parameters)
            {
                if (message.Properties.ContainsKey(MyOperationBypasser.SkipServerMessageProperty))
                {
                    Message returnMessage = message.Properties[MyOperationBypasser.SkipServerMessageProperty] as Message;
                    OperationContext.Current.IncomingMessageProperties.Add(MyOperationBypasser.SkipServerMessageProperty, returnMessage);
                    OperationContext.Current.OutgoingMessageProperties.Add(MyOperationBypasser.SkipServerMessageProperty, returnMessage);
                }
                else
                {
                    this.originalFormatter.DeserializeRequest(message, parameters);
                }
            }

            public Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result)
            {
                if (OperationContext.Current.OutgoingMessageProperties.ContainsKey(MyOperationBypasser.SkipServerMessageProperty))
                {
                    return null;
                }
                else
                {
                    return this.originalFormatter.SerializeReply(messageVersion, parameters, result);
                }
            }
        }

        class MyInvoker : IOperationInvoker
        {
            IOperationInvoker originalInvoker;

            public MyInvoker(IOperationInvoker originalInvoker)
            {
                if (!originalInvoker.IsSynchronous)
                {
                    throw new NotSupportedException("This implementation only supports synchronous invokers");
                }

                this.originalInvoker = originalInvoker;
            }

            public object[] AllocateInputs()
            {
                return this.originalInvoker.AllocateInputs();
            }

            public object Invoke(object instance, object[] inputs, out object[] outputs)
            {
                if (OperationContext.Current.IncomingMessageProperties.ContainsKey(MyOperationBypasser.SkipServerMessageProperty))
                {
                    outputs = null;
                    return null; // message is stored in the context
                }
                else
                {
                    return this.originalInvoker.Invoke(instance, inputs, out outputs);
                }
            }

            public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
            {
                throw new NotSupportedException();
            }

            public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
            {
                throw new NotSupportedException();
            }

            public bool IsSynchronous
            {
                get { return true; }
            }
        }
    }
    public static void Test()
    {
        string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
        ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
        ServiceEndpoint endpoint = host.AddServiceEndpoint(typeof(ITest), GetBinding(), "");
        endpoint.Behaviors.Add(new MyOperationBypasser());
        foreach (var operation in endpoint.Contract.Operations)
        {
            operation.Behaviors.Add(new MyOperationBypasser());
        }

        host.Open();
        Console.WriteLine("Host opened");

        ChannelFactory<ITest> factory = new ChannelFactory<ITest>(GetBinding(), new EndpointAddress(baseAddress));
        ITest proxy = factory.CreateChannel();
        Console.WriteLine(proxy.Echo("Hello"));

        Console.WriteLine("And now with the bypass header");
        using (new OperationContextScope((IContextChannel)proxy))
        {
            HttpRequestMessageProperty httpRequestProp = new HttpRequestMessageProperty();
            httpRequestProp.Headers.Add("X-BypassServer", "This message will not reach the service operation");
            OperationContext.Current.OutgoingMessageProperties.Add(
                HttpRequestMessageProperty.Name,
                httpRequestProp);
            Console.WriteLine(proxy.Echo("Hello"));
        }

        ((IClientChannel)proxy).Close();
        factory.Close();

        Console.Write("Press ENTER to close the host");
        Console.ReadLine();
        host.Close();
    }
}