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

Strange Behavior in HttpWebRequest when Saving XDocument to Body

我在尝试使用 HttpWebRequest class 以 .NET 4 为目标发送 POST 请求时发现了一些奇怪的行为。这可能只与 XDocument 有关object 我正在尝试发送错误行为,或者我不理解 Save() 方法和 XDocumentToString() 方法之间的区别。

调用以下内容将导致请求的 ContentLength 被设置为 316 对于我的特定 XDocument 当保存到流中时:

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

这导致服务器无法理解我的请求 body。我使用 fiddler 捕获了正在上传的 XML,然后将原始 XML 复制并粘贴到 Fiddler 的 composer window 中。将此请求发送到服务器后,它出人意料地毫无问题地做出了响应。当我检查 headers 时,我注意到 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 header 值?这是 bug/known 问题吗?

编辑:根据要求,这里有一个 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

基于所提供的不完整代码示例(甚至无法编译,更不用说产生所描述的准确结果,因为第二种情况使用未声明的变量 requestDoc 并且不会发出 XML流的声明),我有理由相信您实际上在第一个场景中获得了流中的 UTF8 BOM。

我不知道 XDocument class 本身的设置可以控制它。但是明确指定要用于输出的 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,您可以节省回答者的大量重复工作,并增加回答的可能性。