Trying to use MessageInspector to modify the message before the wcf service through the proxy. However while debugging the message body does not gets copied and body shows
<s:Body>... stream ...</s:Body>
What is the problem with the code?
public class CustomWCFMessageInspector : IClientMessageInspector
{
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
request = ModifyMessage(request);
return null;
}
private Message ModifyMessage(Message oldMessage)
{
Message newMessage = null;
MessageBuffer msgbuf = oldMessage.CreateBufferedCopy(int.MaxValue);
Message tmpMessage = msgbuf.CreateMessage();
XmlDictionaryReader xdr = tmpMessage.GetReaderAtBodyContents();
XDocument xd = ConvertToXDocument(xdr);
EmitTags(xd);
var ms = new MemoryStream();
var xw = XmlWriter.Create(ms);
xd.Save(xw);
xw.Flush();
xw.Close();
ms.Position = 0;
XmlReader xr = XmlReader.Create(ms);
newMessage = Message.CreateMessage(tmpMessage.Version, null, xr);
newMessage.Headers.CopyHeadersFrom(tmpMessage);
newMessage.Properties.CopyProperties(tmpMessage.Properties);
return newMessage;
}
}
Here is solution:
if you call Message.ToString() you will get
..stream..
Instead use System.Xml.XmlWriter. Here is a sample:
MessageBuffer buffer = reply.CreateBufferedCopy(Int32.MaxValue);
Message msg = buffer.CreateMessage();
StringBuilder sb = new StringBuilder();
using (System.Xml.XmlWriter xw = System.Xml.XmlWriter.Create(sb))
{
msg.WriteMessage(xw);
xw.Close();
}
Console.WriteLine("Message Received:\n{0}", sb.ToString());
The problem was that the newMessage body was not shown in the watch window after doing ToString()
Created the buffered copy of the message to be shown in the debugger.
MessageBuffer messageBuffer = newMessage.CreateBufferedCopy(int.MaxValue);
Message message = messageBuffer.CreateMessage();
So there is No problem in the code. It is just that the debugger is not showing the message body as mentioned in the link below
http://msdn.microsoft.com/en-us/library/ms734675(v=VS.90).aspx
in the Accessing the Message Body for Debugging section.
I suspect ToString will return what you are getting. ToString is often used for debugging, and hence only shows you basic information about the object. You need to do something like this in ConvertToXDocument:
XDocument x = XDocument.Load(xdr);
Related
I got this error upon receving an object from a subscription in azure service bus.
An exception of type 'System.Runtime.Serialization.SerializationException' occurred in System.Runtime.Serialization.dll but was not handled in user code
I've tried some deserialization code but nothing works.
This is how I send a message. Please tell me how to receive it.
public void SendMessage()
{
BrokeredMessage message = new BrokeredMessage(new TestMessage() {
MsgNumber = 1, MsgContent = "testing message" }, new DataContractSerializer(typeof(TestMessage)));
// Send message to the topic
TopicClient topicClient = TopicClient.CreateFromConnectionString(cn, topicNamespace);
topicClient.Send(message);
}
public string ReceiveMessage(){
//??????
}
To receive a single message, you need to get the SubscriptionClient :
public void ReceiveMessage(string connectionString, string topicPath, string subscriptionName)
{
var subscriptionClient = SubscriptionClient.CreateFromConnectionString(connectionString, topicPath, subscriptionName);
var brokeredMessage = subscriptionClient.Receive();
var message = brokeredMessage.GetBody<TestMessage>();
}
I have a wcf service and I am trying to call it from other client, but the response I am getting as a reply is incomplete. It stops from the
public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
if (Log.IsDebugEnabled)
{
Log.Debug("VisService SOAP Response >>>");
//create a copy of the response for logging purpose
MessageBuffer buffer = reply.CreateBufferedCopy(Int32.MaxValue);
reply = buffer.CreateMessage();
//log the copy to avoid removal of the actual response object
System.ServiceModel.Channels.Message replyCopy = buffer.CreateMessage();
MemoryStream ms = new MemoryStream();
System.Xml.XmlDictionaryWriter writer = System.Xml.XmlDictionaryWriter.CreateTextWriter(ms);
replyCopy.WriteMessage(writer);
//move the position of the cursor to the begining to
//read the entire message from start
ms.Position = 0;
string visServiceSOAPResponse = new StreamReader(ms, Encoding.UTF8).ReadLine();
Log.Debug(visServiceSOAPResponse);
//For displaying the message in the mail confirmation box
SaveResponseToLog("\nVisService SOAP Response >>>\n" + visServiceSOAPResponse);
}
}
//This function logs the SOAP
//request in the application log file
public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, IClientChannel channel)
{
if (Log.IsDebugEnabled)
{
string visServiceSOAPRequest = request.ToString();
Log.Debug("VisService SOAP Request >>>");
Log.Debug(visServiceSOAPRequest);
//For displaying the message in the mail confirmation box
SaveResponseToLog("\nVisService SOAP Request >>>\n" + visServiceSOAPRequest);
}
return null;
}
}
and the response from the wcf is only till the "...xmlns="
Your log writing function is buggy. Please scrap it alltogether. Use AppendAllText if you want to append text to a file.
In addition, you seem to read only the first line of the response. You need to read the whole response before writing it to the file.
Edit:
Your log function has too many bugs and weird things to actually fix it. Replace it with:
private void SaveResponseToLog(string msg)
{
System.IO.File.AppendAllText(filename, msg, Encoding.UTF8);
}
I have a Custom ClientMessageInspector that records requests but not replies to my service.
The code is:
namespace MessageListener.Instrumentation
{
public class MessageInspector : IClientMessageInspector
{
private Message TraceMessage(MessageBuffer buffer)
{
// Must use a buffer rather than the original message, because the Message's body can be processed only once.
Message msg = buffer.CreateMessage();
using (RREM_GilbaneEntities3 entities3 = new RREM_GilbaneEntities3())
{
SOAPMessage soapMessages = new SOAPMessage
{
SOAPMessage1 = msg.ToString(),
created = DateTime.Now,
source = "Interface12",
sourceIP = "Interface12"
};
entities3.SOAPMessages.Add(soapMessages);
entities3.SaveChanges();
}
//Return copy of origonal message with unalterd State
return buffer.CreateMessage();
}
public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
reply = TraceMessage(reply.CreateBufferedCopy(int.MaxValue));
}
public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)
{
request = TraceMessage(request.CreateBufferedCopy(int.MaxValue));
return null;
}
}
}
What seems to be happening is both AfterRecievReply and BeforeSendRequest are being called. In AfterRecieveReply before I call TraceMessage, I can see the whole reply. Inside TraceMessage, when I do:
// Must use a buffer rather than the original message, because the Message's body can be processed only once.
Message msg = buffer.CreateMessage();
it turns the reply into junk:
msg {<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header />
<soap:Body>... stream ...</soap:Body>
</soap:Envelope>}
What's going on?
The reply isn't a junk message - it's just when you call ToString on it that it doesn't show the body of the message. Remember that a message can only be consumed once; once its body is read, it cannot be read again. Since many places (including the watch window of debuggers) will call ToString on an object, this method is implemented in a way that if it doesn't know for sure that a message body can be read multiple times, then it won't, which seems to be your case. If you want to really write out the message, try using this code:
public string MessageToString(Message message) {
using (MemoryStream ms = new MemoryStream()) {
XmlWriterSettings ws = new XmlWriterSettings();
ws.Encoding = new UTF8Encoding(false);
using (XmlWriter w = XmlWriter.Create(ms)) {
message.WriteMessage(w);
w.Flush();
return ws.Encoding.GetString(ms.ToArray());
}
}
}
void validateMessage(ref System.ServiceModel.Channels.Message message)
{
XmlDocument bodyDoc = new XmlDocument();
var body = message.GetReaderAtBodyContents();
bodyDoc.Load(body);
XmlReaderSettings settings = new XmlReaderSettings();
settings.Schemas = schemas;
settings.ValidationType = ValidationType.Schema;
settings.ValidationEventHandler += new ValidationEventHandler(ValidationCallBack);
XmlReader reader = XmlReader.Create(new XmlNodeReader(bodyDoc), settings);
while (reader.Read()) ; // do nothing, just validate
// Create new message
Message newMsg = Message.CreateMessage(message.Version, null,
new XmlNodeReader(bodyDoc.DocumentElement));
newMsg.Headers.CopyHeadersFrom(message);
foreach (string propertyKey in message.Properties.Keys)
newMsg.Properties.Add(propertyKey, message.Properties[propertyKey]);
// Close the original message and return new message
message.Close();
message = newMsg;
}
private static void ValidationCallBack(object sender, ValidationEventArgs e)
{
Console.WriteLine("Validation Error: {0}", e.Message);
//throw new RequestValidationFault(e.Message);
throw new FaultException<string>(e.Message);
}
object IDispatchMessageInspector.AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
{
try
{
validateMessage(ref request);
}
catch (FaultException e)
{
throw new FaultException<string>(e.Message);
}
return null;
}
The above code screenshot help me validate message requested from client against the schema and throw the errors if some inconsistency happen. But I don't know how to pass error message back to the WCFservice since I also would like to return the error message to the client.
I tried to add the error message to request message and return to WCF service. But I have no idea on how to put the error message to its parent element in the body content which is easy for me to get it in the service operation. Thanks in advance!
I have a MessageInspector which logs the messages that come through? What is the reason why you can only read a Message once and must create a copy? I have seen the documentation from MSDN that I need to create a buffered copy, but I don't know why it is implemented this way? Can someone explain it to me?
private static void SendRequest(string request)
{
var req = (HttpWebRequest) WebRequest.Create("http://urltoservice.svc/MethodToCall");
req.ContentType = "text/xml";
req.Method = "POST";
using (var stm = req.GetRequestStream())
{
using (var stmw = new StreamWriter(stm))
{
stmw.Write(request);
}
}
byte[] myData;
using (var webResponse = req.GetResponse())
{
var responseStream = webResponse.GetResponseStream();
myData = ReadFully(responseStream);
}
// Do whatever you need with the response
string responseString = Encoding.ASCII.GetString(myData);
}
If I don't have access to the server part or the ability to change the MessageInspector to use a buffered copy of the message, can I modify the message above to make a copy of a stream? If so, how would I go about doing that?
You can copy your message into a buffer and play with it.
More details about working with messages you can find on the following link: http://msdn.microsoft.com/en-us/library/ms734675.aspx