.NET SoapExtension не может изменить запрос soap на сервере

#.net #soap-extension

#.net #soap-расширение

Вопрос:

Я пытаюсь реализовать SoapExtension в .NET, который сжимал бы весь трафик soap (как запросы, так и ответы). У меня есть контроль как над клиентом, так и над сервером. Я начал с копирования этого:http://www.mastercsharp.com/article.aspx ?articleId=86 amp;amp;TopicID = 7 и, слегка изменив его, у меня возникла проблема.

Я могу изменить (zip) тело запроса, когда оно отправляется от клиента, оно поступает на сервер, обратная операция (unzip) тоже хороша, НО, похоже, я не могу заставить фреймворк отражать изменения, которые я вношу в объект stream! На сервере я установил правильные данные в объект newStream, но при вызове веб-метода аргументы по-прежнему архивируются (и, следовательно, недействительны). Есть идеи, почему?

Чтобы упростить задачу для всех, включая меня, я уменьшил SoapExtension до этого:

 using System;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.IO;
using System.Net;

// Define a SOAP Extension that traces the SOAP request and SOAP
// response for the XML Web service method the SOAP extension is
// applied to.

public class TraceExtension : SoapExtension {
Stream oldStream;
Stream newStream;
string filename;

// Save the Stream representing the SOAP request or SOAP response into
// a local memory buffer.
public override Stream ChainStream(Stream stream)
{
    oldStream = stream;
    newStream = new MemoryStream();
    return newStream;
}

// When the SOAP extension is accessed for the first time, the XML Web
// service method it is applied to is accessed to store the file
// name passed in, using the corresponding SoapExtensionAttribute.  
public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute)
{
    return ((TraceExtensionAttribute)attribute).Filename;
}

// The SOAP extension was configured to run using a configuration file
// instead of an attribute applied to a specific XML Web service
// method.
public override object GetInitializer(Type WebServiceType)
{
    // Return a file name to log the trace information to, based on the
    // type.
    return "C:\"   WebServiceType.FullName   ".log";
}

// Receive the file name stored by GetInitializer and store it in a
// member variable for this specific instance.
public override void Initialize(object initializer)
{
    filename = (string)initializer;
}

//  If the SoapMessageStage is such that the SoapRequest or
//  SoapResponse is still in the SOAP format to be sent or received,
//  save it out to a file.
public override void ProcessMessage(SoapMessage message)
{
    switch (message.Stage)
    {
        case SoapMessageStage.BeforeSerialize:
            break;
        case SoapMessageStage.AfterSerialize:
            WriteOutput(message);
            break;
        case SoapMessageStage.BeforeDeserialize:
            WriteInput(message);
            break;
        case SoapMessageStage.AfterDeserialize:
            break;
    }
}

public void WriteOutput(SoapMessage message)
{
    newStream.Position = 0;
    FileStream fs = new FileStream(filename, FileMode.Append,
        FileAccess.Write);
    StreamWriter w = new StreamWriter(fs);

    string soapString = (message is SoapServerMessage) ? "SoapResponse" : "SoapRequest";
    w.WriteLine("-----"   soapString   " at "   DateTime.Now);
    w.Flush();
    Copy(newStream, fs);
    w.Close();
    newStream.Position = 0;
    Copy(newStream, oldStream);
}

//public void WriteInput(SoapMessage message)
//{
//    Copy(oldStream, newStream);
//    FileStream fs = new FileStream(filename, FileMode.Append,
//        FileAccess.Write);
//    StreamWriter w = new StreamWriter(fs);

//    string soapString = (message is SoapServerMessage) ?
//        "SoapRequest" : "SoapResponse";
//    w.WriteLine("-----"   soapString  
//        " at "   DateTime.Now);
//    w.Flush();
//    newStream.Position = 0;
//    Copy(newStream, fs);
//    w.Close();
//    newStream.Position = 0;
//}


public void WriteInput(SoapMessage message)
{
    oldStream.Position = 0;
    StreamReader reader = new StreamReader(oldStream);
    StreamWriter writer = new StreamWriter(newStream);
    string data = reader.ReadToEnd();
    data = data.Replace("false", "true");
    writer.Write(data);
    writer.Flush();
}

void Copy(Stream from, Stream to)
{
    TextReader reader = new StreamReader(from);
    TextWriter writer = new StreamWriter(to);
    writer.WriteLine(reader.ReadToEnd());
    writer.Flush();
}
  

}

И это все равно не будет работать должным образом. Когда запрос soap принимается на сервере (метод WriteInput), все значения «false» изменяются на «true», а данные сохраняются в объекте newStream, но он по-прежнему будет отображать значения «false» при вызове webmethod!

Мои вызовы выглядят следующим образом: Client:

  [SoapDocumentMethodAttribute("http://Company.com/Product/admintool/webservices/SaveNativeRequest", RequestNamespace = "http://Company.com/Product/admintool/webservices/", ResponseNamespace = "http://Company.com/Product/admintool/webservices/", Use = SoapBindingUse.Literal, ParameterStyle = SoapParameterStyle.Wrapped)]
        [SoapHeaderAttribute("Ticket")]
        [TraceExtension(Priority = 0)]
        public void SaveNativeRequest(XmlNode nativeRequest, int? batchId)
        {
            this.Invoke("SaveNativeRequest", new object[] { nativeRequest, batchId });
        }
  

Сервер:

  [WebMethod]
    [TraceExtension(Priority = 0)]
    [SoapHeader("Ticket", Direction = SoapHeaderDirection.In)]
    public void SaveNativeRequest(XmlNode nativeRequest, int? batchId)
    {...}
  

Просто для полноты, это класс атрибута

 // Create a SoapExtensionAttribute for the SOAP Extension that can be
// applied to an XML Web service method.
using System;
using System.Web.Services.Protocols;
[AttributeUsage(AttributeTargets.Method)]
public class TraceExtensionAttribute : SoapExtensionAttribute
{
    private string filename = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)   @"log.txt";
    private int priority;

    public override Type ExtensionType
    {
        get { return typeof(TraceExtension); }
    }

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

    public string Filename
    {
        get
        {
            return filename;
        }
        set
        {
            filename = value;
        }
    }
}
  

Я просмотрел эту статью: h **p://hyperthink.net/blog/inside-of-chainstream / и я почти уверен, что правильно использую метод chainstream. Я знаю, что есть другие способы сделать это, но этот действительно казался чистым вариантом. Я прочитал всевозможные статьи на эту тему, это http://msdn.microsoft.com/en-us/magazine/cc188761.aspx статья вдохновила меня на обходной путь, я могу использовать

 (System.Web.HttpContext.Current.Items["RequestSoapContext"] as Microsoft.Web.Services3.SoapContext).Envelope.Body
  

в методе ProcessMessage для непосредственного манипулирования телом soap (это работает нормально, но некрасиво)

Я упускаю из виду что-то очевидное? Может ли какая-либо другая часть приложения вмешиваться в цепочку потоков на сервере? Я буду действительно очень рад, если у кого-нибудь есть какие-либо идеи по этому поводу.

Комментарии:

1. Моя вторая рекомендация по SoapExtensions заключается в том, чтобы сначала заставить работать пример , а затем медленно превратить пример в SoapExtension, который вы действительно хотите.

2. Конечно, моя первая рекомендация — всегда использовать WCF, так что вы можете использовать механизмы расширения намного лучше, чем SoapExtension.

3. Приложение не новое, и, к сожалению, перенос всего монстра на WFC просто невозможен. Что касается вашего второго комментария, выполнение примера в значительной степени заставило бы все это работать, так что, да 🙂

4. @Filip: нет, я имею в виду, что пример действительно работает, если вы будете следовать инструкциям. Заставить его работать — это просто вопрос обучения следовать инструкциям (например, настройка web.config). Затем начните изменять его, чтобы он стал тем, что вам нужно. Я сделал это с расширением для проверки входящих (и исходящих!) сообщений. Я сходил с ума, пока не решил полностью вернуться к примеру.

5. Нет, это не так. Редактирование web.config не требуется, если предоставлены атрибуты метода.

Ответ №1:

Я думаю, вам нужно сбросить позицию newStream в методе WriteInput.

Поэтому, пожалуйста, добавьте следующую строку в конце метода WriteInput:

 newStream.Position = 0;
  

Это должно решить вашу проблему.