将 XDocument 保存到 Body 时 HttpWebRequest 中的奇怪行为
Strange Behavior in HttpWebRequest when Saving XDocument to Body
我在尝试使用 HttpWebRequest
class 以 .NET 4 为目标发送 POST 请求时发现了一些奇怪的行为。这可能只与 XDocument
有关object 我正在尝试发送错误行为,或者我不理解 Save()
方法和 XDocument
的 ToString()
方法之间的区别。
调用以下内容将导致请求的 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,您可以节省回答者的大量重复工作,并增加回答的可能性。
我在尝试使用 HttpWebRequest
class 以 .NET 4 为目标发送 POST 请求时发现了一些奇怪的行为。这可能只与 XDocument
有关object 我正在尝试发送错误行为,或者我不理解 Save()
方法和 XDocument
的 ToString()
方法之间的区别。
调用以下内容将导致请求的 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,您可以节省回答者的大量重复工作,并增加回答的可能性。