【发布时间】:2011-04-21 19:45:09
【问题描述】:
我正在尝试在 .NET 中实现一个 SoapExtension,它将压缩所有的肥皂流量(请求和响应)。我可以控制客户端和服务器。我已经开始复制这个:http://www.mastercsharp.com/article.aspx?ArticleID=86&&TopicID=7 并稍微修改它,我有一个问题。
当从客户端发送请求正文时,我能够修改(压缩)请求正文,它到达服务器,反向操作(解压缩)也很好,但我似乎无法让框架反映我对流对象所做的更改!在服务器上,我为 newStream 对象设置了正确的数据,但是当调用 WebMethod 时,参数仍然被压缩(因此无效)。知道为什么吗?
为了让包括我自己在内的每个人都更容易,我将 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();
}
}
它仍然无法正常工作。当服务器(WriteInput 方法)收到soap 请求时,所有“false”值都更改为“true”并将数据保存到newStream 对象,但在webmethod 调用中仍会显示“false”值!
我的电话如下所示: 客户:
[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 方法中直接操作皂体(这很好用,但是很丑)
我是否忽略了一些明显的东西?应用程序的其他部分是否会干扰服务器上的流链接?如果有人对此有任何见解,我将非常高兴。
【问题讨论】:
-
我对 SoapExtensions 的第二个建议是首先让 example 工作,然后慢慢将示例转换为您真正想要的 SoapExtension。
-
当然,我的第一个建议始终是使用 WCF,因此您可以使用比 SoapExtension 更好的扩展机制。
-
这个应用程序不是新的,将整个怪物移植到 WFC 是不可能的,遗憾的是。对于您的第二条评论,使示例工作几乎可以使整个工作正常进行,所以,是的:)
-
@Filip:不,我的意思是,如果您按照说明操作,示例 确实 有效。让它工作只是学习遵循指示(例如 web.config 设置)的问题。 然后开始改变它成为你所需要的。我通过扩展来验证传入(和传出!)消息。直到我决定回到这个例子之前,我都快疯了。
-
不,它没有。如果提供了方法属性,则无需编辑 web.config。
标签: .net soap-extension