Enabling SOAP Message Inspection for a Single Operation - wcf

How can I force an IMessageInspector to fire only when a specific operation is called, rather than firing whenever a call to the service is made? Currently, I am applying a custom IEndpointBehavior to the endpoint which calls this IMessageInspector, but this is not exactly what I would like to do...

The message inspectors are bound to the dispatch runtime object, which is a single one for each endpoint, not operation. If a parameter inspector works, then you can use it (it's bound to the operation), but if you need a message inspector, then it cannot be bound to a single operation. But you can, inside your inspector, check whether the operation is what you expect (based on the Action header, which is unique per operation), as shown in the code below.
public class StackOverflow_7502134
{
[ServiceContract]
public interface ITest
{
[OperationContract]
string Echo(string text);
[OperationContract]
int Add(int x, int y);
}
public class Service : ITest
{
public string Echo(string text)
{
return text;
}
public int Add(int x, int y)
{
return x + y;
}
}
public class MyInspector : IEndpointBehavior, IDispatchMessageInspector
{
string[] operationNames;
public MyInspector(params string[] operationNames)
{
this.operationNames = operationNames ?? new string[0];
}
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(this);
}
public void Validate(ServiceEndpoint endpoint)
{
}
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
// by default, action == <serviceContractNamespace>/<serviceContractName>/<operationName>
string operationName = request.Headers.Action.Substring(request.Headers.Action.LastIndexOf('/') + 1);
if (this.operationNames.Contains(operationName))
{
Console.WriteLine("Inspecting request to operation {0}", operationName);
Console.WriteLine(request);
Console.WriteLine();
return operationName;
}
else
{
return null;
}
}
public void BeforeSendReply(ref Message reply, object correlationState)
{
string operationName = correlationState as string;
if (operationName != null)
{
Console.WriteLine("Inspecting reply from operation {0}", operationName);
Console.WriteLine(reply);
Console.WriteLine();
}
}
}
public static void Test()
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
MyInspector inspector = new MyInspector("Add"); // inspecting Add, not Echo
host.AddServiceEndpoint(typeof(ITest), new BasicHttpBinding(), "").Behaviors.Add(inspector);
host.Open();
Console.WriteLine("Host opened");
ChannelFactory<ITest> factory = new ChannelFactory<ITest>(new BasicHttpBinding(), new EndpointAddress(baseAddress));
ITest proxy = factory.CreateChannel();
Console.WriteLine("Calling Echo");
Console.WriteLine(proxy.Echo("Hello world"));
Console.WriteLine();
Console.WriteLine("Calling Add");
Console.WriteLine(proxy.Add(4, 5));
((IClientChannel)proxy).Close();
factory.Close();
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
}

Related

WCF REST read URL-encoded response

I'm trying to use WCF to consume a 3rd-party REST service which responds with URL-encoded data:
a=1&b=2&c=3
I have this now:
[DataContract]
class Response {
[DataMember(Name="a")]
public int A { get;set;}
[DataMember(Name="b")]
public int B { get;set;}
[DataMember(Name="c")]
public int C { get;set;}
}
[ServiceContract]
interface IService
{
[OperationContract]
Response Foo();
}
But it comes back with:
There was an error checking start element of object of type Response. The data at the root level is invalid. Line 1, position 1.
WCF does not "understand" forms-data content type (application/x-www-forms-urlencoded), so it won't be able to read that response directly. You can either implement a message formatter which will be able to convert that format into your contract, or you can receive the response as a Stream (which will give you the full bytes of the response), which you can decode into your class.
The code below shows how you could implement a formatter for this operation. It's not generic, but you should get the picture of what needs to be done.
public class StackOverflow_16493746
{
[ServiceContract]
public class Service
{
[WebGet(UriTemplate = "*")]
public Stream GetData()
{
WebOperationContext.Current.OutgoingResponse.ContentType = "application/x-www-form-urlencoded";
return new MemoryStream(Encoding.UTF8.GetBytes("a=1&b=2&c=3"));
}
}
[ServiceContract]
interface IService
{
[WebGet]
Response Foo();
}
[DataContract]
class Response
{
[DataMember(Name = "a")]
public int A { get; set; }
[DataMember(Name = "b")]
public int B { get; set; }
[DataMember(Name = "c")]
public int C { get; set; }
}
public class MyResponseFormatter : IClientMessageFormatter
{
private IClientMessageFormatter originalFormatter;
public MyResponseFormatter(IClientMessageFormatter originalFormatter)
{
this.originalFormatter = originalFormatter;
}
public object DeserializeReply(Message message, object[] parameters)
{
HttpResponseMessageProperty httpResp = (HttpResponseMessageProperty)
message.Properties[HttpResponseMessageProperty.Name];
if (httpResp.Headers[HttpResponseHeader.ContentType] == "application/x-www-form-urlencoded")
{
if (parameters.Length > 0)
{
throw new InvalidOperationException("out/ref parameters not supported in this formatter");
}
byte[] bodyBytes = message.GetReaderAtBodyContents().ReadElementContentAsBase64();
NameValueCollection pairs = HttpUtility.ParseQueryString(Encoding.UTF8.GetString(bodyBytes));
Response result = new Response();
foreach (var key in pairs.AllKeys)
{
string value = pairs[key];
switch (key)
{
case "a":
result.A = int.Parse(value);
break;
case "b":
result.B = int.Parse(value);
break;
case "c":
result.C = int.Parse(value);
break;
}
}
return result;
}
else
{
return this.originalFormatter.DeserializeReply(message, parameters);
}
}
public Message SerializeRequest(MessageVersion messageVersion, object[] parameters)
{
throw new NotSupportedException("This is a reply-only formatter");
}
}
public class MyClientBehavior : WebHttpBehavior
{
protected override IClientMessageFormatter GetReplyClientFormatter(OperationDescription operationDescription, ServiceEndpoint endpoint)
{
return new MyResponseFormatter(base.GetReplyClientFormatter(operationDescription, endpoint));
}
}
public static void Test()
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
WebServiceHost host = new WebServiceHost(typeof(Service), new Uri(baseAddress));
host.Open();
Console.WriteLine("Host opened");
ChannelFactory<IService> factory = new ChannelFactory<IService>(new WebHttpBinding(), new EndpointAddress(baseAddress));
factory.Endpoint.Behaviors.Add(new MyClientBehavior());
IService proxy = factory.CreateChannel();
Response resp = proxy.Foo();
Console.WriteLine("a={0},b={1},c={2}", resp.A, resp.B, resp.C);
((IClientChannel)proxy).Close();
factory.Close();
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
}

IClientMessageInspector detect one-way operation

I need to do some operation logging in IClientMessageInspector and knowning when an operation has started and ended is vital. I can't however get AfterReceiveReply for one-way operations which is clear why. Is there a way to know that an operation is one-way in BeforeSendRequest overload so I could just ignore it?
There's no information on the inspector itself (or the message passed to BeforeSendRequest), but you can pass this information to the inspector, and use the message action to see whether the operation is one way or not.
public class StackOverflow_10354828
{
[ServiceContract]
public interface ITest
{
[OperationContract]
string Echo(string text);
[OperationContract(IsOneWay = true)]
void Process(string input);
}
public class Service : ITest
{
public string Echo(string text)
{
return text;
}
public void Process(string input) { }
}
class MyInspector : IClientMessageInspector
{
public HashSet<string> oneWayActions;
public MyInspector(ServiceEndpoint endpoint)
{
this.oneWayActions = new HashSet<string>();
foreach (var operation in endpoint.Contract.Operations)
{
if (operation.IsOneWay)
{
oneWayActions.Add(operation.Messages[0].Action);
}
}
}
public void AfterReceiveReply(ref Message reply, object correlationState)
{
Console.WriteLine("In AfterReceiveReply");
}
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
Console.WriteLine("In BeginSendRequest");
if (this.oneWayActions.Contains(request.Headers.Action))
{
Console.WriteLine("This is a one-way operation");
}
return null;
}
}
class MyBehavior : IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
clientRuntime.MessageInspectors.Add(new MyInspector(endpoint));
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
}
public void Validate(ServiceEndpoint endpoint)
{
}
}
public static void Test()
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
host.AddServiceEndpoint(typeof(ITest), new BasicHttpBinding(), "");
host.Open();
Console.WriteLine("Host opened");
ChannelFactory<ITest> factory = new ChannelFactory<ITest>(new BasicHttpBinding(), new EndpointAddress(baseAddress));
factory.Endpoint.Behaviors.Add(new MyBehavior());
ITest proxy = factory.CreateChannel();
proxy.Echo("Hello");
Console.WriteLine();
proxy.Process("world");
Console.WriteLine();
((IClientChannel)proxy).Close();
factory.Close();
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
}
Replying to myself, a the moment I'm doing this:
bool isOneWay = request.Headers.ReplyTo == null;

Implementing RequestWrapper for RESTful WCF service

I've written a simple object called RequestWrapper that contains single method of type:
TResult WrapRequest<TResult>(Func<TResult> action)
It wraps any action with try..catch, error handling, logging, database connection, transaction (commit & rollback), etc.
Currently I use it like this: (example, not production code)
return RequestWrapper.WrapRequest(() =>
{
Topic entity = GetRepository<Topic>().Find(uid);
if (entity == null)
throw new EntityNotFoundException("Topic not found.");
return new Topic
{
Name = entity.Name,
Posts = entity.Posts.Select(x => new Post
{
Body = x.Body,
}).ToList()
};
});
I simply wrap around every method of my RESTful web service (using WCF and WebHttpBinding).
My question is: How should I implement behavior that would do the wrapping for me automatically? Is it possible?
You can use a custom IOperationInvoker which wraps the original one does what you need. The code below shows a sample implementation of one, and you can find more information about invokers at http://blogs.msdn.com/b/carlosfigueira/archive/2011/05/17/wcf-extensibility-ioperationinvoker.aspx.
public class StackOverflow_10156890
{
[ServiceContract]
public interface ITest
{
[WebGet]
[WrappedOperationBehavior]
string Echo(string text);
[WebInvoke(BodyStyle = WebMessageBodyStyle.WrappedRequest)]
[WrappedOperationBehavior]
int Divide(int x, int y);
}
public class Service : ITest
{
public string Echo(string text)
{
return text;
}
public int Divide(int x, int y)
{
return x / y;
}
}
class RequestWrapperOperationInvoker : IOperationInvoker
{
IOperationInvoker originalInvoker;
public RequestWrapperOperationInvoker(IOperationInvoker originalInvoker)
{
this.originalInvoker = originalInvoker;
}
public object[] AllocateInputs()
{
return this.originalInvoker.AllocateInputs();
}
public object Invoke(object instance, object[] inputs, out object[] outputs)
{
Console.WriteLine("Do initialization, etc. here");
object result = null;
try
{
result = this.originalInvoker.Invoke(instance, inputs, out outputs);
}
catch (Exception e)
{
Console.WriteLine("Log exception: {0}: {1}", e.GetType().FullName, e.Message);
result = null;
outputs = null;
}
finally
{
Console.WriteLine("Do finalization, etc. here");
}
return result;
}
public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
{
throw new NotSupportedException("Only synchronous operations supported");
}
public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
{
throw new NotSupportedException("Only synchronous operations supported");
}
public bool IsSynchronous
{
get { return true; }
}
}
class WrappedOperationBehaviorAttribute : Attribute, IOperationBehavior
{
public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
{
}
public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
{
dispatchOperation.Invoker = new RequestWrapperOperationInvoker(dispatchOperation.Invoker);
}
public void Validate(OperationDescription operationDescription)
{
}
}
public static void Test()
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
var endpoint = host.AddServiceEndpoint(typeof(ITest), new WebHttpBinding(), "");
endpoint.Behaviors.Add(new WebHttpBehavior());
host.Open();
Console.WriteLine("Host opened");
WebClient c = new WebClient();
Console.WriteLine(c.DownloadString(baseAddress + "/Echo?text=Hello%20world"));
c = new WebClient();
c.Headers[HttpRequestHeader.ContentType] = "application/json";
Console.WriteLine(c.UploadString(baseAddress + "/Divide", "{\"x\":12,\"y\":0}"));
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
}

WCF Rest weird routing behavior

I have a resource available at
http://localhost/resource
I do not have any route like
http:/localhost/resource?name=john
but when I try to hit the above URI I get the result of http://localhost/resource. I was expecting a 404.
Any idea why its ignoring ?name=john and match the url ??
Query string parameters are optional and not "officially" part of the address - it goes from the scheme to the path (via host and port). There are many scenarios where you have one operation at address http://something.com/path, and in the operation code you look at the query string parameters to make some decision. For example, the code below looks for a "format" parameter in the query string which may or may not be passed, and all requests (with or without it) are correctly routed to the operation.
public class StackOverflow_10422764
{
[DataContract(Name = "Person", Namespace = "")]
public class Person
{
[DataMember]
public string Name { get; set; }
[DataMember]
public int Age { get; set; }
}
[ServiceContract]
public class Service
{
[WebGet(UriTemplate = "/NoQueryParams")]
public Person GetPerson()
{
string format = WebOperationContext.Current.IncomingRequest.UriTemplateMatch.QueryParameters["format"];
if (format == "xml")
{
WebOperationContext.Current.OutgoingResponse.Format = WebMessageFormat.Xml;
}
else if (format == "json")
{
WebOperationContext.Current.OutgoingResponse.Format = WebMessageFormat.Json;
}
return new Person { Name = "Scooby Doo", Age = 10 };
}
}
public static void Test()
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
WebServiceHost host = new WebServiceHost(typeof(Service), new Uri(baseAddress));
host.Open();
Console.WriteLine("Host opened");
WebClient c = new WebClient();
Console.WriteLine(c.DownloadString(baseAddress + "/NoQueryParams"));
c = new WebClient();
Console.WriteLine(c.DownloadString(baseAddress + "/NoQueryParams?format=json"));
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
}
Update after comments:
If you want to force the request to contain exactly the parameters specified in the template, then you can use something like a message inspector to validate that.
public class StackOverflow_10422764
{
[DataContract(Name = "Person", Namespace = "")]
public class Person
{
[DataMember]
public string Name { get; set; }
[DataMember]
public int Age { get; set; }
}
[ServiceContract]
public class Service
{
[WebGet(UriTemplate = "/NoQueryParams")]
public Person GetPerson()
{
string format = WebOperationContext.Current.IncomingRequest.UriTemplateMatch.QueryParameters["format"];
if (format == "xml")
{
WebOperationContext.Current.OutgoingResponse.Format = WebMessageFormat.Xml;
}
else if (format == "json")
{
WebOperationContext.Current.OutgoingResponse.Format = WebMessageFormat.Json;
}
return new Person { Name = "Scooby Doo", Age = 10 };
}
[WebGet(UriTemplate = "/TwoQueryParam?x={x}&y={y}")]
public int Add(int x, int y)
{
return x + y;
}
}
public class MyForceQueryMatchBehavior : IEndpointBehavior, IDispatchMessageInspector
{
#region IEndpointBehavior Members
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(this);
}
public void Validate(ServiceEndpoint endpoint)
{
}
#endregion
#region IDispatchMessageInspector Members
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
UriTemplateMatch uriTemplateMatch = (UriTemplateMatch)request.Properties["UriTemplateMatchResults"];
// TODO: may need to deal with the case of implicit UriTemplates, or if you want to allow for some query parameters to be ommitted
List<string> uriTemplateQueryVariables = uriTemplateMatch.Template.QueryValueVariableNames.Select(x => x.ToLowerInvariant()).ToList();
List<string> requestQueryVariables = uriTemplateMatch.QueryParameters.Keys.OfType<string>().Select(x => x.ToLowerInvariant()).ToList();
if (uriTemplateQueryVariables.Count != requestQueryVariables.Count || uriTemplateQueryVariables.Except(requestQueryVariables).Count() > 0)
{
throw new WebFaultException(HttpStatusCode.NotFound);
}
return null;
}
public void BeforeSendReply(ref Message reply, object correlationState)
{
}
#endregion
}
static void SendRequest(string uri)
{
Console.WriteLine("Request to {0}", uri);
WebClient c = new WebClient();
try
{
Console.WriteLine(" {0}", c.DownloadString(uri));
}
catch (WebException e)
{
HttpWebResponse resp = e.Response as HttpWebResponse;
Console.WriteLine(" ERROR => {0}", resp.StatusCode);
}
}
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(Service), new WebHttpBinding(), "");
endpoint.Behaviors.Add(new WebHttpBehavior());
endpoint.Behaviors.Add(new MyForceQueryMatchBehavior());
host.Open();
Console.WriteLine("Host opened");
SendRequest(baseAddress + "/NoQueryParams");
SendRequest(baseAddress + "/NoQueryParams?format=json");
SendRequest(baseAddress + "/TwoQueryParam?x=7&y=9&z=other");
SendRequest(baseAddress + "/TwoQueryParam?x=7&y=9");
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
}

How do I implement Orchestration on WCF using MessageInspector

I have a WCF Service, And I inspect that using MessageInspector ( inherit IDispatchMessageInspector)
I want to do some thing before running service and result of that ı want not run service.
I want to prevent client call but client dont receive exception.
Can you help me
This scenario looks like the post in the MSDN WCF forum entitled "IDispatchMessageInspector.AfterReceiveRequest - skip operation and manually generate custom response instead". If this is what you need (when you receive a message in the inspector, you decide that you want to skip the service operation, but return a message to the client and the client should not see an exception), then this answer should work for you as well. Notice that you'll need to create the response message in the same format as the client expects, otherwise you'll have an exception.
This code uses three of the (many) WCF extensibility points to achieve that scenario, a message inspector (as you've mentioned you're using), a message formatter and an operation invoker. I've blogged about them in an ongoing series about WCF extensibility at http://blogs.msdn.com/b/carlosfigueira/archive/2011/03/14/wcf-extensibility.aspx.
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();
}
}