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);
}
Related
In one of our code, we are getting below error.
at System.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32 errorCode, IntPtr errorInfo)
at System.Web.Util.Misc.ThrowIfFailedHr(Int32 hresult)
at System.Web.Hosting.IIS7WorkerRequest.SetUnknownRequestHeader(String name, String value, Boolean replace)
at System.Web.HttpHeaderCollection.SetHeader(String name, String value, Boolean replace)
at System.Web.HttpHeaderCollection.Add(String name, String value)
Code is as below:
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
string correlationId = newAuditTrail.GetCorrelationIdFromRequest(request).ToString();
string url = newAuditTrail.GetUrlFromRequest(request).ToString();
HttpContext.Current.Request.Headers.Add("CorrelatinId", correlationId);
HttpContext.Current.Request.Headers.Add("Url", url);
Error is thrown on line:
HttpContext.Current.Request.Headers.Add("CorrelatinId", correlationId);
I noticed operation contract of method, it is defined as oneway.
[OperationContract(IsOneWay=true)]
If you want to add http header in the http request, please refer to the below code segements.
public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)
{
HttpRequestMessageProperty httpRequestMessage;
object httpRequestMessageObject;
if (request.Properties.TryGetValue(HttpRequestMessageProperty.Name, out httpRequestMessageObject))
{
httpRequestMessage = httpRequestMessageObject as HttpRequestMessageProperty;
if (string.IsNullOrEmpty(httpRequestMessage.Headers[USER_AGENT_HTTP_HEADER]))
{
httpRequestMessage.Headers[USER_AGENT_HTTP_HEADER] = this.m_userAgent;
}
}
else
{
httpRequestMessage = new HttpRequestMessageProperty();
httpRequestMessage.Headers.Add(USER_AGENT_HTTP_HEADER, this.m_userAgent);
request.Properties.Add(HttpRequestMessageProperty.Name, httpRequestMessage);
}
return null;
}
We could also use WebOperationContext to add http header on the client-side.
Add Request header to WCF when using ConfigurationChannelFactory.CreateChannel
Please refer to the below discussion.
How to add a custom HTTP header to every WCF call?
Feel free to let me know if the problem still exists.
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());
}
}
}
I just got started intercepting requests to my WCF service.
I'm calling the web service with java code that looks like this ( short version )
connection = (HttpURLConnection)url.openConnection();
connection.setRequestMethod("GET");
connection.setRequestProperty("Username", "Testname");
I'm receiving the request but I cant get/find the headers in the message request. I've tried something like this:
public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
{
int headerIndex = request.Headers.FindHeader("Username", string.Empty);
var username = request.Headers["Username"]
return null;
}
But I always end up with -1 or exceptions. What is the right way to do this? Am I doing it wrong on the Java side as well?
The Headers property in the Message class will give you the SOAP headers; what you're looking for are the HTTP headers. To get to those, you should use the HttpRequestMessageProperty:
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
var prop = (HttpRequestMessageProperty)request.Properties[HttpRequestMessageProperty.Name];
var userName = prop.Headers["Username"];
return null;
}
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!
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);