将XDocument保存到Body时HttpWebRequest中的奇怪行为

时间:2015-12-02 16:37:25

标签: c# .net .net-4.0 httpwebrequest linq-to-xml

我尝试使用针对.NET 4的HttpWebRequest类发送POST请求时发现了一些奇怪的行为。这可能只与我试图发送行为异常的XDocument对象有关,或者我不理解Save()方法和ToString() XDocument方法之间的区别。

调用以下内容会导致请求的ContentLength在保存到流中时针对我的特定XDocument设置为 316

using(var stream = request.GetRequestStream())
{
    xDoc.Save(stream, SaveOptions.DisableFormatting);
}

这导致服务器无法理解我的请求正文。使用fiddler我捕获了正在上传的XML,然后复制&将原始XML粘贴到Fiddler的作曲家窗口中。在将此请求发送到服务器后,它出人意料地做出了回应而没有问当我检查标题时,我注意到Fiddler为ContentLength计算了 313

我尝试了一些实验来解决这个问题,例如首先设置ContentLength值(因为我知道这个特定请求应该是什么值),但保存到流总是会导致异常,说明数据比预期的ContentLength长。最后,我决定使用此代码解决方法来对XDocument进行字符串化,从而使HttpWebRequest计算出正确的ContentLength:

using(var stream = request.GetRequestStream())
using(var writer = new StreamWriter(stream))
{
    writer.Write(xDoc.ToString(SaveOptions.DisableFormatting));
}

以上计算 313 并允许服务器正确响应。

为什么直接保存到流与从ToString()方法写入数据会导致HttpWebRequest计算不同的ContentLength标头值?这是一个错误/已知问题吗?

编辑:根据要求,这是一个MCVE来说明这一点(我正在连接的程序的API非常糟糕,我为他们的混乱道歉):

XDocument xDoc = new XDocument(
    new XDeclaration("1.0", "UTF-8", "yes"),
    new XElement("PVDM_HTTPINTERFACE",
        new XElement("FUNCTION",
            new XElement("NAME", "LoginUserEx3"),
            new XElement("PARAMETERS",
                new XElement("ENTITYID", 1),
                new XElement("USERNAME", "testadmin"),
                new XElement("PASSWORD", "12345"),
                new XElement("CLIENTPINGABLE", false),
                new XElement("REMOTEAUTH", false)
            )
        )
    )
);

HttpWebRequest requestWrittenWithString = (HttpWebRequest)WebRequest.Create("http://localhost");
requestWrittenWithString.Method = "POST";
HttpWebRequest requestWrittenWithSave = (HttpWebRequest)WebRequest.Create("http://localhost");
requestWrittenWithSave.Method = "POST";

//Save directly to stream
using(var stream = requestWrittenWithSave.GetRequestStream())
{
    xDoc.Save(stream, SaveOptions.DisableFormatting);
}

long saveContentLength = requestWrittenWithSave.ContentLength; //316

//StreamWriter with ToString() method
using(var stream = requestWrittenWithString.GetRequestStream())
using(var writer = new StreamWriter(stream))
{
    writer.Write(xDoc.ToString(SaveOptions.DisableFormatting));
}

long toStringContentLength = requestWrittenWithString.ContentLength; //313

1 个答案:

答案 0 :(得分:1)

基于提供的不完整的代码示例(甚至不编译,从不介意产生完全描述的结果,因为第二个场景使用未声明的变量requestDoc并且不向流发出XML声明),我有理由相信您实际上是在第一个场景中在流中获取UTF8 BOM。

我不知道XDocument类中的设置会控制它。但是,显式指定用于输出的TextWriter非常简单,StreamWriter的默认编码不包括BOM(正如您在第二个场景中已经演示过的那样)。

这样的事情应该在第一个场景中修复你的问题:

using (var stream = requestWrittenWithSave.GetRequestStream())
using (TextWriter writer = new StreamWriter(stream))
{
    xDoc.Save(writer, SaveOptions.DisableFormatting);
}

当然,您可以改为使用XDocument.ToString()方法,如第二个示例所示。无论哪种方式都没问题。


如需将来参考,请重新阅读How to create a Minimal, Complete, and Verifiable example处的信息,以了解“MCVE”的含义。如果你提供一个好的MCVE,你可以为回答者节省大量的重复工作,并增加答案的可能性。