I\'m calling an external HTTPS webservice.
In order to check what is wrong, the owner needs the SOAP request I\'m sending.
I have a web reference and the gen
Add this to the element of your web.config or App.config file. It will create a trace.log file in your project's bin/Debug folder. Or, you can specify an absolute path for the log file using the initializeData attribute.
<system.diagnostics>
<trace autoflush="true"/>
<sources>
<source name="System.Net" maxdatasize="9999" tracemode="protocolonly">
<listeners>
<add name="TraceFile" type="System.Diagnostics.TextWriterTraceListener" initializeData="trace.log"/>
</listeners>
</source>
</sources>
<switches>
<add name="System.Net" value="Verbose"/>
</switches>
</system.diagnostics>
It warns that the maxdatasize and tracemode attributes are not allowed, but they increase the amount of data that can be logged, and avoid logging everything in hex.
If you work in a more restricted environment and don't have the luxury of using an application like Fiddler, you can do the following:
using (var reader = new System.IO.StreamReader(Request.InputStream))
{
result = reader.ReadToEnd();
}
It's not an ideal or pretty solution, but if you are working in a moderately restricted environment, it gets the job done.
@mting923's suggestion was really helpful, thanks!
Other ideas (e.g. leveraging SoapExtension, or creating 'spy' classes a la How do I get access to SOAP response) are interesting, but can't be used in .NET Core. But a generated SOAP proxy class is still just a WCF client under the hood, and so the IClientMessageInspector approach works a treat, even for an .NET Core Azure Function calling an older SOAP web service.
In the example above, the response wireup was implied but not fully shown. Also, in the latest .NET Core APIs, MessageInspectors
is now ClientMessageInspectors
and Behaviors
is now EndpointBehaviors
. So for completeness, the following works for me in a .NET Core 3.1 Azure Function:
public class SoapMessageInspector : IClientMessageInspector
{
public string LastRequestXml { get; private set; }
public string LastResponseXml { get; private set; }
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
LastRequestXml = request.ToString();
return request;
}
public void AfterReceiveReply(ref Message reply, object correlationState)
{
LastResponseXml = reply.ToString();
}
}
public class SoapInspectorBehavior : IEndpointBehavior
{
private readonly SoapMessageInspector inspector_ = new SoapMessageInspector();
public string LastRequestXml => inspector_.LastRequestXml;
public string LastResponseXml => inspector_.LastResponseXml;
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
}
public void Validate(ServiceEndpoint endpoint)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
clientRuntime.ClientMessageInspectors.Add(inspector_);
}
}
And then it can be set up like this:
var client = new ServiceClient();
var soapInspector = new SoapInspectorBehavior();
client.Endpoint.EndpointBehaviors.Add(soapInspector);
After invoking a web service call on the client proxy, soapInspector.LastRequestXml
and soapInspector.LastResponseXml
will contain the raw SOAP request and response (as strings).
What you need is a SoapExtension. There's quite a few good examples here:
How do I get access to SOAP response
Getting RAW Soap Data from a Web Reference Client running in ASP.net
XML Parse error while processing the SOAP response
One of the articles linked to: http://msdn.microsoft.com/en-us/magazine/cc164007.aspx
Also search SO for: https://stackoverflow.com/search?q=SoapExtension
You can use IClientMEssageInspector and IEndpointBehavior to fullfill this. I found using this way can capture the exact soap request likely as the fiddler one:
Create a class like this in the same project:
public class ClientMessageInspector : System.ServiceModel.Dispatcher.IClientMessageInspector
{
#region IClientMessageInspector Members
public string LastRequestXml { get; private set; }
public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
}
public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)
{
string requestHeaderName = request.Headers.Action.Replace("urn:#",string.Empty);
LastRequestXml = request.ToString();
string serializedRequestFile = string.Format(requestHeaderName + "_request_{0}.xml", DateTime.Now.ToString("yyyyMMddHHmmss"));
string exportedFolder = ConfigurationManager.AppSettings["SubmittedRequestXmLocation"];
printSoapRequest(request, exportedFolder, serializedRequestFile);
return request;
}
public void printSoapRequest(System.ServiceModel.Channels.Message request, string exportedFolder, string fileName)
{
if (exportedFolder.Equals(string.Empty))
return;
if (!Directory.Exists(exportedFolder))
{
Directory.CreateDirectory(exportedFolder);
}
string exportedFile = string.Format("{0}\\{1}", exportedFolder, fileName);
if (File.Exists(exportedFile))
{
File.Delete(exportedFile);
}
string strRequestXML = request.ToString();
XDocument xDoc = XDocument.Parse(strRequestXML);
XmlWriter xw = XmlWriter.Create(exportedFile);
xDoc.Save(xw);
xw.Flush();
xw.Close();
LogOutput("Request file exported: " + exportedFile);
}
}
public class CustomInspectorBehavior : IEndpointBehavior
{
private readonly ClientMessageInspector clientMessageInspector = new ClientMessageInspector();
public string LastRequestXml
{
get { return clientMessageInspector.LastRequestXml; }
}
public string LastResponseXml
{
get { return clientMessageInspector.LastRequestXml; }
}
public void AddBindingParameters(
ServiceEndpoint endpoint,
System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
}
public void Validate(ServiceEndpoint endpoint)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
clientRuntime.MessageInspectors.Add(clientMessageInspector);
}
}
Then you can call it like the following:
ProxyClass _class = new ProxyClass();
var requestInterceptor = new CustomInspectorBehavior();
_client.Endpoint.Behaviors.Add(requestInterceptor);
When you call the service method, it will automatically execute the interceptor and print the output. Using this way you can also manipulate the soap message before sending to the server!
You can simply serialize the request object, before subtmit, like this:
var sreq = new SomeSoapRequest();
// ... fill in here ...
var serxml = new System.Xml.Serialization.XmlSerializer(sreq.GetType());
var ms = new MemoryStream();
serxml.Serialize(ms, sreq);
string xml = Encoding.UTF8.GetString(ms.ToArray());
// in xml string you have SOAP request