Nhận dữ liệu xà phòng RAW từ ứng dụng khách tham chiếu web đang chạy trong ASP.net


95

Tôi đang cố gắng gặp sự cố khi quay một ứng dụng khách dịch vụ web trong dự án hiện tại của mình. Tôi không chắc về nền tảng của Máy chủ dịch vụ (Nhiều khả năng là LAMP). Tôi tin rằng có lỗi ở phía hàng rào của họ vì tôi đã loại bỏ các vấn đề tiềm ẩn với khách hàng của mình. Máy khách là proxy tham chiếu web loại ASMX tiêu chuẩn được tạo tự động từ WSDL dịch vụ.

Những gì tôi cần đến là Thông báo RAW SOAP (Yêu cầu và phản hồi)

cách tốt nhất để làm việc này là gì?

Câu trả lời:


135

Tôi đã thực hiện những thay đổi sau web.configđể nhận được Phong bì SOAP (Yêu cầu / Phản hồi). Thao tác này sẽ xuất tất cả thông tin SOAP thô vào tệp trace.log.

<system.diagnostics>
  <trace autoflush="true"/>
  <sources>
    <source name="System.Net" maxdatasize="1024">
      <listeners>
        <add name="TraceFile"/>
      </listeners>
    </source>
    <source name="System.Net.Sockets" maxdatasize="1024">
      <listeners>
        <add name="TraceFile"/>
      </listeners>
    </source>
  </sources>
  <sharedListeners>
    <add name="TraceFile" type="System.Diagnostics.TextWriterTraceListener"
      initializeData="trace.log"/>
  </sharedListeners>
  <switches>
    <add name="System.Net" value="Verbose"/>
    <add name="System.Net.Sockets" value="Verbose"/>
  </switches>
</system.diagnostics>

2
Điều này không hoạt động đối với tôi vào tháng 2/2012 bằng cách sử dụng VS 2010. Có ai biết giải pháp cập nhật hơn không?
qxotk

2
Điều này là hữu ích. Tuy nhiên, trong trường hợp của tôi, các phản hồi được Gzipped và việc kéo mã ASCII hoặc hex ra khỏi đầu ra kết hợp là một điều khó khăn. Các phương thức xuất thay thế của chúng có phải là phương thức nhị phân hoặc chỉ là văn bản không?

2
@Dediqated - bạn có thể đặt đường dẫn đến tệp trace.log bằng cách điều chỉnh giá trị của thuộc tính initializeData trong ví dụ trên.
DougCouto

3
Nó không còn sử dụng nữa, tôi đã sử dụng Wirehark để xem dữ liệu SOAP thô. Nhưng dù gì cũng cảm ơn!
Dediqated

7
Tốt. Nhưng XML đối với tôi giống như: System.Net Verbose: 0: [14088] 00000000: 3C 3F 78 6D 6C 20 76 65-72 73 69 6F 6E 3D 22 31: <? Xml version = "1 System.Net Verbose: 0: [14088] 00000010: 2E 30 22 20 65 6E 63 6F-64 69 6E 67 3D 22 75 74: .0 "encoding =" ut ....... Bất kỳ ý tưởng nào về cách định dạng nó hoặc chỉ có dữ liệu xml .?
Habeeb

35

Bạn có thể triển khai SoapExtension ghi lại toàn bộ yêu cầu và phản hồi vào một tệp nhật ký. Sau đó, bạn có thể bật SoapExtension trong web.config, giúp bạn dễ dàng bật / tắt cho mục đích gỡ lỗi. Đây là một ví dụ mà tôi đã tìm thấy và sửa đổi để sử dụng cho riêng mình, trong trường hợp của tôi, việc ghi nhật ký được thực hiện bởi log4net nhưng bạn có thể thay thế các phương thức ghi nhật ký bằng phương thức của riêng mình.

public class SoapLoggerExtension : SoapExtension
{
    private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
    private Stream oldStream;
    private Stream newStream;

    public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute)
    {
        return null;
    }

    public override object GetInitializer(Type serviceType)
    {
        return null;
    }

    public override void Initialize(object initializer)
    {

    }

    public override System.IO.Stream ChainStream(System.IO.Stream stream)
    {
        oldStream = stream;
        newStream = new MemoryStream();
        return newStream;
    }

    public override void ProcessMessage(SoapMessage message)
    {

        switch (message.Stage)
        {
            case SoapMessageStage.BeforeSerialize:
                break;
            case SoapMessageStage.AfterSerialize:
                Log(message, "AfterSerialize");
                    CopyStream(newStream, oldStream);
                    newStream.Position = 0;
                break;
                case SoapMessageStage.BeforeDeserialize:
                    CopyStream(oldStream, newStream);
                    Log(message, "BeforeDeserialize");
                break;
            case SoapMessageStage.AfterDeserialize:
                break;
        }
    }

    public void Log(SoapMessage message, string stage)
    {

        newStream.Position = 0;
        string contents = (message is SoapServerMessage) ? "SoapRequest " : "SoapResponse ";
        contents += stage + ";";

        StreamReader reader = new StreamReader(newStream);

        contents += reader.ReadToEnd();

        newStream.Position = 0;

        log.Debug(contents);
    }

    void ReturnStream()
    {
        CopyAndReverse(newStream, oldStream);
    }

    void ReceiveStream()
    {
        CopyAndReverse(newStream, oldStream);
    }

    public void ReverseIncomingStream()
    {
        ReverseStream(newStream);
    }

    public void ReverseOutgoingStream()
    {
        ReverseStream(newStream);
    }

    public void ReverseStream(Stream stream)
    {
        TextReader tr = new StreamReader(stream);
        string str = tr.ReadToEnd();
        char[] data = str.ToCharArray();
        Array.Reverse(data);
        string strReversed = new string(data);

        TextWriter tw = new StreamWriter(stream);
        stream.Position = 0;
        tw.Write(strReversed);
        tw.Flush();
    }
    void CopyAndReverse(Stream from, Stream to)
    {
        TextReader tr = new StreamReader(from);
        TextWriter tw = new StreamWriter(to);

        string str = tr.ReadToEnd();
        char[] data = str.ToCharArray();
        Array.Reverse(data);
        string strReversed = new string(data);
        tw.Write(strReversed);
        tw.Flush();
    }

    private void CopyStream(Stream fromStream, Stream toStream)
    {
        try
        {
            StreamReader sr = new StreamReader(fromStream);
            StreamWriter sw = new StreamWriter(toStream);
            sw.WriteLine(sr.ReadToEnd());
            sw.Flush();
        }
        catch (Exception ex)
        {
            string message = String.Format("CopyStream failed because: {0}", ex.Message);
            log.Error(message, ex);
        }
    }
}

[AttributeUsage(AttributeTargets.Method)]
public class SoapLoggerExtensionAttribute : SoapExtensionAttribute
{
    private int priority = 1; 

    public override int Priority
    {
        get { return priority; }
        set { priority = value; }
    }

    public override System.Type ExtensionType
    {
        get { return typeof (SoapLoggerExtension); }
    }
}

Sau đó, bạn thêm phần sau vào web.config của mình, nơi YourNamespace và YourAssembly trỏ đến lớp và tập hợp SoapExtension của bạn:

<webServices>
  <soapExtensionTypes>
    <add type="YourNamespace.SoapLoggerExtension, YourAssembly" 
       priority="1" group="0" />
  </soapExtensionTypes>
</webServices>

Tôi sẽ thử. Tôi đã xem xét phạm vi xà phòng, nhưng đối với tôi, tôi thấy nó giống như việc lưu trữ dịch vụ nhiều hơn. Sẽ cung cấp cho bạn đánh dấu nếu nó hoạt động :)
Andrew Harry

Có, điều này sẽ hoạt động để ghi lại các cuộc gọi được thực hiện từ ứng dụng web của bạn thông qua proxy đã tạo.
John Lemp

Đây là lộ trình mà tôi đã sử dụng, nó hoạt động rất tốt và tôi có thể sử dụng xpath để che giấu bất kỳ dữ liệu nhạy cảm nào trước khi ghi lại.
Dave Baghdanov

Tôi cần thêm tiêu đề vào yêu cầu xà phòng của khách hàng. Điều này đã giúp tôi. rhyous.com/2015/04/29/…
Rhyous

Tôi cũng mới sử dụng c # và không biết phải đặt tên Assembly là gì. Tiện ích mở rộng dường như không chạy.
Collin Anderson,

28

Không chắc tại sao lại gặp rắc rối với web.config hoặc một lớp serializer. Đoạn mã dưới đây phù hợp với tôi:

XmlSerializer xmlSerializer = new XmlSerializer(myEnvelope.GetType());

using (StringWriter textWriter = new StringWriter())
{
    xmlSerializer.Serialize(textWriter, myEnvelope);
    return textWriter.ToString();
}

17
Từ đâu myEnvelopeđến?
ProfK

6
Tôi không hiểu tại sao câu trả lời này không có nhiều ủng hộ hơn, thẳng thắn và vào vấn đề! Làm tốt!
Batista

1
myEnvalope sẽ là đối tượng mà bạn đang gửi qua dịch vụ web. Tôi không thể làm cho điều này hoạt động như được mô tả (đặc biệt là hàm textWriter.tostring) nhưng nó hoạt động đủ tốt cho các mục đích của tôi trong chế độ gỡ lỗi, trong đó bạn có thể thấy xml thô sau khi bạn gọi serialize. Rất đánh giá cao câu trả lời này, vì một số người khác đã làm việc với kết quả hỗn hợp (đăng nhập bằng cách sử dụng web.config chẳng hạn chỉ trả về một phần của xml và cũng không dễ dàng để phân tích cú pháp).
user2366842

@Bimmerbound: điều này có hoạt động với các tin nhắn xà phòng an toàn được gửi qua VPN được mã hóa không?
Người đàn ông trong chuối của chúng ta vào

6
Câu trả lời này không tạo ra phong bì xà phòng, nó chỉ định dạng thành xml. Làm thế nào để nhận được yêu cầu phong bì xà phòng?
Ali Karaca

21

Hãy thử Fiddler2, nó sẽ cho phép bạn kiểm tra các yêu cầu và phản hồi. Có thể cần lưu ý rằng Fiddler hoạt động với cả lưu lượng truy cập http và https.


Nó là một https mã hóa dịch vụ
Andrew Harry

8
Fiddler thể hủy mã hóa lưu lượng https
Aaron Fischer

1
Tôi thấy giải pháp này tốt hơn là thêm theo dõi vào web / app.config. Cảm ơn!
andyuk 18/09/09

1
Nhưng sử dụng System.Diagnostics trong tập tin cấu hình không đòi hỏi ứng dụng của bên thứ 3 để được cài đặt
Azat

1
@AaronFischer: Liệu Fiddler có làm việc với các tin nhắn xà phòng được gửi qua VPN được mã hóa không?
Người đàn ông trong chuối của chúng tôi vào

6

Có vẻ như giải pháp của Tim Carter không hoạt động nếu lệnh gọi đến tham chiếu web ném ra một ngoại lệ. Tôi đã cố gắng truy cập phản hồi web thô để tôi có thể kiểm tra nó (trong mã) trong trình xử lý lỗi khi ngoại lệ được ném ra. Tuy nhiên, tôi thấy rằng nhật ký phản hồi được viết bởi phương pháp của Tim bị trống khi cuộc gọi ném ra một ngoại lệ. Tôi không hoàn toàn hiểu mã, nhưng có vẻ như phương pháp của Tim cắt vào quy trình sau điểm mà .Net đã vô hiệu hóa và hủy phản hồi web.

Tôi đang làm việc với một khách hàng đang phát triển dịch vụ web theo cách thủ công với mã hóa cấp thấp. Tại thời điểm này, họ đang thêm thông báo lỗi quy trình nội bộ của riêng họ dưới dạng thông báo có định dạng HTML vào phản hồi TRƯỚC phản hồi được định dạng SOAP. Tất nhiên, tài liệu tham khảo trên web automagic .Net đã thổi bùng lên điều này. Nếu tôi có thể nhận được phản hồi HTTP thô sau khi một ngoại lệ được đưa ra, tôi có thể tìm kiếm và phân tích cú pháp bất kỳ phản hồi SOAP nào trong phản hồi HTTP trả về hỗn hợp và biết rằng họ đã nhận được dữ liệu của tôi đồng ý hay không.

Một lát sau ...

Đây là một giải pháp hoạt động, ngay cả sau khi thực thi (lưu ý rằng tôi chỉ sau khi phản hồi - cũng có thể nhận được yêu cầu):

namespace ChuckBevitt
{
    class GetRawResponseSoapExtension : SoapExtension
    {
        //must override these three methods
        public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute)
        {
            return null;
        }
        public override object GetInitializer(Type serviceType)
        {
            return null;
        }
        public override void Initialize(object initializer)
        {
        }

        private bool IsResponse = false;

        public override void ProcessMessage(SoapMessage message)
        {
            //Note that ProcessMessage gets called AFTER ChainStream.
            //That's why I'm looking for AfterSerialize, rather than BeforeDeserialize
            if (message.Stage == SoapMessageStage.AfterSerialize)
                IsResponse = true;
            else
                IsResponse = false;
        }

        public override Stream ChainStream(Stream stream)
        {
            if (IsResponse)
            {
                StreamReader sr = new StreamReader(stream);
                string response = sr.ReadToEnd();
                sr.Close();
                sr.Dispose();

                File.WriteAllText(@"C:\test.txt", response);

                byte[] ResponseBytes = Encoding.ASCII.GetBytes(response);
                MemoryStream ms = new MemoryStream(ResponseBytes);
                return ms;

            }
            else
                return stream;
        }
    }
}

Đây là cách bạn định cấu hình nó trong tệp cấu hình:

<configuration>
     ...
  <system.web>
    <webServices>
      <soapExtensionTypes>
        <add type="ChuckBevitt.GetRawResponseSoapExtension, TestCallWebService"
           priority="1" group="0" />
      </soapExtensionTypes>
    </webServices>
  </system.web>
</configuration>

"TestCallWebService" sẽ được thay thế bằng tên của thư viện (đó tình cờ là tên của ứng dụng bảng điều khiển thử nghiệm mà tôi đang làm việc).

Bạn thực sự không cần phải truy cập ChainStream; bạn sẽ có thể làm điều đó đơn giản hơn từ ProcessMessage như:

public override void ProcessMessage(SoapMessage message)
{
    if (message.Stage == SoapMessageStage.BeforeDeserialize)
    {
        StreamReader sr = new StreamReader(message.Stream);
        File.WriteAllText(@"C:\test.txt", sr.ReadToEnd());
        message.Stream.Position = 0; //Will blow up 'cause type of stream ("ConnectStream") doesn't alow seek so can't reset position
    }
}

Nếu bạn tra cứu SoapMessage.Stream, nó phải là một luồng chỉ đọc mà bạn có thể sử dụng để kiểm tra dữ liệu tại thời điểm này. Đây là một nguyên nhân khó hiểu nếu bạn đọc luồng, các quả bom xử lý tiếp theo mà không tìm thấy dữ liệu bị lỗi (luồng đã ở cuối) và bạn không thể đặt lại vị trí ban đầu.

Điều thú vị là nếu bạn thực hiện cả hai phương pháp, theo cách ChainStream và ProcessMessage, thì phương thức ProcessMessage sẽ hoạt động vì bạn đã thay đổi loại luồng từ ConnectStream thành MemoryStream trong ChainStream và MemoryStream cho phép các hoạt động tìm kiếm. (Tôi đã thử truyền ConnectStream sang MemoryStream - không được phép.)

Vì vậy, ..... Microsoft nên cho phép các hoạt động tìm kiếm trên loại ChainStream hoặc làm cho SoapMessage.Stream thực sự là một bản sao chỉ đọc như nó được cho là. (Viết cho dân biểu của bạn, v.v.)

Một điểm nữa. Sau khi tạo một cách để truy xuất phản hồi HTTP thô sau một ngoại lệ, tôi vẫn không nhận được phản hồi đầy đủ (như được xác định bởi trình dò ​​tìm HTTP). Điều này là do khi dịch vụ web phát triển thêm thông báo lỗi HTML vào đầu phản hồi, nó không điều chỉnh tiêu đề Độ dài nội dung, do đó giá trị Độ dài nội dung nhỏ hơn kích thước của nội dung phản hồi thực tế. Tất cả những gì tôi nhận được là số ký tự của giá trị Độ dài Nội dung - phần còn lại bị thiếu. Rõ ràng, khi .Net đọc luồng phản hồi, nó chỉ đọc số ký tự Độ dài Nội dung và không cho phép giá trị Độ dài Nội dung có thể bị sai. Điều này là vì nó nên được; nhưng nếu giá trị tiêu đề Độ dài Nội dung sai, thì cách duy nhất bạn sẽ nhận được toàn bộ nội dung phản hồi là sử dụng trình đánh giá HTTP (Tôi sử dụng HTTP Analyzer từhttp://www.ieins Inspector.com ).


2

Tôi muốn có khuôn khổ thực hiện việc ghi nhật ký cho bạn bằng cách nối vào một luồng ghi nhật ký mà các bản ghi này sẽ làm khung xử lý luồng cơ bản đó. Điều sau đây không rõ ràng như tôi muốn, vì bạn không thể quyết định giữa yêu cầu và phản hồi trong phương thức ChainStream. Sau đây là cách tôi xử lý nó. Cảm ơn Jon Hanna vì đã ghi đè ý tưởng phát trực tiếp

public class LoggerSoapExtension : SoapExtension
{
    private static readonly string LOG_DIRECTORY = ConfigurationManager.AppSettings["LOG_DIRECTORY"];
    private LogStream _logger;

    public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute)
    {
        return null;
    }
    public override object GetInitializer(Type serviceType)
    {
        return null;
    }
    public override void Initialize(object initializer)
    {
    }
    public override System.IO.Stream ChainStream(System.IO.Stream stream)
    {
        _logger = new LogStream(stream);
        return _logger;
    }
    public override void ProcessMessage(SoapMessage message)
    {
        if (LOG_DIRECTORY != null)
        {
            switch (message.Stage)
            {
                case SoapMessageStage.BeforeSerialize:
                    _logger.Type = "request";
                    break;
                case SoapMessageStage.AfterSerialize:
                    break;
                case SoapMessageStage.BeforeDeserialize:
                    _logger.Type = "response";
                    break;
                case SoapMessageStage.AfterDeserialize:
                    break;
            }
        }
    }
    internal class LogStream : Stream
    {
        private Stream _source;
        private Stream _log;
        private bool _logSetup;
        private string _type;

        public LogStream(Stream source)
        {
            _source = source;
        }
        internal string Type
        {
            set { _type = value; }
        }
        private Stream Logger
        {
            get
            {
                if (!_logSetup)
                {
                    if (LOG_DIRECTORY != null)
                    {
                        try
                        {
                            DateTime now = DateTime.Now;
                            string folder = LOG_DIRECTORY + now.ToString("yyyyMMdd");
                            string subfolder = folder + "\\" + now.ToString("HH");
                            string client = System.Web.HttpContext.Current != null && System.Web.HttpContext.Current.Request != null && System.Web.HttpContext.Current.Request.UserHostAddress != null ? System.Web.HttpContext.Current.Request.UserHostAddress : string.Empty;
                            string ticks = now.ToString("yyyyMMdd'T'HHmmss.fffffff");
                            if (!Directory.Exists(folder))
                                Directory.CreateDirectory(folder);
                            if (!Directory.Exists(subfolder))
                                Directory.CreateDirectory(subfolder);
                            _log = new FileStream(new System.Text.StringBuilder(subfolder).Append('\\').Append(client).Append('_').Append(ticks).Append('_').Append(_type).Append(".xml").ToString(), FileMode.Create);
                        }
                        catch
                        {
                            _log = null;
                        }
                    }
                    _logSetup = true;
                }
                return _log;
            }
        }
        public override bool CanRead
        {
            get
            {
                return _source.CanRead;
            }
        }
        public override bool CanSeek
        {
            get
            {
                return _source.CanSeek;
            }
        }

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

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

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

        public override void Flush()
        {
            _source.Flush();
            if (Logger != null)
                Logger.Flush();
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            return _source.Seek(offset, origin);
        }

        public override void SetLength(long value)
        {
            _source.SetLength(value);
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            count = _source.Read(buffer, offset, count);
            if (Logger != null)
                Logger.Write(buffer, offset, count);
            return count;
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            _source.Write(buffer, offset, count);
            if (Logger != null)
                Logger.Write(buffer, offset, count);
        }
        public override int ReadByte()
        {
            int ret = _source.ReadByte();
            if (ret != -1 && Logger != null)
                Logger.WriteByte((byte)ret);
            return ret;
        }
        public override void Close()
        {
            _source.Close();
            if (Logger != null)
                Logger.Close();
            base.Close();
        }
        public override int ReadTimeout
        {
            get { return _source.ReadTimeout; }
            set { _source.ReadTimeout = value; }
        }
        public override int WriteTimeout
        {
            get { return _source.WriteTimeout; }
            set { _source.WriteTimeout = value; }
        }
    }
}
[AttributeUsage(AttributeTargets.Method)]
public class LoggerSoapExtensionAttribute : SoapExtensionAttribute
{
    private int priority = 1;
    public override int Priority
    {
        get
        {
            return priority;
        }
        set
        {
            priority = value;
        }
    }
    public override System.Type ExtensionType
    {
        get
        {
            return typeof(LoggerSoapExtension);
        }
    }
}

1
@TimCarter nó đã hoạt động với tôi, điều duy nhất tôi đã xóa là phần khách hàng, đặt tên cho tôi với dấu hai chấm, gây ra một ngoại lệ. Vì vậy, khi tôi xóa dòng này, nó sẽ hoạt động tốt cho những ai muốn có một tệp cho mọi yêu cầu / phản hồi. Điều duy nhất cần thêm là điều gì đó rõ ràng nếu bạn đọc các câu trả lời khác và đó là bạn phải thêm nút web.config để chỉ ra soapExtension. Cám ơn!
netadictos

1

Đây là phiên bản đơn giản của câu trả lời hàng đầu. Thêm cái này vào<configuration> phần tử của bạn web.confighoặc App.configtệp. Nó sẽ tạo một trace.logtệp trong bin/Debugthư mục dự án của bạn . Hoặc, bạn có thể chỉ định một đường dẫn tuyệt đối cho tệp nhật ký bằng cách sử dụng initializeDatathuộc tính.

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

Nó cảnh báo rằng các thuộc tính maxdatasizetracemodethuộc tính không được phép, nhưng chúng làm tăng số lượng dữ liệu có thể được ghi lại và tránh ghi lại mọi thứ trong hex.


0

Bạn chưa chỉ định ngôn ngữ bạn đang sử dụng nhưng giả sử C # / .NET, bạn có thể sử dụng tiện ích mở rộng SOAP .

Nếu không, hãy sử dụng công cụ đánh hơi như Wireshark


1
Vâng, đã thử qua Wirehark, nó chỉ có thể hiển thị cho tôi thông tin tiêu đề, nội dung xà phòng được mã hóa.
Andrew Harry

Điều đó có thể do bạn đang sử dụng https. Phần mở rộng SOAP theo như tôi có thể nhớ hoạt động ở cấp độ cao hơn, do đó bạn sẽ có thể xem dữ liệu sau khi nó đã được giải mã.
rbrayb

2
Sử dụng Fiddler thay vì Wireshark, nó giải mã https ra khỏi hộp.
Rodrigo Strauss

-1

Tôi nhận ra mình đến bữa tiệc khá muộn và vì ngôn ngữ không thực sự được chỉ định, đây là một giải pháp VB.NET dựa trên câu trả lời của Bimmerbound, trong trường hợp bất kỳ ai tình cờ gặp phải điều này và cần một giải pháp. Lưu ý: bạn cần có tham chiếu đến lớp stringbuilder trong dự án của mình, nếu bạn chưa có.

 Shared Function returnSerializedXML(ByVal obj As Object) As String
    Dim xmlSerializer As New System.Xml.Serialization.XmlSerializer(obj.GetType())
    Dim xmlSb As New StringBuilder
    Using textWriter As New IO.StringWriter(xmlSb)
        xmlSerializer.Serialize(textWriter, obj)
    End Using


    returnSerializedXML = xmlSb.ToString().Replace(vbCrLf, "")

End Function

Chỉ cần gọi hàm và nó sẽ trả về một chuỗi với xml được tuần tự hóa của đối tượng mà bạn đang cố gắng chuyển đến dịch vụ web (trên thực tế, điều này cũng hoạt động với bất kỳ đối tượng nào bạn muốn ném vào nó).

Lưu ý thêm, lệnh gọi thay thế trong hàm trước khi trả về xml là loại bỏ các ký tự vbCrLf khỏi đầu ra. Của tôi đã có một loạt chúng trong xml được tạo, tuy nhiên điều này rõ ràng sẽ khác nhau tùy thuộc vào những gì bạn đang cố gắng tuần tự hóa và tôi nghĩ rằng chúng có thể bị loại bỏ trong khi đối tượng được gửi đến dịch vụ web.


Đối với bất cứ ai đã tát tôi bằng lời khen ngợi, vui lòng cho tôi biết những gì tôi đang thiếu / những gì có thể được cải thiện.
user2366842
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.