Làm cách nào để nhận yêu cầu XML SOAP của một yêu cầu dịch vụ Web WCF?


Câu trả lời:


143

Tôi nghĩ ý của bạn là bạn muốn xem XML ở máy khách chứ không phải theo dõi nó ở máy chủ. Trong trường hợp đó, câu trả lời của bạn nằm trong câu hỏi tôi đã liên kết ở trên, và cả ở Cách kiểm tra hoặc sửa đổi thông báo trên máy khách . Tuy nhiên, vì phiên bản .NET 4 của bài viết đó thiếu C # và ví dụ .NET 3.5 có một số nhầm lẫn (nếu không phải là lỗi) trong đó, ở đây nó được mở rộng cho mục đích của bạn.

Bạn có thể chặn tin nhắn trước khi nó được gửi đi bằng IClientMessageIns Inspector :

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

Các phương thức trong giao diện đó BeforeSendRequestAfterReceiveReplycấp cho bạn quyền truy cập vào yêu cầu và trả lời. Để sử dụng trình kiểm tra, bạn cần thêm nó vào IEndpointBehavior :

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

Bạn có thể để các phương thức khác của giao diện đó dưới dạng triển khai trống, trừ khi bạn cũng muốn sử dụng chức năng của chúng. Đọc hướng dẫn để biết thêm chi tiết.

Sau khi bạn khởi tạo ứng dụng khách, hãy thêm hành vi vào điểm cuối. Sử dụng tên mặc định từ dự án WCF mẫu:

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

Đặt điểm ngắt trong MyMessageInspector.BeforeSendRequest(); request.ToString()bị quá tải để hiển thị XML.

Nếu bạn định xử lý các tin nhắn, bạn phải làm việc trên một bản sao của tin nhắn. Xem Sử dụng Lớp Thư để biết chi tiết.

Cảm ơn câu trả lời của Zach Bonham ở một câu hỏi khác vì đã tìm thấy các liên kết này.


Tôi đã ủng hộ cả bạn và câu trả lời của Zach Bonham! Cảm ơn vì giải pháp đơn giản. Tôi kết nối nó để viết một tệp dựa trên cấu hình
Rhyous

public object BeforeSendRequest (ref Message request, IClientChannel channel) {if (ConfigurationManager.AppSettings ["SaveWCFRequestXmlFiles"]! = "true") return null; var file = Path.Combine (ConfigurationManager.AppSettings ["XmlFilePath"], "Request-" + DateTime.Now.ToString ("yyyyMMdd-Hmmssfff") + ".xml"); FileWriter.Write (request.ToString (), tệp); trả về null; }
Rhyous

1
Điều này hoạt động, nhưng nó không làm những gì tôi (và OP, tôi giả sử) muốn, vì nó không cung cấp cho bạn quyền truy cập vào phần thân đến thô như đã đến cấp HTTP. XML đã được xác thực trước khi bạn nhận được nó. Khi dịch vụ web (đôi khi xảy ra) trả lời trang lỗi HTML, tôi cần lấy HTML của trang đó. Và những gì tôi nhận được, và hoàn toàn không cần, là một phần của XML bị bỏ đi ("... Luồng ..." trong Message.ToString và XML chưa hoàn chỉnh với ReadOuterXml và ReadInnerXml từ XmlReader mà bạn có thể lấy cho phần nội dung )
Luc VdV 28/09/18

Tương tự như nhận xét của @ LucVdV, điều này không cung cấp cho bạn yêu cầu thực tế. Có sự khác biệt nhỏ giữa những gì điều này tạo ra so với những gì Fiddler nói với bạn thực sự đã vượt qua giới hạn. Sự khác biệt đáng kể đến mức sao chép kết quả vào SoapUI hoặc Postman sẽ gây ra lỗi kỳ lạ khi bạn gửi yêu cầu.
asontu

30

lựa chọn 1

Sử dụng theo dõi / ghi nhật ký tin nhắn .

Có một cái nhìn ở đâyở đây .


Lựa chọn 2

Bạn luôn có thể sử dụng Fiddler để xem các yêu cầu HTTP và phản hồi.


Lựa chọn 3

Sử dụng theo dõi System.Net .


1
Hãy cẩn thận với Fiddler khi sử dụng cấu hình bảo mật hoang tưởng cấp (ví dụ như giấy chứng nhận lẫn nhau qua SSL ) đối với một số lý do, sự hiện diện của một proxy gây ngoại lệ này: System.ServiceModel.Security.SecurityNegotiationException
user1778770

1
Không biết tại sao Phương án 1 không phù hợp với tôi. Đã thử một số tùy chọn cấu hình khác nhau để ghi nhật ký thư và nội dung không hiển thị trong SvcTraceViewer.
Shiv

Tùy chọn 1 đã làm việc cho tôi. Tôi có thể xem toàn bộ tin nhắn SOAP, cả thư đi và thư đến. Tôi phải đặt cờ cấp dịch vụ để xem những gì tôi muốn. logMessagesAtServiceLevel = "true"
Amonn

@vbguyny đầy đủ mẫu bằng cách sử dụng tính năng Truy tìm mạng?
PreguntonCojoneroCabrón

7
OperationContext.Current.RequestContext.RequestMessage 

ngữ cảnh này là phía máy chủ có thể truy cập trong quá trình xử lý yêu cầu. Điều này không hoạt động cho các hoạt động một chiều


2
Chỉ phía máy chủ , không phải phía khách hàng
PreguntonCojoneroCabrón

7

Đơn giản là chúng tôi có thể theo dõi thông báo yêu cầu là.

OperationContext context = OperationContext.Current;

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

{

Message msg = context.RequestContext.RequestMessage;

string reqXML = msg.ToString();

}

1
Tôi đoán điều này có sẵn ở điểm cuối dịch vụ (máy chủ). Có cách nào để nhận được điều tương tự ở đầu máy khách từ nơi yêu cầu được gửi không?
Himansz

4

Tôi chỉ muốn thêm điều này vào câu trả lời từ Kimberly. Có thể nó có thể tiết kiệm một chút thời gian và tránh các lỗi biên dịch do không thực hiện tất cả các phương pháp mà giao diện IEndpointBehaviour yêu cầu.

Trân trọng

Nicki

    /*
        // 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

Tôi đang sử dụng giải pháp dưới đây cho lưu trữ IIS ở chế độ tương thích ASP.NET. Tín dụng cho blog MSDN của Rodney Viana .

Thêm thông tin sau vào web.config của bạn trong appSettings:

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

Thay thế global.asax.cs của bạn bằng bên dưới (cũng sửa tên không gian tên):

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);
        }

    }
}

Nó sẽ tạo tệp nhật ký trong thư mục LogPath với XML yêu cầu và phản hồi.


Chỉ dành cho phía máy chủ ?
PreguntonCojoneroCabrón

0

Có một cách khác để xem XML SOAP - MessageEncoder tùy chỉnh . Sự khác biệt chính so với IClientMessageIns Inspector là nó hoạt động ở cấp thấp hơn, vì vậy nó nắm bắt nội dung byte gốc bao gồm bất kỳ xml nào không đúng định dạng.

Để triển khai theo dõi bằng cách sử dụng phương pháp này, bạn cần bao bọc một textMessageEncoding tiêu chuẩn với bộ mã hóa thông báo tùy chỉnh làm phần tử liên kết mới và áp dụng liên kết tùy chỉnh đó cho điểm cuối trong cấu hình của bạn .

Ngoài ra, bạn có thể xem ví dụ như cách tôi đã làm điều đó trong dự án của mình - gói textMessageEncoding, bộ mã hóa ghi nhật ký , phần tử ràng buộc tùy chỉnh và cấu hình .

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.