如何获得WCF Web服务请求的XML SOAP请求?


Answers:


140

我认为您的意思是您想在客户端看到XML,而不是在服务器上跟踪它。在这种情况下,您的答案将在上面我链接的问题中,以及如何在客户端上检查或修改消息中。但是,由于该文章的.NET 4版本缺少C#,并且.NET 3.5示例中有些混乱(如果不是错误的话),因此此处将其扩展为您的用途。

您可以使用IClientMessageInspector拦截消息,然后再将其发送出去:

using System.ServiceModel.Dispatcher;
public class MyMessageInspector : IClientMessageInspector
{ }

该界面中的方法BeforeSendRequestAfterReceiveReply,使您可以访问请求和答复。要使用检查器,您需要将其添加到IEndpointBehavior中

using System.ServiceModel.Description;
public class InspectorBehavior : IEndpointBehavior
{
    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
    {
        clientRuntime.MessageInspectors.Add(new MyMessageInspector());
    }
}

您可以将该接口的其他方法保留为空的实现,除非您也想使用它们的功能。阅读操作方法以获取更多详细信息。

实例化客户端后,将行为添加到端点。使用示例WCF项目中的默认名称:

ServiceReference1.Service1Client client = new ServiceReference1.Service1Client();
client.Endpoint.Behaviors.Add(new InspectorBehavior());
client.GetData(123);

在中设置一个断点MyMessageInspector.BeforeSendRequest()request.ToString()重载以显示XML。

如果要完全处理消息,则必须处理消息的副本。有关详细信息,请参见使用消息类

感谢Zach Bonham对另一个找到这些链接的问题的回答


我给了你和Zach Bonham都答覆!感谢您的简单解决方案。我将其连接起来以根据配置写入文件
Rhyous 2014年

公共对象BeforeSendRequest(ref消息请求,IClientChannel通道){如果(ConfigurationManager.AppSettings [“ SaveWCFRequestXmlFiles”]!=“ true”)返回null;var file = Path.Combine(ConfigurationManager.AppSettings [“ XmlFilePath”],“ Request-” + DateTime.Now.ToString(“ yyyyMMdd-Hmmssfff”)+“ .xml”); FileWriter.Write(request.ToString(),file); 返回null; }
Rhyous 2014年

1
这行得通,但它并不能满足我(以及我想做的OP)的要求,因为它无法让您访问到达HTTP级别的原始传入主体。在获取XML之前,已经对其进行了验证。当Web服务(有时会发生)回复HTML错误页面时,我需要获取该页面的HTML。我得到的,而且绝对不需要的,是XML的一部分(Message.ToString中的“ ... Stream ...”,以及从XmlReader读取的带有ReadOuterXml和ReadInnerXml的不完整XML) )
Luc VdV

类似于@LucVdV的注释,这没有给您实际的请求。产生的内容与Fiddler告诉您的内容之间确实存在微小差异。差异非常大,以至于在发送请求时将结果复制到SoapUI或Postman中都会产生奇怪的错误。
asontu

29

选项1

使用消息跟踪/记录

在这里这里看看。


选项2

您始终可以使用Fiddler来查看HTTP请求和响应。


选项3

使用System.Net跟踪


1
由于某些原因在使用偏执级别的安全配置(例如,基于SSL的相互证书)时,请小心Fiddler,代理的存在会导致以下异常:System.ServiceModel.Security.SecurityNegotiationException
user1778770 2014年

1
不知道为什么选项1对我不起作用。尝试了几种不同的消息记录配置选项,并且正文未在SvcTraceViewer中显示。
2016年

选项1对我有用。我能够看到整个SOAP消息,包括传出消息和传入消息。我必须设置一个服务级别标志才能看到我想要的东西。logMessagesAtServiceLevel = “真”
Amonn

@vbguyny使用网络跟踪的完整示例?
PreguntonCojoneroCabrón

7
OperationContext.Current.RequestContext.RequestMessage 

该上下文是请求处理期间可访问的服务器端。这不适用于单向操作


2
只有服务器端,而不是客户端
PreguntonCojoneroCabrón

7

我们可以简单地将请求消息跟踪为。

OperationContext context = OperationContext.Current;

if (context != null && context.RequestContext != null)

{

Message msg = context.RequestContext.RequestMessage;

string reqXML = msg.ToString();

}

1
我想这在服务端点(服务器)上可用。是否有办法从发送请求的客户端获得相同的内容?
Himansz

3

我只是想将此添加到Kimberly的答案中。也许可以节省一些时间并避免由于未实现IEndpointBehaviour接口所需的所有方法而导致的编译错误。

最好的祝福

尼基

    /*
        // This is just to illustrate how it can be implemented on an imperative declarared binding, channel and client.

        string url = "SOME WCF URL";
        BasicHttpBinding wsBinding = new BasicHttpBinding();                
        EndpointAddress endpointAddress = new EndpointAddress(url);

        ChannelFactory<ISomeService> channelFactory = new ChannelFactory<ISomeService>(wsBinding, endpointAddress);
        channelFactory.Endpoint.Behaviors.Add(new InspectorBehavior());
        ISomeService client = channelFactory.CreateChannel();
    */    
        public class InspectorBehavior : IEndpointBehavior
        {
            public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
            {
                // No implementation necessary  
            }

            public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
            {
                clientRuntime.MessageInspectors.Add(new MyMessageInspector());
            }

            public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
            {
                // No implementation necessary  
            }

            public void Validate(ServiceEndpoint endpoint)
            {
                // No implementation necessary  
            }  

        }

        public class MyMessageInspector : IClientMessageInspector
        {
            public object BeforeSendRequest(ref Message request, IClientChannel channel)
            {
                // Do something with the SOAP request
                string request = request.ToString();
                return null;
            }

            public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
            {
                // Do something with the SOAP reply
                string replySoap = reply.ToString();
            }
        }

1

我将以下解决方案用于ASP.NET兼容模式下的IIS托管。感谢Rodney Viana的MSDN博客

在appSettings下将以下内容添加到您的web.config中:

<add key="LogPath" value="C:\\logpath" />
<add key="LogRequestResponse" value="true" />

将您的global.asax.cs替换为以下内容(还修复名称空间名称):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.SessionState;

using System.Text;
using System.IO;
using System.Configuration;

namespace Yournamespace
{
    public class Global : System.Web.HttpApplication
    {
        protected static bool LogFlag;
        protected static string fileNameBase;
        protected static string ext = "log";

        // One file name per day
        protected string FileName
        {
            get
            {
                return String.Format("{0}{1}.{2}", fileNameBase, DateTime.Now.ToString("yyyy-MM-dd"), ext);
            }
        }

        protected void Application_Start(object sender, EventArgs e)
        {
            LogFlag = bool.Parse(ConfigurationManager.AppSettings["LogRequestResponse"].ToString());
            fileNameBase = ConfigurationManager.AppSettings["LogPath"].ToString() + @"\C5API-";   
        }

        protected void Session_Start(object sender, EventArgs e)
        {

        }

        protected void Application_BeginRequest(object sender, EventArgs e)
        {
            if (LogFlag) 
            {                
                // Creates a unique id to match Rquests with Responses
                string id = String.Format("Id: {0} Uri: {1}", Guid.NewGuid(), Request.Url);
                FilterSaveLog input = new FilterSaveLog(HttpContext.Current, Request.Filter, FileName, id);
                Request.Filter = input;
                input.SetFilter(false);
                FilterSaveLog output = new FilterSaveLog(HttpContext.Current, Response.Filter, FileName, id);
                output.SetFilter(true);
                Response.Filter = output;
            }
        }

        protected void Application_AuthenticateRequest(object sender, EventArgs e)
        {

        }

        protected void Application_Error(object sender, EventArgs e)
        {

        }

        protected void Session_End(object sender, EventArgs e)
        {

        }

        protected void Application_End(object sender, EventArgs e)
        {

        }
    }

    class FilterSaveLog : Stream
    {

        protected static string fileNameGlobal = null;
        protected string fileName = null;

        protected static object writeLock = null;
        protected Stream sinkStream;
        protected bool inDisk;
        protected bool isClosed;
        protected string id;
        protected bool isResponse;
        protected HttpContext context;

        public FilterSaveLog(HttpContext Context, Stream Sink, string FileName, string Id)
        {
            // One lock per file name
            if (String.IsNullOrWhiteSpace(fileNameGlobal) || fileNameGlobal.ToUpper() != fileNameGlobal.ToUpper())
            {
                fileNameGlobal = FileName;
                writeLock = new object();
            }
            context = Context;
            fileName = FileName;
            id = Id;
            sinkStream = Sink;
            inDisk = false;
            isClosed = false;
        }

        public void SetFilter(bool IsResponse)
        {


            isResponse = IsResponse;
            id = (isResponse ? "Reponse " : "Request ") + id;

            //
            // For Request only read the incoming stream and log it as it will not be "filtered" for a WCF request
            //
            if (!IsResponse)
            {
                AppendToFile(String.Format("at {0} --------------------------------------------", DateTime.Now));
                AppendToFile(id);

                if (context.Request.InputStream.Length > 0)
                {
                    context.Request.InputStream.Position = 0;
                    byte[] rawBytes = new byte[context.Request.InputStream.Length];
                    context.Request.InputStream.Read(rawBytes, 0, rawBytes.Length);
                    context.Request.InputStream.Position = 0;

                    AppendToFile(rawBytes);
                }
                else
                {
                    AppendToFile("(no body)");
                }
            }

        }

        public void AppendToFile(string Text)
        {
            byte[] strArray = Encoding.UTF8.GetBytes(Text);
            AppendToFile(strArray);

        }

        public void AppendToFile(byte[] RawBytes)
        {
            bool myLock = System.Threading.Monitor.TryEnter(writeLock, 100);


            if (myLock)
            {
                try
                {

                    using (FileStream stream = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
                    {
                        stream.Position = stream.Length;
                        stream.Write(RawBytes, 0, RawBytes.Length);
                        stream.WriteByte(13);
                        stream.WriteByte(10);

                    }

                }
                catch (Exception ex)
                {
                    string str = string.Format("Unable to create log. Type: {0} Message: {1}\nStack:{2}", ex, ex.Message, ex.StackTrace);
                    System.Diagnostics.Debug.WriteLine(str);
                    System.Diagnostics.Debug.Flush();


                }
                finally
                {
                    System.Threading.Monitor.Exit(writeLock);


                }
            }


        }


        public override bool CanRead
        {
            get { return sinkStream.CanRead; }
        }

        public override bool CanSeek
        {
            get { return sinkStream.CanSeek; }
        }

        public override bool CanWrite
        {
            get { return sinkStream.CanWrite; }
        }

        public override long Length
        {
            get
            {
                return sinkStream.Length;
            }
        }

        public override long Position
        {
            get { return sinkStream.Position; }
            set { sinkStream.Position = value; }
        }

        //
        // For WCF this code will never be reached
        //
        public override int Read(byte[] buffer, int offset, int count)
        {
            int c = sinkStream.Read(buffer, offset, count);
            return c;
        }

        public override long Seek(long offset, System.IO.SeekOrigin direction)
        {
            return sinkStream.Seek(offset, direction);
        }

        public override void SetLength(long length)
        {
            sinkStream.SetLength(length);
        }

        public override void Close()
        {

            sinkStream.Close();
            isClosed = true;
        }

        public override void Flush()
        {

            sinkStream.Flush();
        }

        // For streamed responses (i.e. not buffered) there will be more than one Response (but the id will match the Request)
        public override void Write(byte[] buffer, int offset, int count)
        {
            sinkStream.Write(buffer, offset, count);
            AppendToFile(String.Format("at {0} --------------------------------------------", DateTime.Now));
            AppendToFile(id);
            AppendToFile(buffer);
        }

    }
}

它应在带有请求和响应XML的LogPath文件夹中创建日志文件。


仅用于服务器端
PreguntonCojoneroCabrón

0

还有另一种查看XML SOAP的自定义MessageEncoder的方法。与IClientMessageInspector的主要区别在于它可以在较低级别上工作,因此它可以捕获原始字节内容,包括任何格式错误的xml。

为了使用这种方法实现跟踪,您需要包装一个标准textMessageEncoding,并将自定义消息编码器作为新的绑定元素,并将该自定义绑定应用于config中的终结点。

另外,您还可以作为示例查看我在项目中的操作方式- 包装textMessageEncoding,日志记录编码器,自定义绑定元素config

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.