This is a continuation of the Stack Overflow topic The equivalent of Fiddler SOAP Request/Response in the Database. I implemented a ClientMessageInspector like so:
public class RREMOutputMessageInspector : IClientMessageInspector
{
private string _connStringProd = String.Empty;
public void AfterReceiveReply(ref Message reply, object correlationState)
{
MessageBuffer buffer = reply.CreateBufferedCopy(Int32.MaxValue);
reply = buffer.CreateMessage();
_connStringProd = ConfigurationManager.ConnectionStrings["RREM_Gilbane_ProdConnectionString"].ToString();
NREMGilbaneTableAdapters.SOAPMessagesTableAdapter soapMessages =
new NREMGilbaneTableAdapters.SOAPMessagesTableAdapter();
soapMessages.Connection.ConnectionString = _connStringProd;
string replyString = buffer.CreateMessage().ToString();
soapMessages.Insert(replyString, DateTime.Now, "Interface12", "Interface12", "", "", "", "");
}
public object BeforeSendRequest(ref Message request, System.ServiceModel.IClientChannel channel)
{
MessageBuffer buffer = request.CreateBufferedCopy(Int32.MaxValue);
request = buffer.CreateMessage();
_connStringProd = ConfigurationManager.ConnectionStrings["RREM_Gilbane_ProdConnectionString"].ToString();
NREMGilbaneTableAdapters.SOAPMessagesTableAdapter soapMessages =
new NREMGilbaneTableAdapters.SOAPMessagesTableAdapter();
soapMessages.Connection.ConnectionString = _connStringProd;
string requestString = buffer.CreateMessage().ToString();
soapMessages.Insert(requestString, DateTime.Now, "Interface12", "Interface12", "", "", "", "");
return null;
}
Then I implemented a behavior for the inspector:
public class RREMBehavior : IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
throw new NotImplementedException();
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
{
RREMOutputMessageInspector inspector = new RREMOutputMessageInspector();
clientRuntime.MessageInspectors.Add(inspector);
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
{
throw new NotImplementedException();
}
public void Validate(ServiceEndpoint endpoint)
{
throw new NotImplementedException();
}
Finally in my WCF Service, I have the following when making a call to the other web service:
documentSvcResponse = client.retrieveDocument(authHeader, retrieveDoc);
client.Endpoint.Behaviors.Add(new RREMBehavior());
I don't hit any breakpoints in my Inspector. Are you supposed to be able to set breakpoints in an inspector. Also, this web service has a loop implemented where the above 2 lines are being executed multiple times. Is that the problem? I see no evidence that this is working, Help again please!
Yes you should. Have you added the endpointbehavior and extensions to your web.config file? Have a look at this msdn article: http://msdn.microsoft.com/en-us/library/system.servicemodel.dispatcher.iclientmessageinspector(v=vs.110).aspx
I don't really understand your question regarding the loop? But it sound a bit strange.
Related
I'm well aware of how to use FaultException with details. I know I can declare details contract, then I need to decorate the method which is expected to throw this kind of exceptions with [FaultContract(type(DetailsContractType))] and then I do throw FaultException in that method. All that is understood and worked. What I need is to be able to throw FaultException from all methods of all contracts in my WCF host. Adding [FaultContract(type(DetailsContractType))] to each method of each operation contract seems to much to me. Is there another way to allow this kind of exceptions without decorating methods with that attribute? If I just remove that attribute everything stops working and the exception becomes just FaultException on the client side. I was thinking about DataContractResolver but it looks like it is not involved in DetailsContractType resolution. Any ideas, hints, solutions?
Using IErrorHandler does not relieve you from decorating contract operations with the FaultContractAttribute what I'm trying to avoid. It is even stated in the example you referred, there is a comment there
// This behavior requires that the contract have a SOAP fault with a detail type of
GreetingFault.
and
throw new InvalidOperationException(String.Format(
"EnforceGreetingFaultBehavior requires a "
+ "FaultContractAttribute(typeof(GreetingFault)) in each operation contract. "
+ "The \"{0}\" operation contains no FaultContractAttribute.",
opDesc.Name)
);
You can implement the IErrorHandler interface to uniformly handle errors in WCF,here is a Demo:
[ServiceContract]
public interface IDemo {
[OperationContract]
void DeleteData(int dataId);
}
class DemoService : IDemo
{
public void DeleteData(int dataId)
{
if (dataId<0) {
throw new ArgumentException("error");
}
}
}
The above code is the interface and implementation class of WCF service.
class MyCustErrorHandler : IErrorHandler
{
public bool HandleError(Exception error)
{
return true;
}
public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
FaultException faultException = new FaultException(error.Message);
MessageFault messageFault = faultException.CreateMessageFault();
fault = Message.CreateMessage(version,messageFault,"my-test-error");
}
}
The above code is the implementation class of the IErrorHandler interface.
class MyEndpointBehavior : IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
return;
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
return;
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
MyCustErrorHandler handler = new MyCustErrorHandler();
endpointDispatcher.ChannelDispatcher.ErrorHandlers.Add(handler);
}
public void Validate(ServiceEndpoint endpoint)
{
return;
}
}
We add a custom error handling class by extending the Behavior method of the endpoint.
ServiceEndpoint ep = selfHost.AddServiceEndpoint(typeof(IDemo), new BasicHttpBinding(), "CalculatorService");
MyEndpointBehavior myEndpointBehavior = new MyEndpointBehavior();
ep.EndpointBehaviors.Add(myEndpointBehavior);
The client executes the following code will print "error" in the console:
try {
demoClient.DeleteData(-3);
}
catch (FaultException fault) {
string err = fault.Reason.GetMatchingTranslation().Text;
Console.WriteLine(err);
}
For more information about IErrorhandler,Please refer to the following link:
https://learn.microsoft.com/en-us/dotnet/api/system.servicemodel.dispatcher.ierrorhandler?view=netframework-4.8
UPDATE
If you don’t want to use IErrorhandler, you can also use FaultReason:
public string SayHello(string name) {
if (name.Length<2) {
FaultReasonText faultReasonText = new FaultReasonText("name length cannot be less than 2");
FaultReason reason = new FaultReason(faultReasonText);
throw new FaultException(reason);
}
return "hello";
}
The client needs to catch exceptions when calling:
try {
string res = channnel.Sayhello("B");
}
catch (FaultException fex) {
if (fex.Reason != null) {
FaultReason reason = fex.Reason;
//Get error information
FaultReasonText txt = reason.GetMatchingTranslation();
Console.WriteLine(txt.Text);
}
}
I have some doubts about Spring AMQP and the correlationId of an AMQP message.
I have a Project with two queues (“queue.A” and “queue.B”) and one MessageListener on each:
public class ServerHandlerQueueA implements MessageListener {
#Override
public void onMessage(Message msg)
public class ServerHandlerQueueB implements MessageListener {
#Override
public void onMessage(Message msg)
In some cases, when I receive a message in the “queue.A”, I have to redirect it to “queue.B”:
rabbitTemplate.convertAndSend(routingkey, msg, new MessagePostProcessor()
{ …});
In all cases I send the response to the client using the following:
String routingkey = msg.getMessageProperties().getReplyTo();
rabbitTemplate.convertAndSend(routingkey, respuesta, new MessagePostProcessor() {
#Override
public Message postProcessMessage(Message msg) throws AmqpException
{….}
});
This is working correctly if I use Spring AMQP on the client side:
Object _response = getRabbitOperations().convertSendAndReceive(requestExchange, routingKeyManagement, msg,
new MessagePostProcessor()
{
public Message postProcessMessage(Message message) throws AmqpException
{….}
});
But If I use the java client (on then client side):
RpcClient _rpcClient = new RpcClient(channel, exchangeName, routingKey);
Response _response = _rpcClient.doCall(new AMQP.BasicProperties.Builder()
.contentType("application/json")
.deliveryMode(2)
.priority(1)
.userId("myUser")
.appId("MyApp")
.replyTo(replyQueueName)
.correlationId(corrId)
.type("NewOrder")
.build(),
messageBodyBytes);
I always get a NullPointerException in:
com.rabbitmq.client.RpcClient$1.handleDelivery(RpcClient.java:195)
I think it's because of the correlationId treatment. When I send a message with Spring AMQP I can see the “spring_listener_return_correlation” and “spring_request_return_correlation” headers in the consumer, but the “correlationId” property is always null.
How can I make it compatible with the pure java client and with Spring AMQP? I am doing something wrong?
Thanks!
------ EDIT ----------
I’ve upgraded to Spring AMQP 1.7.4 version.
I send a message like this:
Object respuesta = getRabbitOperations().convertSendAndReceive(requestExchange, routingKey, _object,
new MessagePostProcessor()
{
public Message postProcessMessage(Message message) throws AmqpException
{
message.getMessageProperties().setUserId(“myUser”);
message.getMessageProperties().setType(“myType”);
message.getMessageProperties().setAppId("myApp");
message.getMessageProperties().setMessageId(counter.incrementAndGet() + "-myType");
message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.NON_PERSISTENT);
message.getMessageProperties().setRedelivered(false);
return message;
}
});
On the server I have:
#Override
public void onMessage(Message msg)
{
MessageProperties mp = msg.getMessageProperties();
Gson __gson = new Gson();
String _stringMP = __gson.toJson(mp);
System.out.println("MessageProperties:\n" + _stringMP);
}
And I think the problem is that I always get the correlationId null:
{"headers":{"spring_listener_return_correlation":"49bd0a84-9abb-4719-b8a7-8668a4a77f32","spring_request_return_correlation":"32","__TypeId__":"MyType"},"messageId":"32-MyType","appId":"myApp","type":"MyType","replyTo":"amq.rabbitmq.reply-to.g2dkABByYWJiaXRATkRFUy1QQzAyAAAsMwAAAAgD.ia4+GgHgoeBnajbHxOgW+w\u003d\u003d","contentType":"application/json","contentEncoding":"UTF-8","contentLength":0,"contentLengthSet":false,"priority":0,"redelivered":false,"receivedExchange":"requestExchange","receivedRoutingKey":"inquiry","receivedUserId":"myUser",
"deliveryTag":5,"deliveryTagSet":true,"messageCount":0,"consumerTag":"amq.ctag-4H_P9CbWYZMML-QsmyaQYQ","consumerQueue":"inquiryQueue","receivedDeliveryMode":"NON_PERSISTENT"}
If I use the Java Client I can see the correlationId:
{"headers":{},"appId":"XBID","type":"MyOrders","correlationId":[49], ….
------------ EDIT 2 --------------------------------
I have tried with:
getRabbitOperations().convertAndSend(requestExchange, routingKeyInquiry,
_object,
new MessagePostProcessor()
{
public Message postProcessMessage(Message message) throws AmqpException
{
message.getMessageProperties().setUserId(“myUser”);
message.getMessageProperties().setType(“myType”);
message.getMessageProperties().setAppId("myApp");
message.getMessageProperties().setMessageId(counter.incrementAndGet() + "-myType");
message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.NON_PERSISTENT);
message.getMessageProperties().setRedelivered(false);
message.getMessageProperties().setCorrelationIdString(UUID.randomUUID().toString());
return message;
}
});
But the "correlationId" is always null at the server side.
What version are you using?
The return correlation headers have nothing to do with correlationId; they are used to correlate returned (mandatory) requests and replies.
As long as you copy the correlationId and replyTo from the queue.A message to the queue.B message, it should all work ok.
If you can't figure it out, post debug logs from all 3 servers someplace.
EDIT
This works fine for me...
#SpringBootApplication
public class So46316261Application implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(So46316261Application.class, args).close();
}
#Autowired
private RabbitTemplate template;
#Override
public void run(String... arg0) throws Exception {
Object reply = this.template.convertSendAndReceive("queue.A", "foo");
System.out.println(reply);
Connection conn = this.template.getConnectionFactory().createConnection();
Channel channel = conn.createChannel(false);
RpcClient client = new RpcClient(channel, "", "queue.A");
Response response = client.doCall(new AMQP.BasicProperties.Builder()
.contentType("text/plain")
.deliveryMode(2)
.priority(1)
.userId("guest")
.appId("MyApp")
.replyTo("amq.rabbitmq.reply-to")
.correlationId("bar")
.type("NewOrder")
.build(),
"foo".getBytes());
System.out.println(new String(response.getBody()));
channel.close();
conn.close();
}
#Bean
public Queue queueA() {
return new Queue("queue.A");
}
#Bean
public Queue queueB() {
return new Queue("queue.B");
}
#RabbitListener(queues = "queue.A")
public void listen(Message in) {
System.out.println(in);
this.template.send("queue.B", in);
}
#RabbitListener(queues = "queue.B")
public String listenB(Message in) {
System.out.println(in);
return "FOO";
}
}
(Body:'foo' MessageProperties [headers={}, correlationId=1, replyTo=amq.rabbitmq.reply-to.g2dkABByYWJiaXRAbG9jYWxob3N0AAACyAAAAAAB.hp0xZxgVpXcuj9+5QkcOOw==, contentType=text/plain, contentEncoding=UTF-8, contentLength=0, priority=0, redelivered=false, receivedExchange=, receivedRoutingKey=queue.B, deliveryTag=1, consumerTag=amq.ctag-oanHvT3YyUb_Lajl0gpZSQ, consumerQueue=queue.B])
FOO
(Body:'foo' MessageProperties [headers={}, appId=MyApp, type=NewOrder, correlationId=1, replyTo=amq.rabbitmq.reply-to.g2dkABByYWJiaXRAbG9jYWxob3N0AAACzAAAAAAB.okm02YXf0s0HdqZynVIn2w==, contentType=text/plain, contentLength=0, priority=1, redelivered=false, receivedExchange=, receivedRoutingKey=queue.B, deliveryTag=2, consumerTag=amq.ctag-oanHvT3YyUb_Lajl0gpZSQ, consumerQueue=queue.B])
FOO
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;
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();
}
}
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();
}
}