use*_*352 4 .net c# wcf soap wcf-binding
我尝试从WCF消息中删除整个SOAP标头,只想离开信封体.任何人都可以告诉我怎么做到这一点?
像这样创建一个WCF消息:
**string response = "Hello World!";
Message msg = Message.CreateMessage(MessageVersion.Soap11, "*", new TextBodyWriter(response));
msg.Headers.Clear();**
Run Code Online (Sandbox Code Playgroud)
发送SOAP消息将是:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header />
<s:Body>
<Binary>Hello World!</Binary>
</s:Body>
</s:Envelope>
Run Code Online (Sandbox Code Playgroud)
但我不想要SOAP头元素,我只需要包络体.如何从WCF消息中删除头元素?
这个问题是一个棘手的问题:让我们逐步解决它
该Message班在写它的头文件ToString()的方法。ToString()然后调用内部重载ToString(XmlDictionaryWriter writer),然后开始写入:
// System.ServiceModel.Channels.Message
internal void ToString(XmlDictionaryWriter writer)
{
if (this.IsDisposed)
{
throw TraceUtility.ThrowHelperError(this.CreateMessageDisposedException(), this);
}
if (this.Version.Envelope != EnvelopeVersion.None)
{
this.WriteStartEnvelope(writer);
this.WriteStartHeaders(writer);
MessageHeaders headers = this.Headers;
for (int i = 0; i < headers.Count; i++)
{
headers.WriteHeader(i, writer);
}
writer.WriteEndElement();
MessageDictionary arg_60_0 = XD.MessageDictionary;
this.WriteStartBody(writer);
}
this.BodyToString(writer);
if (this.Version.Envelope != EnvelopeVersion.None)
{
writer.WriteEndElement();
writer.WriteEndElement();
}
}
Run Code Online (Sandbox Code Playgroud)
该this.WriteStartHeaders(writer);代码将写入标头标签,而与标头的数量无关。通过writer.WriteEndElement()for循环进行匹配。这writer.WriteEndElement() 必须与要写入的标头标记匹配,否则Xml文档将无效。
因此,我们没有办法重写虚拟方法来摆脱头文件:WriteStartHeaders调用虚拟方法,OnWriteStartHeaders但是标签关闭防止简单地将其关闭。我们必须更改整个ToString()方法,以便删除任何与标头相关的结构,以得出:
- write start of envelope
- write start of body
- write body
- write end of body
- write end of envelope
Run Code Online (Sandbox Code Playgroud)
在上面的伪代码中,我们可以控制“写体”部分以外的所有内容。ToString(XmlDictionaryWriter writer)除以外,所有在开头中调用的方法都是公共的BodyToString。因此,我们需要通过反射或任何适合您需求的方法来调用它。编写没有标题的消息变得简单:
private void ProcessMessage(Message msg, XmlDictionaryWriter writer)
{
msg.WriteStartEnvelope(writer); // start of envelope
msg.WriteStartBody(writer); // start of body
var bodyToStringMethod = msg.GetType()
.GetMethod("BodyToString", BindingFlags.Instance | BindingFlags.NonPublic);
bodyToStringMethod.Invoke(msg, new object[] {writer}); // write body
writer.WriteEndElement(); // write end of body
writer.WriteEndElement(); // write end of envelope
}
Run Code Online (Sandbox Code Playgroud)
现在,我们有了一种无需标题即可获取消息内容的方法。但是应如何调用此方法?
我们只希望没有标题的消息作为字符串
太好了,我们不需要在意重写ToString()随后调用消息的初始编写的方法。只需在程序中创建一个采用Message和的方法,XmlDictionaryWriter然后调用该方法即可获取不带标题的消息。
我们希望该ToString()方法返回没有标题的消息
这个有点复杂。我们不能轻易地从Message类中继承,因为我们需要从System.ServiceModel程序集中提取很多依赖项。我不会去这个答案。
我们可以做的是利用某些框架的功能来围绕现有对象创建代理,并拦截对原始对象的某些调用以替换/增强其行为:我习惯于使用Castle Dynamic代理,因此让我们使用它。
我们想拦截ToString()方法,所以我们围绕Message正在使用的对象创建一个代理,并添加一个拦截器以将实现的ToString方法替换为Message我们的实现:
var msg = Message.CreateMessage(MessageVersion.Soap11, "*");
msg.Headers.Clear();
var proxyGenerator = new Castle.DynamicProxy.ProxyGenerator();
var proxiedMessage = proxyGenerator.CreateClassProxyWithTarget(msg, new ProxyGenerationOptions(),
new ToStringInterceptor());
Run Code Online (Sandbox Code Playgroud)
在ToStringInterceptor需要做几乎同样的事情,最初的ToString()方法,不过,我们会用我们的ProcessMessage方法上面定义的:
public class ToStringInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
if (invocation.Method.Name != "ToString")
{
invocation.Proceed();
}
else
{
var result = string.Empty;
var msg = invocation.InvocationTarget as Message;
StringWriter stringWriter = new StringWriter(CultureInfo.InvariantCulture);
XmlDictionaryWriter xmlDictionaryWriter =
XmlDictionaryWriter.CreateDictionaryWriter(new XmlTextWriter(stringWriter));
try
{
ProcessMessage(msg, xmlDictionaryWriter);
xmlDictionaryWriter.Flush();
result = stringWriter.ToString();
}
catch (XmlException ex)
{
result = "ErrorMessage";
}
invocation.ReturnValue = result;
}
}
private void ProcessMessage(Message msg, XmlDictionaryWriter writer)
{
// same method as above
}
}
Run Code Online (Sandbox Code Playgroud)
我们在这里:调用消息的ToString()方法现在将返回一个没有标题的信封。我们可以将消息传递给框架的其他部分,并知道它应该可以正常工作:直接调用Message的某些内部管道仍然可以产生初始输出,但是如果没有完全重新实现,我们将无法控制它。
XmlWriter的原实里所使用的一个ToString()中Message,EncodingFallbackAwareXmlTextWriter。此类在System.ServiceModel中是内部的,将其拔出作为练习留给读者。结果,由于xml没有XmlTextWriter使用我所使用的简单格式进行格式化,因此输出略有不同。ToString()在让值冒泡之前,拦截器可以简单地解析从初始调用返回的xml 并删除标头节点。这是另一个可行的解决方案。public class ToStringInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
if (invocation.Method.Name != "ToString")
{
invocation.Proceed();
}
else
{
var result = string.Empty;
var msg = invocation.InvocationTarget as Message;
StringWriter stringWriter = new StringWriter(CultureInfo.InvariantCulture);
XmlDictionaryWriter xmlDictionaryWriter =
XmlDictionaryWriter.CreateDictionaryWriter(new XmlTextWriter(stringWriter));
try
{
ProcessMessage(msg, xmlDictionaryWriter);
xmlDictionaryWriter.Flush();
result = stringWriter.ToString();
}
catch (XmlException ex)
{
result = "ErrorMessage";
}
invocation.ReturnValue = result;
}
}
private void ProcessMessage(Message msg, XmlDictionaryWriter writer)
{
msg.WriteStartEnvelope(writer);
msg.WriteStartBody(writer);
var bodyToStringMethod = msg.GetType()
.GetMethod("BodyToString", BindingFlags.Instance | BindingFlags.NonPublic);
bodyToStringMethod.Invoke(msg, new object[] { writer });
writer.WriteEndElement();
writer.WriteEndElement();
}
}
internal class Program
{
private static void Main(string[] args)
{
var msg = Message.CreateMessage(MessageVersion.Soap11, "*");
msg.Headers.Clear();
var proxyGenerator = new Castle.DynamicProxy.ProxyGenerator();
var proxiedMessage = proxyGenerator.CreateClassProxyWithTarget(msg, new ProxyGenerationOptions(),
new ToStringInterceptor());
var initialResult = msg.ToString();
var proxiedResult = proxiedMessage.ToString();
Console.WriteLine("Initial result");
Console.WriteLine(initialResult);
Console.WriteLine();
Console.WriteLine("Proxied result");
Console.WriteLine(proxiedResult);
Console.ReadLine();
}
}
Run Code Online (Sandbox Code Playgroud)