当服务的响应是非法 XML 时,如何清理它?
所以我相信我理解这些概念,并且我认为我应该使用 IClientMessageInspector
但在我看来,我必须使用 System.ServiceModel.Channels.Message 主体的所有工具
需要在.NET 中使用XML 对象,但我无法做到这一点,因为该消息的正文是非法的XML。最终,该消息是这样的:
[random number]
<validXml />
[other random number]
例如:
379
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<ns1:getVersionResponse>
<ns1:getVersionResponse xsi:type="xsd:string">1.2.3</ns1:getVersionResponse>
</ns1:getVersionResponse>
</soapenv:Body>
</soapenv:Envelope>
0
这是一个可以工作的示例,除了上面的 XML 无效之外(这正是我首先这样做的原因!):
public class CleanRandomNumbersInspector : IClientMessageInspector
{
public void AfterReceiveReply(ref Message reply, object correlationState)
{
const int tenMegabytes = 10485760;
var buffer = reply.CreateBufferedCopy(tenMegabytes);
var copyToRead = buffer.CreateMessage();
string body;
using (var reader = copyToRead.GetReaderAtBodyContents())
{
body = reader.ReadOuterXml();
}
// Now that we have the text, modify it
body = body.Trim('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0');
// Shove it back into the message
var copyToPassOn = buffer.CreateMessage();
using (var stream = GenerateStreamFromString(body))
{
var writer = XmlDictionaryWriter.CreateTextWriter(stream);
copyToPassOn.WriteBodyContents(writer);
}
// Implement this method to inspect/modify messages after a message
// is received but prior to passing it back to the client
Console.WriteLine("AfterReceiveReply called");
}
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
// GNDN
return null;
}
private static Stream GenerateStreamFromString(string s)
{
var stream = new MemoryStream();
var writer = new StreamWriter(stream);
writer.Write(s);
writer.Flush();
stream.Position = 0;
return stream;
}
}
最终,是的,我要知道,这是一项糟糕的服务。然而,在对方解决这个问题之前,我仍然需要消耗它。我该怎么做?
So I believe I understand the concepts and I THINK I should be using an IClientMessageInspector
but it appears to me that all tools I have to work with the body of a System.ServiceModel.Channels.Message
requires using XML objects within .NET which I cannot do because, well, this message's body is illegal XML. Ultimately, the message is something like this:
[random number]
<validXml />
[other random number]
for example:
379
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<ns1:getVersionResponse>
<ns1:getVersionResponse xsi:type="xsd:string">1.2.3</ns1:getVersionResponse>
</ns1:getVersionResponse>
</soapenv:Body>
</soapenv:Envelope>
0
Here is an example that would work except for the fact that the above is invalid XML (exactly the reason I'm doing this in the first place!):
public class CleanRandomNumbersInspector : IClientMessageInspector
{
public void AfterReceiveReply(ref Message reply, object correlationState)
{
const int tenMegabytes = 10485760;
var buffer = reply.CreateBufferedCopy(tenMegabytes);
var copyToRead = buffer.CreateMessage();
string body;
using (var reader = copyToRead.GetReaderAtBodyContents())
{
body = reader.ReadOuterXml();
}
// Now that we have the text, modify it
body = body.Trim('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0');
// Shove it back into the message
var copyToPassOn = buffer.CreateMessage();
using (var stream = GenerateStreamFromString(body))
{
var writer = XmlDictionaryWriter.CreateTextWriter(stream);
copyToPassOn.WriteBodyContents(writer);
}
// Implement this method to inspect/modify messages after a message
// is received but prior to passing it back to the client
Console.WriteLine("AfterReceiveReply called");
}
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
// GNDN
return null;
}
private static Stream GenerateStreamFromString(string s)
{
var stream = new MemoryStream();
var writer = new StreamWriter(stream);
writer.Write(s);
writer.Flush();
stream.Position = 0;
return stream;
}
}
Ultimately, yes, I know, this is a bad service. However, I NEED to consume it nonetheless before the other party fixes this. How can I do this?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您可以通过使用自定义消息编码器来做到这一点(包装原始编码器,并且在 MessageEncoder.ReadMessage 的实现中,您可以在将正文移交给原始编码器之前删除任何无关的字节)。
但这真的很糟糕吗?这看起来像分块传输编码(请参阅 http://www.w3.org/ Protocols/rfc2616/rfc2616-sec3.html,第 3.6.1 节) - 检查 HTTP 标头以查看是否找到
Transfer-Encoding: chunked
标头,然后由 HTTP 传输来删除这些块。 WCF 中的 HTTP 传输可以处理这些问题,您在哪里看到这个问题?You can do that by using a custom message encoder (wrap the original encoder, and in the implementation of
MessageEncoder.ReadMessage
you'd remove whatever extraneous bytes prior to handing the body over to the original encoder).But is this really bad XML? This looks like a chunked transfer encoding (see http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html, section 3.6.1) - check the HTTP headers to see if you find a
Transfer-Encoding: chunked
header, then it's up to the HTTP transport to remove those chunks. The HTTP transport in WCF can deal with those, where are you seeing this problem?我认为如果不完全绕过 API,您将无法在 .Net 应用程序内部执行此操作。
我的建议是为该 Web 服务编写一个小型辅助代理服务,该服务加载它、去除垃圾并输出有效的 XML。
然后,您的主应用程序将能够将其作为普通 XML SOAP 服务从代理服务器读取,并且不需要了解有关它们发送的错误代码的任何信息。
然后,当他们自行修复服务时,您只需将应用程序中的 URL 更改回原始 URL,然后关闭代理即可;主应用程序中不需要更改代码,而且之后也不会留下多余的代码。
I don't think you're going to be able to do it inside of your .Net application without completely bypassing the APIs.
My suggestion would be to write yourself a small helper proxy service for this web service, which loads it, strips off the garbage, and outputs the valid XML.
Then your main app will be able to read it from your proxy server as a normal XML SOAP service and wouldn't need to know anything about the bad code they're sending.
And then when they do fix the service themselves, you can simply change the URL in your application back to the original one, and switch off your proxy; no code changes required in your main app, but also no redundant code left behind in it afterward.