【发布时间】:2009-01-20 15:26:16
【问题描述】:
我正在调用外部 HTTPS 网络服务。
为了检查有什么问题,所有者需要我发送的 SOAP 请求。
我有一个 web 参考和 VS 2008 生成的代理类...
有没有办法在发送之前查看 SOAP 消息?
我正在考虑一些 .net 代码...因为我尝试的嗅探器没有“看到”网络服务调用不知道为什么。
【问题讨论】:
标签: .net web-services soap
我正在调用外部 HTTPS 网络服务。
为了检查有什么问题,所有者需要我发送的 SOAP 请求。
我有一个 web 参考和 VS 2008 生成的代理类...
有没有办法在发送之前查看 SOAP 消息?
我正在考虑一些 .net 代码...因为我尝试的嗅探器没有“看到”网络服务调用不知道为什么。
【问题讨论】:
标签: .net web-services soap
您可以在提交之前简单地序列化请求对象,如下所示:
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
【讨论】:
【讨论】:
您可以使用 IClientMEssageInspector 和 IEndpointBehavior 来完成此操作。我发现使用这种方式可以像提琴手一样捕获确切的肥皂请求:
在同一个项目中创建一个这样的类:
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);
}
}
那么你可以像下面这样调用它:
ProxyClass _class = new ProxyClass();
var requestInterceptor = new CustomInspectorBehavior();
_client.Endpoint.Behaviors.Add(requestInterceptor);
当你调用服务方法时,它会自动执行拦截器并打印输出。使用这种方式,您还可以在发送到服务器之前操作soap消息!
【讨论】:
@mting923 的建议真的很有帮助,谢谢!
其他想法(例如利用 SoapExtension,或创建类似于 How do I get access to SOAP response 的“间谍”类)很有趣,但不能在 .NET Core 中使用。但是生成的 SOAP 代理类在底层仍然只是一个 WCF 客户端,因此 IClientMessageInspector 方法很有效,即使对于调用旧 SOAP Web 服务的 .NET Core Azure 函数也是如此。
在上面的示例中,响应连接是隐含的,但未完全显示。此外,在最新的 .NET Core API 中,MessageInspectors 现在是 ClientMessageInspectors,Behaviors 现在是 EndpointBehaviors。因此,为了完整起见,以下内容适用于 .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_);
}
}
然后可以这样设置:
var client = new ServiceClient();
var soapInspector = new SoapInspectorBehavior();
client.Endpoint.EndpointBehaviors.Add(soapInspector);
在客户端代理上调用 Web 服务调用后,soapInspector.LastRequestXml 和 soapInspector.LastResponseXml 将包含原始 SOAP 请求和响应(作为字符串)。
【讨论】:
将此添加到 web.config 或 App.config 文件的元素中。它将在项目的 bin/Debug 文件夹中创建一个 trace.log 文件。或者,您可以使用 initializeData 属性指定日志文件的绝对路径。
<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>
它警告说不允许使用 maxdatasize 和 tracemode 属性,但它们会增加可记录的数据量,并避免以十六进制记录所有内容。
【讨论】:
如果您在更受限制的环境中工作并且没有使用 Fiddler 之类的应用程序的奢侈,您可以执行以下操作:
using (var reader = new System.IO.StreamReader(Request.InputStream))
{
result = reader.ReadToEnd();
}
这不是一个理想或漂亮的解决方案,但如果您在适度受限的环境中工作,它就能完成工作。
【讨论】: